Skip to content

Commit 0435b6d

Browse files
authored
add headers and revamp msgpack tests (#1382)
1 parent d619d41 commit 0435b6d

File tree

25 files changed

+1100
-490
lines changed

25 files changed

+1100
-490
lines changed

client-ts/FunctionalTests/FunctionalTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp2.1</TargetFramework>
5+
<TypeScriptCompileBlocked>True</TypeScriptCompileBlocked>
56
</PropertyGroup>
67

78
<ItemGroup>

client-ts/signalr-protocol-msgpack/spec/MessagePackHubProtocol.spec.ts

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,27 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
import { MessagePackHubProtocol } from "../src/MessagePackHubProtocol"
5-
import { MessageType, InvocationMessage, CompletionMessage, ResultMessage } from "@aspnet/signalr"
5+
import { MessageType, InvocationMessage, CompletionMessage, StreamItemMessage } from "@aspnet/signalr"
66

77
describe("MessageHubProtocol", () => {
88
it("can write/read non-blocking Invocation message", () => {
99
let invocation = <InvocationMessage>{
10+
headers: {},
11+
type: MessageType.Invocation,
12+
target: "myMethod",
13+
arguments: [42, true, "test", ["x1", "y2"], null]
14+
};
15+
16+
let protocol = new MessagePackHubProtocol();
17+
var parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation));
18+
expect(parsedMessages).toEqual([invocation]);
19+
});
20+
21+
it("can write/read Invocation message with headers", () => {
22+
let invocation = <InvocationMessage>{
23+
headers: {
24+
"foo": "bar"
25+
},
1026
type: MessageType.Invocation,
1127
target: "myMethod",
1228
arguments: [42, true, "test", ["x1", "y2"], null]
@@ -19,6 +35,7 @@ describe("MessageHubProtocol", () => {
1935

2036
it("can write/read Invocation message", () => {
2137
let invocation = <InvocationMessage>{
38+
headers: {},
2239
type: MessageType.Invocation,
2340
invocationId: "123",
2441
target: "myMethod",
@@ -31,22 +48,25 @@ describe("MessageHubProtocol", () => {
3148
});
3249

3350
([
34-
[[0x0b, 0x94, 0x03, 0xa3, 0x61, 0x62, 0x63, 0x01, 0xa3, 0x45, 0x72, 0x72],
51+
[[0x0c, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x01, 0xa3, 0x45, 0x72, 0x72],
3552
{
53+
headers: {},
3654
type: MessageType.Completion,
3755
invocationId: "abc",
3856
error: "Err",
3957
result: null
4058
} as CompletionMessage],
41-
[[0x0a, 0x94, 0x03, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xa2, 0x4f, 0x4b],
59+
[[0x0b, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xa2, 0x4f, 0x4b],
4260
{
61+
headers: {},
4362
type: MessageType.Completion,
4463
invocationId: "abc",
4564
error: null,
4665
result: "OK"
4766
} as CompletionMessage],
48-
[[0x07, 0x93, 0x03, 0xa3, 0x61, 0x62, 0x63, 0x02],
67+
[[0x08, 0x94, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x02],
4968
{
69+
headers: {},
5070
type: MessageType.Completion,
5171
invocationId: "abc",
5272
error: null,
@@ -59,48 +79,68 @@ describe("MessageHubProtocol", () => {
5979
}));
6080

6181
([
62-
[[0x07, 0x93, 0x02, 0xa3, 0x61, 0x62, 0x63, 0x08],
82+
[[0x08, 0x94, 0x02, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x08],
83+
{
84+
headers: {},
85+
type: MessageType.StreamItem,
86+
invocationId: "abc",
87+
item: 8
88+
} as StreamItemMessage]
89+
] as [[number[], StreamItemMessage]]).forEach(([payload, expected_message]) =>
90+
it("can read StreamItem message", () => {
91+
let messages = new MessagePackHubProtocol().parseMessages(new Uint8Array(payload).buffer);
92+
expect(messages).toEqual([expected_message]);
93+
}));
94+
95+
([
96+
[[0x0c, 0x94, 0x02, 0x81, 0xa1, 0x74, 0xa1, 0x75, 0xa3, 0x61, 0x62, 0x63, 0x08],
6397
{
98+
headers: {
99+
"t": "u"
100+
},
64101
type: MessageType.StreamItem,
65102
invocationId: "abc",
66103
item: 8
67-
} as ResultMessage]
68-
] as [[number[], CompletionMessage]]).forEach(([payload, expected_message]) =>
69-
it("can read Result message", () => {
104+
} as StreamItemMessage]
105+
] as [[number[], StreamItemMessage]]).forEach(([payload, expected_message]) =>
106+
it("can read message with headers", () => {
70107
let messages = new MessagePackHubProtocol().parseMessages(new Uint8Array(payload).buffer);
71108
expect(messages).toEqual([expected_message]);
72109
}));
73110

74111
([
75-
[[0x00], new Error("Invalid payload.")],
76-
[[0x01, 0x90], new Error("Invalid payload.")],
77-
[[0x01, 0xc2], new Error("Invalid payload.")],
78-
[[0x02, 0x91, 0x05], new Error("Invalid message type.")],
79-
[[0x03, 0x91, 0xa1, 0x78], new Error("Invalid message type.")],
80-
[[0x02, 0x91, 0x01], new Error("Invalid payload for Invocation message.")],
81-
[[0x02, 0x91, 0x02], new Error("Invalid payload for stream Result message.")],
82-
[[0x03, 0x92, 0x03, 0xa0], new Error("Invalid payload for Completion message.")],
83-
[[0x05, 0x94, 0x03, 0xa0, 0x02, 0x00], new Error("Invalid payload for Completion message.")],
84-
[[0x04, 0x93, 0x03, 0xa0, 0x01], new Error("Invalid payload for Completion message.")],
85-
[[0x04, 0x93, 0x03, 0xa0, 0x03], new Error("Invalid payload for Completion message.")]
86-
] as [number[], Error][]).forEach(([payload, expected_error]) =>
87-
it("throws for invalid messages", () => {
112+
["message with no payload", [0x00], new Error("Invalid payload.")],
113+
["message with empty array", [0x01, 0x90], new Error("Invalid payload.")],
114+
["message without outer array", [0x01, 0xc2], new Error("Invalid payload.")],
115+
["message with out-of-range message type", [0x03, 0x92, 0x05, 0x80], new Error("Invalid message type.")],
116+
["message with non-integer message type", [0x04, 0x92, 0xa1, 0x78, 0x80], new Error("Invalid message type.")],
117+
["message with invalid headers", [0x03, 0x92, 0x01, 0x05], new Error("Invalid headers.")],
118+
["Invocation message with invalid invocation id", [0x03, 0x92, 0x01, 0x80], new Error("Invalid payload for Invocation message.")],
119+
["StreamItem message with invalid invocation id", [0x03, 0x92, 0x02, 0x80], new Error("Invalid payload for stream Result message.")],
120+
["Completion message with invalid invocation id", [0x04, 0x93, 0x03, 0x80, 0xa0], new Error("Invalid payload for Completion message.")],
121+
["Completion message with unexpected result", [0x06, 0x95, 0x03, 0x80, 0xa0, 0x02, 0x00], new Error("Invalid payload for Completion message.")],
122+
["Completion message with missing result", [0x05, 0x94, 0x03, 0x80, 0xa0, 0x01], new Error("Invalid payload for Completion message.")],
123+
["Completion message with missing error", [0x05, 0x94, 0x03, 0x80, 0xa0, 0x03], new Error("Invalid payload for Completion message.")]
124+
] as [string, number[], Error][]).forEach(([name, payload, expected_error]) =>
125+
it("throws for " + name, () => {
88126
expect(() => new MessagePackHubProtocol().parseMessages(new Uint8Array(payload).buffer))
89127
.toThrow(expected_error);
90128
}));
91129

92130
it("can read multiple messages", () => {
93131
let payload = [
94-
0x07, 0x93, 0x02, 0xa3, 0x61, 0x62, 0x63, 0x08,
95-
0x0a, 0x94, 0x03, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xa2, 0x4f, 0x4b];
132+
0x08, 0x94, 0x02, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x08,
133+
0x0b, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xa2, 0x4f, 0x4b];
96134
let messages = new MessagePackHubProtocol().parseMessages(new Uint8Array(payload).buffer);
97135
expect(messages).toEqual([
98136
{
137+
headers: {},
99138
type: MessageType.StreamItem,
100139
invocationId: "abc",
101140
item: 8
102-
} as ResultMessage,
141+
} as StreamItemMessage,
103142
{
143+
headers: {},
104144
type: MessageType.Completion,
105145
invocationId: "abc",
106146
error: null,

client-ts/signalr-protocol-msgpack/src/MessagePackHubProtocol.ts

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
import { IHubProtocol, ProtocolType, MessageType, HubMessage, InvocationMessage, ResultMessage, CompletionMessage, StreamInvocationMessage } from "@aspnet/signalr";
4+
import { IHubProtocol, ProtocolType, MessageType, HubMessage, InvocationMessage, StreamItemMessage, CompletionMessage, StreamInvocationMessage, MessageHeaders } from "@aspnet/signalr";
55
import { BinaryMessageFormat } from "./BinaryMessageFormat"
66
import { Buffer } from 'buffer';
77
import * as msgpack5 from "msgpack5";
@@ -28,13 +28,14 @@ export class MessagePackHubProtocol implements IHubProtocol {
2828
}
2929

3030
let messageType = properties[0] as MessageType;
31+
3132
switch (messageType) {
3233
case MessageType.Invocation:
33-
return this.createInvocationMessage(properties);
34+
return this.createInvocationMessage(this.readHeaders(properties), properties);
3435
case MessageType.StreamItem:
35-
return this.createStreamItemMessage(properties);
36+
return this.createStreamItemMessage(this.readHeaders(properties), properties);
3637
case MessageType.Completion:
37-
return this.createCompletionMessage(properties);
38+
return this.createCompletionMessage(this.readHeaders(properties), properties);
3839
case MessageType.Ping:
3940
return this.createPingMessage(properties);
4041
default:
@@ -48,75 +49,80 @@ export class MessagePackHubProtocol implements IHubProtocol {
4849
}
4950

5051
return {
51-
type: properties[0]
52+
// Ping messages have no headers.
53+
type: MessageType.Ping
5254
} as HubMessage;
5355
}
5456

55-
private createInvocationMessage(properties: any[]): InvocationMessage {
56-
if (properties.length != 4) {
57+
private createInvocationMessage(headers: MessageHeaders, properties: any[]): InvocationMessage {
58+
if (properties.length != 5) {
5759
throw new Error("Invalid payload for Invocation message.");
5860
}
5961

60-
let invocationId = properties[1];
62+
let invocationId = properties[2] as string;
6163
if (invocationId) {
6264
return {
65+
headers,
6366
type: MessageType.Invocation,
6467
invocationId: invocationId,
65-
target: properties[2],
66-
arguments: properties[3]
67-
} as InvocationMessage;
68+
target: properties[3] as string,
69+
arguments: properties[4],
70+
};
6871
}
6972
else {
7073
return {
74+
headers,
7175
type: MessageType.Invocation,
72-
target: properties[2],
73-
arguments: properties[3]
74-
} as InvocationMessage;
76+
target: properties[3],
77+
arguments: properties[4]
78+
};
7579
}
7680

7781
}
7882

79-
private createStreamItemMessage(properties: any[]): ResultMessage {
80-
if (properties.length != 3) {
83+
private createStreamItemMessage(headers: MessageHeaders, properties: any[]): StreamItemMessage {
84+
if (properties.length != 4) {
8185
throw new Error("Invalid payload for stream Result message.");
8286
}
8387

8488
return {
89+
headers,
8590
type: MessageType.StreamItem,
86-
invocationId: properties[1],
87-
item: properties[2]
88-
} as ResultMessage;
91+
invocationId: properties[2],
92+
item: properties[3]
93+
} as StreamItemMessage;
8994
}
9095

91-
private createCompletionMessage(properties: any[]): CompletionMessage {
92-
if (properties.length < 3) {
96+
private createCompletionMessage(headers: MessageHeaders, properties: any[]): CompletionMessage {
97+
if (properties.length < 4) {
9398
throw new Error("Invalid payload for Completion message.");
9499
}
95100

96101
const errorResult = 1;
97102
const voidResult = 2;
98103
const nonVoidResult = 3;
99104

100-
let resultKind = properties[2];
105+
let resultKind = properties[3];
101106

102-
if ((resultKind === voidResult && properties.length != 3) ||
103-
(resultKind !== voidResult && properties.length != 4)) {
107+
if ((resultKind === voidResult && properties.length != 4) ||
108+
(resultKind !== voidResult && properties.length != 5)) {
104109
throw new Error("Invalid payload for Completion message.");
105110
}
106111

107112
let completionMessage = {
113+
headers,
108114
type: MessageType.Completion,
109-
invocationId: properties[1],
115+
invocationId: properties[2],
110116
error: null as string,
111117
result: null as any
112118
};
113119

114120
switch (resultKind) {
115121
case errorResult:
116-
completionMessage.error = properties[3];
122+
completionMessage.error = properties[4];
117123
break;
118124
case nonVoidResult:
119-
completionMessage.result = properties[3];
125+
completionMessage.result = properties[4];
120126
break;
121127
}
122128

@@ -139,17 +145,25 @@ export class MessagePackHubProtocol implements IHubProtocol {
139145

140146
private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer {
141147
let msgpack = msgpack5();
142-
let payload = msgpack.encode([MessageType.Invocation, invocationMessage.invocationId || null,
148+
let payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null,
143149
invocationMessage.target, invocationMessage.arguments]);
144150

145151
return BinaryMessageFormat.write(payload.slice());
146152
}
147153

148154
private writeStreamInvocation(streamInvocationMessage: StreamInvocationMessage): ArrayBuffer {
149155
let msgpack = msgpack5();
150-
let payload = msgpack.encode([MessageType.StreamInvocation, streamInvocationMessage.invocationId,
156+
let payload = msgpack.encode([MessageType.StreamInvocation, streamInvocationMessage.headers || {}, streamInvocationMessage.invocationId,
151157
streamInvocationMessage.target, streamInvocationMessage.arguments]);
152158

153159
return BinaryMessageFormat.write(payload.slice());
154160
}
161+
162+
private readHeaders(properties: any): MessageHeaders {
163+
let headers: MessageHeaders = properties[1] as MessageHeaders;
164+
if (typeof headers !== "object") {
165+
throw new Error("Invalid headers.");
166+
}
167+
return headers;
168+
}
155169
}

0 commit comments

Comments
 (0)