Skip to content

Commit a7c326b

Browse files
committed
fix: do not require an HTTP body on incoming binary event messages
This commit modifies the HTTP receivers/parsers to allow for the incoming body of an HTTP request to be empty if the event message is sent using the binary mode. In structured mode, a `ValidationError` will still be thrown, since the entire event must be encoded in the HTTP body. Signed-off-by: Lance Ball <lball@redhat.com>
1 parent d866691 commit a7c326b

File tree

6 files changed

+43
-35
lines changed

6 files changed

+43
-35
lines changed

Diff for: src/transport/http/binary_receiver.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ export class BinaryHTTPReceiver {
3232
* @returns {CloudEvent} an instance of CloudEvent representing the incoming request
3333
* @throws {ValidationError} of the event does not conform to the spec
3434
*/
35-
parse(payload: string | Record<string, unknown>, headers: Headers): CloudEvent {
36-
if (!payload) throw new ValidationError("payload is null or undefined");
35+
parse(payload: string | Record<string, unknown> | undefined | null, headers: Headers): CloudEvent {
3736
if (!headers) throw new ValidationError("headers is null or undefined");
38-
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
37+
if (payload) {
38+
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
39+
}
3940

4041
if (
4142
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] &&
@@ -61,11 +62,15 @@ export class BinaryHTTPReceiver {
6162
}
6263
}
6364

64-
const parser = parserByContentType[eventObj.datacontenttype as string];
65-
if (!parser) {
66-
throw new ValidationError(`no parser found for content type ${eventObj.datacontenttype}`);
65+
let parsedPayload;
66+
67+
if (payload) {
68+
const parser = parserByContentType[eventObj.datacontenttype as string];
69+
if (!parser) {
70+
throw new ValidationError(`no parser found for content type ${eventObj.datacontenttype}`);
71+
}
72+
parsedPayload = parser.parse(payload);
6773
}
68-
const parsedPayload = parser.parse(payload);
6974

7075
// Every unprocessed header can be an extension
7176
for (const header in sanitizedHeaders) {

Diff for: src/transport/http/structured_receiver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class StructuredHTTPReceiver {
3131
* @returns {CloudEvent} a new CloudEvent instance for the provided headers and payload
3232
* @throws {ValidationError} if the payload and header combination do not conform to the spec
3333
*/
34-
parse(payload: Record<string, unknown> | string, headers: Headers): CloudEvent {
34+
parse(payload: Record<string, unknown> | string | undefined | null, headers: Headers): CloudEvent {
3535
if (!payload) throw new ValidationError("payload is null or undefined");
3636
if (!headers) throw new ValidationError("headers is null or undefined");
3737
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));

Diff for: src/transport/receiver.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export class Receiver {
5959
* @param {Object|JSON} body The body of the HTTP request
6060
* @return {CloudEvent} A new {CloudEvent} instance
6161
*/
62-
accept(headers: Headers, body: string | Record<string, unknown> | CloudEventV1 | CloudEventV03): CloudEvent {
62+
accept(
63+
headers: Headers,
64+
body: string | Record<string, unknown> | CloudEventV1 | CloudEventV03 | undefined | null,
65+
): CloudEvent {
6366
const cleanHeaders: Headers = sanitize(headers);
6467
const mode: Mode = getMode(cleanHeaders);
6568
const version = getVersion(mode, cleanHeaders, body);
@@ -103,7 +106,7 @@ function getMode(headers: Headers): Mode {
103106
function getVersion(
104107
mode: Mode,
105108
headers: Headers,
106-
body: string | Record<string, unknown> | CloudEventV03 | CloudEventV1,
109+
body: string | Record<string, unknown> | CloudEventV03 | CloudEventV1 | undefined | null,
107110
) {
108111
if (mode === Mode.BINARY) {
109112
// Check the headers for the version
@@ -113,7 +116,7 @@ function getVersion(
113116
}
114117
} else {
115118
// structured mode - the version is in the body
116-
return typeof body === "string" ? JSON.parse(body).specversion : body.specversion;
119+
return typeof body === "string" ? JSON.parse(body).specversion : (body as CloudEvent).specversion;
117120
}
118121
return Version.V1;
119122
}

Diff for: test/integration/http_receiver_test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,30 @@ describe("HTTP Transport Binding Receiver for CloudEvents", () => {
3939
expect((event.data as Record<string, string>).lunch).to.equal("sushi");
4040
});
4141

42+
it("Accepts binary events when the data property is undefined", () => {
43+
const binaryHeaders = {
44+
"content-type": "application/json; charset=utf-8",
45+
"ce-specversion": specversion,
46+
"ce-id": id,
47+
"ce-type": type,
48+
"ce-source": source,
49+
};
50+
const event = receiver.accept(binaryHeaders, undefined);
51+
expect(event.data).to.be.undefined;
52+
});
53+
54+
it("Accepts binary events when the data property is null", () => {
55+
const binaryHeaders = {
56+
"content-type": "application/json; charset=utf-8",
57+
"ce-specversion": specversion,
58+
"ce-id": id,
59+
"ce-type": type,
60+
"ce-source": source,
61+
};
62+
const event = receiver.accept(binaryHeaders, null);
63+
expect(event.data).to.be.undefined;
64+
});
65+
4266
it("Converts the JSON body of a structured event to an Object", () => {
4367
const payload = {
4468
id,

Diff for: test/integration/receiver_binary_03_tests.ts

-12
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@ const receiver = new BinaryHTTPReceiver(Version.V03);
99

1010
describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => {
1111
describe("Check", () => {
12-
it("Throw error when payload arg is null or undefined", () => {
13-
// setup
14-
const payload = undefined;
15-
const attributes = {};
16-
17-
// act and assert
18-
expect(receiver.parse.bind(receiver, (payload as unknown) as string, attributes)).to.throw(
19-
ValidationError,
20-
"payload is null or undefined",
21-
);
22-
});
23-
2412
it("Throw error when attributes arg is null or undefined", () => {
2513
// setup
2614
const payload = {};

Diff for: test/integration/receiver_binary_1_tests.ts

-12
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,6 @@ const receiver = new BinaryHTTPReceiver(Version.V1);
1010

1111
describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => {
1212
describe("Check", () => {
13-
it("Throw error when payload arg is null or undefined", () => {
14-
// setup
15-
const payload = null;
16-
const attributes = {};
17-
18-
// act and assert
19-
expect(receiver.parse.bind(receiver, (payload as unknown) as string, attributes)).to.throw(
20-
ValidationError,
21-
"payload is null or undefined",
22-
);
23-
});
24-
2513
it("Throw error when attributes arg is null or undefined", () => {
2614
// setup
2715
const payload = {};

0 commit comments

Comments
 (0)