Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 2AEF0200CDB for ; Sat, 5 Aug 2017 22:14:58 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 29856164466; Sat, 5 Aug 2017 20:14:58 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 456B6164447 for ; Sat, 5 Aug 2017 22:14:57 +0200 (CEST) Received: (qmail 25090 invoked by uid 500); 5 Aug 2017 20:14:56 -0000 Mailing-List: contact commits-help@openwhisk.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@openwhisk.apache.org Delivered-To: mailing list commits@openwhisk.apache.org Received: (qmail 25076 invoked by uid 99); 5 Aug 2017 20:14:56 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 05 Aug 2017 20:14:56 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 5474A811DD; Sat, 5 Aug 2017 20:14:54 +0000 (UTC) Date: Sat, 05 Aug 2017 20:14:54 +0000 To: "commits@openwhisk.apache.org" Subject: [incubator-openwhisk] branch master updated: Support multiple response header values in raw web actions (#2577) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <150196409444.6297.11106631259175566552@gitbox.apache.org> From: markusthoemmes@apache.org Reply-To: "commits@openwhisk.apache.org" X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-openwhisk X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 3fca06efc7adea6724618e466a0db6e3380b371b X-Git-Newrev: c59ef2a5621e1d2362801ce91b9eff1c3cf885e1 X-Git-Rev: c59ef2a5621e1d2362801ce91b9eff1c3cf885e1 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated archived-at: Sat, 05 Aug 2017 20:14:58 -0000 This is an automated email from the ASF dual-hosted git repository. markusthoemmes pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git The following commit(s) were added to refs/heads/master by this push: new c59ef2a Support multiple response header values in raw web actions (#2577) c59ef2a is described below commit c59ef2a5621e1d2362801ce91b9eff1c3cf885e1 Author: Ben Browning AuthorDate: Sat Aug 5 16:14:52 2017 -0400 Support multiple response header values in raw web actions (#2577) This change allows multiple response header values to be set in raw web actions by using a JSON array as the header value. For example: ``` function main() { return { headers: { "Set-Cookie": ["a=b", "c=d"] }, code: 200 } } ``` --- .../scala/whisk/core/controller/WebActions.scala | 15 +++++++++----- docs/webactions.md | 18 ++++++++++++++++- tests/dat/actions/multipleHeaders.js | 8 ++++++++ .../whisk/core/cli/test/WskWebActionsTests.scala | 23 ++++++++++++++++++++++ .../core/controller/test/WebActionsApiTests.scala | 20 +++++++++++++++++++ 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala index 9ac63ff..138bea9 100644 --- a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala +++ b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala @@ -215,11 +215,8 @@ protected[core] object WhiskWebActionsApi extends Directives { Try { val JsObject(fields) = result val headers = fields.get("headers").map { - case JsObject(hs) => hs.map { - case (k, JsString(v)) => RawHeader(k, v) - case (k, JsBoolean(v)) => RawHeader(k, v.toString) - case (k, JsNumber(v)) => RawHeader(k, v.toString) - case _ => throw new Throwable("Invalid header") + case JsObject(hs) => hs.flatMap { + case (k, v) => headersFromJson(k, v) }.toList case _ => throw new Throwable("Invalid header") @@ -251,6 +248,14 @@ protected[core] object WhiskWebActionsApi extends Directives { } } + private def headersFromJson(k: String, v: JsValue) : Seq[RawHeader] = v match { + case JsString(v) => Seq(RawHeader(k, v)) + case JsBoolean(v) => Seq(RawHeader(k, v.toString)) + case JsNumber(v) => Seq(RawHeader(k, v.toString)) + case JsArray(v) => v.flatMap(inner => headersFromJson(k, inner)) + case _ => throw new Throwable("Invalid header") + } + private def interpretHttpResponse(code: StatusCode, headers: List[RawHeader], str: String, transid: TransactionId): RequestContext => Unit = { val parsedHeader: Try[MediaType] = headers.find(_.lowercaseName == `Content-Type`.lowercaseName) match { case Some(header) => diff --git a/docs/webactions.md b/docs/webactions.md index 28b5449..c597a98 100644 --- a/docs/webactions.md +++ b/docs/webactions.md @@ -70,6 +70,22 @@ function main() { } ``` +Or sets multiple cookies: +```javascript +function main() { + return { + headers: { + 'Set-Cookie': [ + 'UserID=Jane; Max-Age=3600; Version=', + 'SessionID=asdfgh123456; Path = /' + ], + 'Content-Type': 'text/html' + }, + statusCode: 200, + body: '

hello

' } +} +``` + Or returns an `image/png`: ```javascript function main() { @@ -97,7 +113,7 @@ It is important to be aware of the [response size limit](reference.md) for actio An OpenWhisk action that is not a web action requires both authentication and must respond with a JSON object. In contrast, web actions may be invoked without authentication, and may be used to implement HTTP handlers that respond with _headers_, _statusCode_, and _body_ content of different types. The web action must still return a JSON object, but the OpenWhisk system (namely the `controller`) will treat a web action differently if its result includes one or more of the following as to [...] -1. `headers`: a JSON object where the keys are header-names and the values are string values for those headers (default is no headers). +1. `headers`: a JSON object where the keys are header-names and the values are string, number, or boolean values for those headers (default is no headers). To send multiple values for a single header, the header's value should be a JSON array of values. 2. `statusCode`: a valid HTTP status code (default is 200 OK). 3. `body`: a string which is either plain text or a base64 encoded string for binary data (default is empty response). diff --git a/tests/dat/actions/multipleHeaders.js b/tests/dat/actions/multipleHeaders.js new file mode 100644 index 0000000..06712c1 --- /dev/null +++ b/tests/dat/actions/multipleHeaders.js @@ -0,0 +1,8 @@ +function main() { + return { + headers: { + "Set-Cookie": ["a=b", "c=d"] + }, + code: 200 + } +} diff --git a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala index 52f8d8b..517371c 100644 --- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala +++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala @@ -27,6 +27,7 @@ import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitRunner import com.jayway.restassured.RestAssured +import com.jayway.restassured.response.Header import common.TestHelpers import common.TestUtils @@ -296,4 +297,26 @@ trait WskWebActionsTests response.statusCode shouldBe 406 response.body.asString should include("Resource representation is only available with these Content-Types:\\ntext/html") } + + it should "support multiple response header values" in withAssetCleaner(wskprops) { + (wp, assetHelper) => + val name = "webaction" + val file = Some(TestUtils.getTestActionFilename("multipleHeaders.js")) + val host = getServiceURL() + val url = host + s"$testRoutePath/$namespace/default/webaction.http" + + assetHelper.withCleaner(wsk.action, name) { + (action, _) => + action.create(name, file, web = Some("true"), annotations = Map("web-custom-options" -> true.toJson)) + } + + val response = RestAssured.given().config(sslconfig).options(url) + + response.statusCode shouldBe 200 + val cookieHeaders = response.headers.getList("Set-Cookie") + cookieHeaders should contain allOf ( + new Header("Set-Cookie", "a=b"), + new Header("Set-Cookie", "c=d") + ) + } } diff --git a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala index d7c0511..3a7722a 100644 --- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala +++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala @@ -1127,6 +1127,26 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi } } + it should s"support multiple values for headers (auth? ${creds.isDefined})" in { + implicit val tid = transid() + + Seq(s"$systemId/proxy/export_c.http"). + foreach { path => + invocationsAllowed += 1 + actionResult = Some( + JsObject( + "headers" -> JsObject( + "Set-Cookie" -> JsArray(JsString("a=b"), JsString("c=d; Path = /"))))) + + Options(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check { + headers should contain allOf ( + HttpHeaders.RawHeader("Set-Cookie", "a=b"), + HttpHeaders.RawHeader("Set-Cookie", "c=d; Path = /") + ) + } + } + } + it should s"invoke action with options verb without custom options (auth? ${creds.isDefined})" in { implicit val tid = transid() customOptions = false -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" '].