Skip to content

Commit 7c2d03e

Browse files
committed
fix: ensure that data encoded as base64 is parsed as an object
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: cloudevents#284 See: cloudevents#282 Signed-off-by: Lance Ball <[email protected]>
1 parent 07e0bef commit 7c2d03e

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

src/transport/http/binary_receiver.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ export class BinaryHTTPReceiver {
7777
eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = headers[header];
7878
}
7979
}
80+
// At this point, if the datacontenttype is application/json and the datacontentencoding is base64
81+
// then the data has already been decoded as a string, then parsed as JSON. We don't need to have
82+
// the datacontentencoding property set - in fact, it's incorrect to do so.
83+
if (
84+
eventObj.datacontenttype === CONSTANTS.MIME_JSON &&
85+
eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64
86+
) {
87+
process.stdout.write(JSON.stringify(eventObj));
88+
process.stdout.write(JSON.stringify(parsedPayload));
89+
90+
delete eventObj.datacontentencoding;
91+
}
8092

8193
const cloudevent = new CloudEvent({ ...eventObj, data: parsedPayload } as CloudEventV1 | CloudEventV03);
8294
this.version === Version.V1 ? validateV1(cloudevent) : validateV03(cloudevent);

src/transport/http/structured_receiver.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CloudEvent, Version } from "../..";
22
import { Headers, sanitize } from "./headers";
3-
import { Parser, JSONParser, MappedParser } from "../../parsers";
3+
import { Parser, JSONParser, MappedParser, Base64Parser } from "../../parsers";
44
import { parserByContentType } from "../../parsers";
55
import { v1structuredParsers, v03structuredParsers } from "./versions";
66
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
@@ -75,6 +75,10 @@ export class StructuredHTTPReceiver {
7575
if (eventObj.data && eventObj.datacontentencoding) {
7676
if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 && !isBase64(eventObj.data)) {
7777
throw new ValidationError("invalid payload");
78+
} else if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64) {
79+
const dataParser = new Base64Parser();
80+
eventObj.data = JSON.parse(dataParser.parse(eventObj.data as string));
81+
delete eventObj.datacontentencoding;
7882
}
7983
}
8084

test/integration/receiver_binary_03_tests.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { expect } from "chai";
44
import { CloudEvent, ValidationError, Version } from "../../src";
55
import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver";
66
import CONSTANTS from "../../src/constants";
7+
import { asBase64 } from "../../src/event/validation";
78

89
const receiver = new BinaryHTTPReceiver(Version.V03);
910

@@ -154,6 +155,26 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => {
154155
// act and assert
155156
expect(receiver.parse.bind(receiver, payload, attributes)).to.not.throw();
156157
});
158+
159+
it("Succeeds when content-type is application/json and datacontentencoding is base64", () => {
160+
const expected = {
161+
whose: "ours",
162+
};
163+
const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number);
164+
const payload = asBase64(bindata);
165+
166+
const attributes = {
167+
[CONSTANTS.CE_HEADERS.TYPE]: "test",
168+
[CONSTANTS.CE_HEADERS.SPEC_VERSION]: Version.V03,
169+
[CONSTANTS.CE_HEADERS.SOURCE]: "/test-source",
170+
[CONSTANTS.CE_HEADERS.ID]: "123456",
171+
[CONSTANTS.CE_HEADERS.TIME]: "2019-06-16T11:42:00Z",
172+
[CONSTANTS.HEADER_CONTENT_TYPE]: "application/json",
173+
[CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING]: "base64",
174+
};
175+
const event = receiver.parse(payload, attributes);
176+
expect(event.data).to.deep.equal(expected);
177+
});
157178
});
158179

159180
describe("Parse", () => {

test/integration/receiver_structured_0_3_test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { expect } from "chai";
33

44
import { CloudEvent, ValidationError, Version } from "../../src";
55
import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver";
6+
import { asBase64 } from "../../src/event/validation";
7+
import CONSTANTS from "../../src/constants";
68

79
const receiver = new StructuredHTTPReceiver(Version.V03);
810
const type = "com.github.pull.create";
@@ -85,6 +87,26 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => {
8587
expect(receiver.parse.bind(receiver, event, attributes)).to.throw(ValidationError, "invalid payload");
8688
});
8789

90+
it("Succeeds when content-type is application/cloudevents+json and datacontentencoding is base64", () => {
91+
const expected = {
92+
whose: "ours",
93+
};
94+
const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number);
95+
const payload = {
96+
data: asBase64(bindata),
97+
specversion: Version.V03,
98+
source,
99+
type,
100+
datacontentencoding: CONSTANTS.ENCODING_BASE64,
101+
};
102+
const attributes = {
103+
"Content-Type": "application/cloudevents+json",
104+
};
105+
106+
const event = receiver.parse(payload, attributes);
107+
expect(event.data).to.deep.equal(expected);
108+
});
109+
88110
it("No error when all required stuff are in place", () => {
89111
// setup
90112
const payload = {

0 commit comments

Comments
 (0)