You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by mr...@apache.org on 2019/04/01 22:13:14 UTC
[incubator-openwhisk-devtools] branch master updated: adding
support for binary response (specifically images) (#235)
This is an automated email from the ASF dual-hosted git repository.
mrutkowski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-devtools.git
The following commit(s) were added to refs/heads/master by this push:
new 97fa248 adding support for binary response (specifically images) (#235)
97fa248 is described below
commit 97fa24813bd3232b0310790729269942fece336b
Author: Priti Desai <pd...@us.ibm.com>
AuthorDate: Mon Apr 1 15:13:09 2019 -0700
adding support for binary response (specifically images) (#235)
* adding support for binary response
* adding support for CORS
* adding support for CORS
---
.../runtimes/javascript/buildtemplate.yaml | 4 ++
.../runtimes/javascript/platform/knative.js | 82 ++++++++++++++++++++--
.../tests/webactionoptions/data-init-run.json | 19 +++++
.../webactionoptions/payload-knative-init-run.http | 24 +++++++
4 files changed, 125 insertions(+), 4 deletions(-)
diff --git a/knative-build/runtimes/javascript/buildtemplate.yaml b/knative-build/runtimes/javascript/buildtemplate.yaml
index 82de16e..ab80f99 100644
--- a/knative-build/runtimes/javascript/buildtemplate.yaml
+++ b/knative-build/runtimes/javascript/buildtemplate.yaml
@@ -34,6 +34,9 @@ spec:
- name: OW_HTTP_METHODS
description: list of HTTP methods, any combination of [GET, POST, PUT, and DELETE], default is [POST]
default: "[POST]"
+ - name: OW_ACTION_RAW
+ description: flag to indicate raw HTTP handling, interpret and process an incoming HTTP body directly
+ default: "false"
steps:
- name: add-ow-env-to-dockerfile
image: "gcr.io/kaniko-project/executor:debug"
@@ -50,6 +53,7 @@ spec:
ENV __OW_ACTION_MAIN "${OW_ACTION_MAIN}"
ENV __OW_ACTION_BINARY "${OW_ACTION_BINARY}"
ENV __OW_HTTP_METHODS "${OW_HTTP_METHODS}"
+ ENV __OW_ACTION_RAW "${OW_ACTION_RAW}"
EOF
- name: build-openwhisk-nodejs-runtime
image: "gcr.io/kaniko-project/executor:latest"
diff --git a/knative-build/runtimes/javascript/platform/knative.js b/knative-build/runtimes/javascript/platform/knative.js
index fb8f9b0..cf08873 100644
--- a/knative-build/runtimes/javascript/platform/knative.js
+++ b/knative-build/runtimes/javascript/platform/knative.js
@@ -19,6 +19,7 @@ var dbg = require('../utils/debug');
var DEBUG = new dbg();
const OW_ENV_PREFIX = "__OW_";
+const CONTENT_TYPE = "Content-Type";
/**
* Pre-process the incoming
@@ -198,14 +199,23 @@ function preProcessRequest(req){
DEBUG.functionEnd();
}
-function postProcessResponse(result, res) {
+function postProcessResponse(req, result, res) {
DEBUG.functionStart();
+
+ var content_types = {
+ json: 'application/json',
+ html: 'text/html',
+ png: 'image/png',
+ svg: 'image/svg+xml',
+ };
+
// After getting the result back from an action, update the HTTP headers,
// status code, and body based on its result if it includes one or more of the
// following as top level JSON properties: headers, statusCode, body
let statusCode = result.code;
let headers = {};
let body = result.response;
+ let contentTypeInHeader = false;
// statusCode: default is 200 OK if body is not empty otherwise 204 No Content
if (result.response.statusCode !== undefined) {
@@ -220,6 +230,27 @@ function postProcessResponse(result, res) {
delete body['headers'];
}
+ // addressing content-type v/s Content-Type
+ // marking 'Content-Type' as standard inside header
+ if (headers.hasOwnProperty(CONTENT_TYPE.toLowerCase())) {
+ headers[CONTENT_TYPE] = headers[CONTENT_TYPE.toLowerCase()];
+ delete headers[CONTENT_TYPE.toLowerCase()];
+ }
+
+ // If a content-type header is not declared in the action result’s headers,
+ // the body is interpreted as application/json for non-string values,
+ // and text/html otherwise.
+ if (!headers.hasOwnProperty(CONTENT_TYPE)) {
+ if (result.response.body !== undefined && typeof result.response.body == "string") {
+ headers[CONTENT_TYPE] = content_types.html;
+ } else {
+ headers[CONTENT_TYPE] = content_types.json;
+ }
+ } else {
+ contentTypeInHeader = true;
+ }
+
+
// body: a string which is either a plain text, JSON object, or a base64 encoded string for binary data (default is "")
// body is considered empty if it is null, "", or undefined
if (result.response.body !== undefined) {
@@ -229,12 +260,39 @@ function postProcessResponse(result, res) {
delete body['binary'];
}
+ //When the content-type is defined, check if the response is binary data or
+ // plain text and decode the plain text using a base64 decoder whenever needed.
+ // Should the body fail to decoded correctly, return an error to the caller.
+ if (contentTypeInHeader && headers[CONTENT_TYPE].lastIndexOf("image", 0) === 0) {
+ if (typeof body === "string") {
+ body = Buffer.from(body, 'base64')
+ headers["Content-Transfer-Encoding"] = "binary";
+ }
+ // TODO: throw an error if body can not be decoded
+ }
+
+
// statusCode: set it to 204 No Content if body is empty
if (statusCode === 200 && body === "") {
statusCode = 204;
}
- res.header(headers).status(statusCode).json(body);
+ if (!headers.hasOwnProperty('Access-Control-Allow-Origin')) {
+ headers['Access-Control-Allow-Origin'] = '*';
+ }
+ if (!headers.hasOwnProperty('Access-Control-Allow-Methods')) {
+ headers['Access-Control-Allow-Methods'] = 'OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH';
+ }
+ // the header Access-Control-Request-Headers is echoed back as the header Access-Control-Allow-Headers if it is present in the HTTP request.
+ // Otherwise, a default value is generated.
+ if (!headers.hasOwnProperty['Access-Control-Allow-Headers']) {
+ headers['Access-Control-Allow-Headers'] = 'Authorization, Origin, X - Requested - With, Content - Type, Accept, User - Agent';
+ if (typeof req.headers['Access-Control-Request-Headers'] !== "undefined") {
+ headers['Access-Control-Allow-Headers'] = req.headers['Access-Control-Request-Headers'];
+ }
+ }
+
+ res.header(headers).status(statusCode).send(body);
DEBUG.functionEnd();
}
@@ -248,6 +306,7 @@ function PlatformKnativeImpl(platformFactory) {
post: 'POST',
put: 'PUT',
delete: 'DELETE',
+ options: 'OPTIONS',
};
const DEFAULT_METHOD = [ 'POST' ];
@@ -268,7 +327,7 @@ function PlatformKnativeImpl(platformFactory) {
service.initCode(req).then(function () {
service.runCode(req).then(function (result) {
- postProcessResponse(result, res)
+ postProcessResponse(req, result, res)
});
}).catch(function (error) {
console.error(error);
@@ -287,10 +346,22 @@ function PlatformKnativeImpl(platformFactory) {
this.registerHandlers = function(app, platform) {
var httpMethods = process.env.__OW_HTTP_METHODS;
// default to "[post]" HTTP method if not defined
- if (typeof httpMethods === "undefined" || !Array.isArray(httpMethods)) {
+ if (typeof httpMethods === "undefined") {
+ console.error("__OW_HTTP_METHODS is undefined; defaulting to '[post]' ...");
+ httpMethods = DEFAULT_METHOD;
+ } else {
+ if (httpMethods.startsWith('[') && httpMethods.endsWith(']')) {
+ httpMethods = httpMethods.substr(1, httpMethods.length);
+ httpMethods = httpMethods.substr(0, httpMethods.length -1);
+ httpMethods = httpMethods.split(',')
+ }
+ }
+ // default to "[post]" HTTP method if specified methods are not valid
+ if (!Array.isArray(httpMethods) || !Array.length) {
console.error("__OW_HTTP_METHODS is undefined; defaulting to '[post]' ...");
httpMethods = DEFAULT_METHOD;
}
+
httpMethods.forEach(function (method) {
switch (method.toUpperCase()) {
case http_method.get:
@@ -305,6 +376,9 @@ function PlatformKnativeImpl(platformFactory) {
case http_method.delete:
app.delete('/', platform.run);
break;
+ case http_method.options:
+ app.options('/', platform.run);
+ break;
default:
console.error("Environment variable '__OW_HTTP_METHODS' has an unrecognized value (" + method + ").");
}
diff --git a/knative-build/runtimes/javascript/tests/webactionoptions/data-init-run.json b/knative-build/runtimes/javascript/tests/webactionoptions/data-init-run.json
new file mode 100644
index 0000000..d045bed
--- /dev/null
+++ b/knative-build/runtimes/javascript/tests/webactionoptions/data-init-run.json
@@ -0,0 +1,19 @@
+{
+ "init": {
+ "name" : "nodejs-web-action-options",
+ "main" : "main",
+ "binary": false,
+ "code" : "function main(params) { if (params.__ow_method == 'OPTIONS') { return { headers: { 'Access-Control-Allow-Methods': 'OPTIONS, GET', 'Access-Control-Allow-Origin': 'example.com' }, statusCode: 200 }}}"
+ },
+ "activation": {
+ "namespace": "default",
+ "action_name": "nodejs-web-action-options",
+ "api_host": "",
+ "api_key": "",
+ "activation_id": "",
+ "deadline": "4102498800000"
+ },
+ "value": {
+ "name": "Joe"
+ }
+}
diff --git a/knative-build/runtimes/javascript/tests/webactionoptions/payload-knative-init-run.http b/knative-build/runtimes/javascript/tests/webactionoptions/payload-knative-init-run.http
new file mode 100644
index 0000000..5556d70
--- /dev/null
+++ b/knative-build/runtimes/javascript/tests/webactionoptions/payload-knative-init-run.http
@@ -0,0 +1,24 @@
+OPTIONS http://localhost:8080/ HTTP/1.1
+content-type: application/json
+
+{
+ "init": {
+ "name" : "nodejs-web-action-options",
+ "main" : "main",
+ "binary": false,
+ "code" : "function main(params) { if (params.__ow_method == 'OPTIONS') { return { headers: { 'Access-Control-Allow-Methods': 'OPTIONS, GET', 'Access-Control-Allow-Origin': 'example.com' }, statusCode: 200 }}}"
+ },
+ "activation": {
+ "namespace": "default",
+ "action_name": "nodejs-web-action-options",
+ "api_host": "",
+ "api_key": "",
+ "activation_id": "",
+ "deadline": "4102498800000"
+ },
+ "value": {
+ "name": "Joe"
+ }
+}
+
+###