fix: ensure that data encoded as base64 is parsed as an object (#285)
The 0.3 specification states that `datacontentencoding` may be set to base64. If an incoming event arrives over HTTP with this value set, and the content type is either application/json or application/cloudevents+json, then ensure that the data is decoded and parsed. Fixes: https://github.com/cloudevents/sdk-javascript/issues/284 See: https://github.com/cloudevents/sdk-javascript/pull/282 Signed-off-by: Lance Ball <lball@redhat.com>
This commit is contained in:
parent
e219a30708
commit
ed9ea956d7
|
@ -77,6 +77,15 @@ export class BinaryHTTPReceiver {
|
||||||
eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = headers[header];
|
eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = headers[header];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// At this point, if the datacontenttype is application/json and the datacontentencoding is base64
|
||||||
|
// then the data has already been decoded as a string, then parsed as JSON. We don't need to have
|
||||||
|
// the datacontentencoding property set - in fact, it's incorrect to do so.
|
||||||
|
if (
|
||||||
|
eventObj.datacontenttype === CONSTANTS.MIME_JSON &&
|
||||||
|
eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64
|
||||||
|
) {
|
||||||
|
delete eventObj.datacontentencoding;
|
||||||
|
}
|
||||||
|
|
||||||
const cloudevent = new CloudEvent({ ...eventObj, data: parsedPayload } as CloudEventV1 | CloudEventV03);
|
const cloudevent = new CloudEvent({ ...eventObj, data: parsedPayload } as CloudEventV1 | CloudEventV03);
|
||||||
this.version === Version.V1 ? validateV1(cloudevent) : validateV03(cloudevent);
|
this.version === Version.V1 ? validateV1(cloudevent) : validateV03(cloudevent);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { CloudEvent, Version } from "../..";
|
import { CloudEvent, Version } from "../..";
|
||||||
import { Headers, sanitize } from "./headers";
|
import { Headers, sanitize } from "./headers";
|
||||||
import { Parser, JSONParser, MappedParser } from "../../parsers";
|
import { Parser, JSONParser, MappedParser, Base64Parser } from "../../parsers";
|
||||||
import { parserByContentType } from "../../parsers";
|
import { parserByContentType } from "../../parsers";
|
||||||
import { v1structuredParsers, v03structuredParsers } from "./versions";
|
import { v1structuredParsers, v03structuredParsers } from "./versions";
|
||||||
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
|
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
|
||||||
|
@ -75,6 +75,10 @@ export class StructuredHTTPReceiver {
|
||||||
if (eventObj.data && eventObj.datacontentencoding) {
|
if (eventObj.data && eventObj.datacontentencoding) {
|
||||||
if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 && !isBase64(eventObj.data)) {
|
if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 && !isBase64(eventObj.data)) {
|
||||||
throw new ValidationError("invalid payload");
|
throw new ValidationError("invalid payload");
|
||||||
|
} else if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64) {
|
||||||
|
const dataParser = new Base64Parser();
|
||||||
|
eventObj.data = JSON.parse(dataParser.parse(eventObj.data as string));
|
||||||
|
delete eventObj.datacontentencoding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { expect } from "chai";
|
||||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||||
import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver";
|
import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver";
|
||||||
import CONSTANTS from "../../src/constants";
|
import CONSTANTS from "../../src/constants";
|
||||||
|
import { asBase64 } from "../../src/event/validation";
|
||||||
|
|
||||||
const receiver = new BinaryHTTPReceiver(Version.V03);
|
const receiver = new BinaryHTTPReceiver(Version.V03);
|
||||||
|
|
||||||
|
@ -154,6 +155,26 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => {
|
||||||
// act and assert
|
// act and assert
|
||||||
expect(receiver.parse.bind(receiver, payload, attributes)).to.not.throw();
|
expect(receiver.parse.bind(receiver, payload, attributes)).to.not.throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Succeeds when content-type is application/json and datacontentencoding is base64", () => {
|
||||||
|
const expected = {
|
||||||
|
whose: "ours",
|
||||||
|
};
|
||||||
|
const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number);
|
||||||
|
const payload = asBase64(bindata);
|
||||||
|
|
||||||
|
const attributes = {
|
||||||
|
[CONSTANTS.CE_HEADERS.TYPE]: "test",
|
||||||
|
[CONSTANTS.CE_HEADERS.SPEC_VERSION]: Version.V03,
|
||||||
|
[CONSTANTS.CE_HEADERS.SOURCE]: "/test-source",
|
||||||
|
[CONSTANTS.CE_HEADERS.ID]: "123456",
|
||||||
|
[CONSTANTS.CE_HEADERS.TIME]: "2019-06-16T11:42:00Z",
|
||||||
|
[CONSTANTS.HEADER_CONTENT_TYPE]: "application/json",
|
||||||
|
[CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING]: "base64",
|
||||||
|
};
|
||||||
|
const event = receiver.parse(payload, attributes);
|
||||||
|
expect(event.data).to.deep.equal(expected);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Parse", () => {
|
describe("Parse", () => {
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { expect } from "chai";
|
||||||
|
|
||||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||||
import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver";
|
import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver";
|
||||||
|
import { asBase64 } from "../../src/event/validation";
|
||||||
|
import CONSTANTS from "../../src/constants";
|
||||||
|
|
||||||
const receiver = new StructuredHTTPReceiver(Version.V03);
|
const receiver = new StructuredHTTPReceiver(Version.V03);
|
||||||
const type = "com.github.pull.create";
|
const type = "com.github.pull.create";
|
||||||
|
@ -85,6 +87,26 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => {
|
||||||
expect(receiver.parse.bind(receiver, event, attributes)).to.throw(ValidationError, "invalid payload");
|
expect(receiver.parse.bind(receiver, event, attributes)).to.throw(ValidationError, "invalid payload");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Succeeds when content-type is application/cloudevents+json and datacontentencoding is base64", () => {
|
||||||
|
const expected = {
|
||||||
|
whose: "ours",
|
||||||
|
};
|
||||||
|
const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number);
|
||||||
|
const payload = {
|
||||||
|
data: asBase64(bindata),
|
||||||
|
specversion: Version.V03,
|
||||||
|
source,
|
||||||
|
type,
|
||||||
|
datacontentencoding: CONSTANTS.ENCODING_BASE64,
|
||||||
|
};
|
||||||
|
const attributes = {
|
||||||
|
"Content-Type": "application/cloudevents+json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = receiver.parse(payload, attributes);
|
||||||
|
expect(event.data).to.deep.equal(expected);
|
||||||
|
});
|
||||||
|
|
||||||
it("No error when all required stuff are in place", () => {
|
it("No error when all required stuff are in place", () => {
|
||||||
// setup
|
// setup
|
||||||
const payload = {
|
const payload = {
|
||||||
|
|
Loading…
Reference in New Issue