From 3a063d72451d1156df8fe9c3499ef1e81e905060 Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Wed, 8 Apr 2020 16:13:57 -0400 Subject: [PATCH] fix: support mTLS in 1.0 Binary and Structured emitters This commit modifies both of the 1.0 emitters so that they may accept typed objects as a part of the configuration. When using mTLS in Node, you need to provide an `Agent` to the underlying HTTP handler. In this case, Axios will pass this object along to Node.js when it is provided. Fixes: https://github.com/cloudevents/sdk-javascript/issues/48 Signed-off-by: Lance Ball --- lib/bindings/http/emitter_binary.js | 46 +++++++++------------ lib/bindings/http/emitter_structured.js | 27 +++++-------- test/http_binding_1.js | 54 +++++++++++++++++++------ v1/index.js | 2 + 4 files changed, 72 insertions(+), 57 deletions(-) diff --git a/lib/bindings/http/emitter_binary.js b/lib/bindings/http/emitter_binary.js index 6ba41b94..79a1d7b4 100644 --- a/lib/bindings/http/emitter_binary.js +++ b/lib/bindings/http/emitter_binary.js @@ -2,58 +2,48 @@ var axios = require("axios"); var empty = require("is-empty"); const Constants = require("./constants.js"); +const defaults = {}; +defaults[Constants.HEADERS] = {}; +defaults[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE] = Constants.DEFAULT_CONTENT_TYPE; function BinaryHTTPEmitter(config, headerByGetter, extensionPrefix){ - this.config = JSON.parse(JSON.stringify(config)); + this.config = Object.assign({}, defaults, config); this.headerByGetter = headerByGetter; this.extensionPrefix = extensionPrefix; - - this.config[Constants.HEADERS] = - (!this.config[Constants.HEADERS] - ? {} - : this.config[Constants.HEADERS]); - - // default is json - if(!this.config[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE]){ - this.config[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE] = - Constants.DEFAULT_CONTENT_TYPE; - } } -BinaryHTTPEmitter.prototype.emit = function(cloudevent) { - // Create new request object - var _config = JSON.parse(JSON.stringify(this.config)); - - // Always set stuff in _config - var _headers = _config[Constants.HEADERS]; +BinaryHTTPEmitter.prototype.emit = function (cloudevent) { + const config = Object.assign({}, this.config); + const headers = Object.assign({}, this.config[Constants.HEADERS]); Object.keys(this.headerByGetter) .filter((getter) => cloudevent[getter]()) .forEach((getter) => { - let header = this.headerByGetter[getter]; - _headers[header.name] = + const header = this.headerByGetter[getter]; + headers[header.name] = header.parser( cloudevent[getter]() ); }); // Set the cloudevent payload - let formatted = cloudevent.format(); + const formatted = cloudevent.format(); let data = formatted.data; data = (formatted.data_base64 ? formatted.data_base64: data); - _config[Constants.DATA_ATTRIBUTE] = data; - // Have extensions? - var exts = cloudevent.getExtensions(); + const exts = cloudevent.getExtensions(); Object.keys(exts) .filter((ext) => Object.hasOwnProperty.call(exts, ext)) .forEach((ext) => { - _headers[this.extensionPrefix + ext] = exts[ext]; + headers[this.extensionPrefix + ext] = exts[ext]; }); - // Return the Promise - return axios.request(_config); -}; + config[Constants.DATA_ATTRIBUTE] = data; + config.headers = headers; + + // Return the Promise + return axios.request(config); + }; module.exports = BinaryHTTPEmitter; diff --git a/lib/bindings/http/emitter_structured.js b/lib/bindings/http/emitter_structured.js index 0937c32c..5c8b1bd5 100644 --- a/lib/bindings/http/emitter_structured.js +++ b/lib/bindings/http/emitter_structured.js @@ -1,30 +1,23 @@ var axios = require("axios"); const Constants = require("./constants.js"); +const defaults = {}; +defaults[Constants.HEADERS] = {}; +defaults[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE] = Constants.DEFAULT_CE_CONTENT_TYPE; function StructuredHTTPEmitter(configuration){ - this.config = JSON.parse(JSON.stringify(configuration)); - - this.config[Constants.HEADERS] = - (!this.config[Constants.HEADERS] - ? {} - : this.config[Constants.HEADERS]); - - if(!this.config[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE]){ - this.config[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE] = - Constants.DEFAULT_CE_CONTENT_TYPE; - } + this.config = Object.assign({}, defaults, configuration); } -StructuredHTTPEmitter.prototype.emit = function(cloudevent) { - // Create new request object - var _config = JSON.parse(JSON.stringify(this.config)); - +StructuredHTTPEmitter.prototype.emit = function (cloudevent) { // Set the cloudevent payload - _config[Constants.DATA_ATTRIBUTE] = cloudevent.format(); + this.config[Constants.DATA_ATTRIBUTE] = cloudevent.format(); // Return the Promise - return axios.request(_config); + return axios.request(this.config).then(response => { + delete this.config[Constants.DATA_ATTRIBUTE]; + return response; + }); }; module.exports = StructuredHTTPEmitter; diff --git a/test/http_binding_1.js b/test/http_binding_1.js index 20dfcb60..bf6483ad 100644 --- a/test/http_binding_1.js +++ b/test/http_binding_1.js @@ -1,14 +1,14 @@ const expect = require("chai").expect; const nock = require("nock"); -const http = require("http"); -const request = require("request"); +const https = require("https"); const {asBase64} = require("../lib/utils/fun.js"); -const BinaryHTTPEmitter = - require("../lib/bindings/http/emitter_binary_1.js"); -const Cloudevent = require("../lib/cloudevent.js"); - -const v1 = require("../v1/index.js"); +const { + Spec, + BinaryHTTPEmitter, + StructuredHTTPEmitter, + Cloudevent +} = require("../v1/index.js"); const type = "com.github.pull.create"; const source = "urn:event:from:myapi/resourse/123"; @@ -28,7 +28,7 @@ const ext2Name = "extension2"; const ext2Value = "acme"; const cloudevent = - new Cloudevent(v1.Spec) + new Cloudevent(Spec) .type(type) .source(source) .dataContentType(ceContentType) @@ -48,7 +48,7 @@ const httpcfg = { }; const binary = new BinaryHTTPEmitter(httpcfg); -const structured = new v1.StructuredHTTPEmitter(httpcfg); +const structured = new StructuredHTTPEmitter(httpcfg); describe("HTTP Transport Binding - Version 1.0", () => { beforeEach(() => { @@ -59,6 +59,21 @@ describe("HTTP Transport Binding - Version 1.0", () => { }); describe("Structured", () => { + it('works with mTLS authentication', () => { + const event = new StructuredHTTPEmitter({ + method: 'POST', + url: `${webhook}/json`, + httpsAgent: new https.Agent({ + cert: 'some value', + key: 'other value' + }) + }) + return event.emit(cloudevent).then(response => { + expect(response.config.headers['Content-Type']) + .to.equal(contentType); + }); + }); + describe("JSON Format", () => { it("requires '" + contentType + "' Content-Type in the header", () => { return structured.emit(cloudevent) @@ -81,7 +96,7 @@ describe("HTTP Transport Binding - Version 1.0", () => { let bindata = Uint32Array.from(dataString, (c) => c.codePointAt(0)); let expected = asBase64(bindata); let binevent = - new Cloudevent(v1.Spec) + new Cloudevent(Spec) .type(type) .source(source) .dataContentType("text/plain") @@ -98,7 +113,7 @@ describe("HTTP Transport Binding - Version 1.0", () => { it("the payload must have 'data_base64' when data is binary", () => { let binevent = - new Cloudevent(v1.Spec) + new Cloudevent(Spec) .type(type) .source(source) .dataContentType("text/plain") @@ -117,6 +132,21 @@ describe("HTTP Transport Binding - Version 1.0", () => { }); describe("Binary", () => { + it('works with mTLS authentication', () => { + const event = new BinaryHTTPEmitter({ + method: 'POST', + url: `${webhook}/json`, + httpsAgent: new https.Agent({ + cert: 'some value', + key: 'other value' + }) + }) + return event.emit(cloudevent).then(response => { + expect(response.config.headers['Content-Type']) + .to.equal(cloudevent.getDataContentType()); + }); + }); + describe("JSON Format", () => { it("requires '" + cloudevent.getDataContentType() + "' Content-Type in the header", () => { return binary.emit(cloudevent) @@ -138,7 +168,7 @@ describe("HTTP Transport Binding - Version 1.0", () => { let bindata = Uint32Array.from(dataString, (c) => c.codePointAt(0)); let expected = asBase64(bindata); let binevent = - new Cloudevent(v1.Spec) + new Cloudevent(Spec) .type(type) .source(source) .dataContentType("text/plain") diff --git a/v1/index.js b/v1/index.js index a2c7c9ad..f0be5ed4 100644 --- a/v1/index.js +++ b/v1/index.js @@ -22,5 +22,7 @@ module.exports = { BinaryHTTPEmitter, StructuredHTTPReceiver, BinaryHTTPReceiver, + Cloudevent: event, + CloudEvent: event, event };