Skip to content

Commit 8522ba8

Browse files
authored
Add MessagePack support for Java SignalR Client (#23532)
* Implement ParseMessages for java messagePack client * Fix some spacing & syntax * Implement write * Tab -> Spaces * MessagePacker -> MessageBufferPacker * Tabs -> Spaces * Tabs -> Spaces * InvocationMessage may not include streamIDs * Only 1 ctor per message type * Fixup HubConnection.java * Change return type of parseMessages to List * Fix HubConnection * Check for primitive value before returning * Implement length header prefix * Minor fixes * Use ByteBuffer to read length header * Add case for Char * Close unpacker * Typo * Override onMessage w/ ByteString * Change OKHttpWebSocketWrapper * Account for nil InvocationId * Change interface & MessagePack impl * Update JsonHubProtocol * Use ByteBuffer * Fixup HubConnection * Fixup more stuff * Convert more stuff to ByteBuffer * Account for ReadOnly * Spacing * No need to reset ByteBuffer when setting position * Add Protocol to HubConnection ctor * Set default, make stuff public * Fixup tests * More test cleanup * Spacing * only grab remaining buffer bytes in json * Last test fixes * Get rid of some unused imports * First round of msgpack tests * Flip condition * Respond to feedback * Spacing * More tests * Add test for primitives * Add more tests, start using msgpack-jackson * Fix build.gradle * Remove debug prints * Start using Type instead of Class * Add overloads for Type, make messagePack readValue() more efficient * Apply feedback, add some tests * Add some tests, fix some tests * Fix tests for real * Add a whole buncha tests * Add TestUtils change that I didn't commit yesterday * Respond to some feedback * Add a couple Json tests * Apply more feedback * Move readonly fix to msgpack * Minor optimization * Fixup some javadocs * Respond to feedback * Remove TypeReference, make Protocols private again * Feedback
1 parent 901ae06 commit 8522ba8

40 files changed

+3824
-485
lines changed

src/SignalR/clients/java/signalr/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ dependencies {
3939
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
4040
api 'io.reactivex.rxjava2:rxjava:2.2.3'
4141
implementation 'org.slf4j:slf4j-api:1.7.25'
42+
compile 'org.msgpack:msgpack-core:0.8.20'
43+
compile 'org.msgpack:jackson-dataformat-msgpack:0.8.20'
4244
}
4345

4446
spotless {

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/CallbackMap.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.microsoft.signalr;
55

6+
import java.lang.reflect.Type;
67
import java.util.ArrayList;
78
import java.util.HashMap;
89
import java.util.List;
@@ -13,10 +14,10 @@ class CallbackMap {
1314
private final Map<String, List<InvocationHandler>> handlers = new HashMap<>();
1415
private final ReentrantLock lock = new ReentrantLock();
1516

16-
public InvocationHandler put(String target, ActionBase action, Class<?>... classes) {
17+
public InvocationHandler put(String target, ActionBase action, Type... types) {
1718
try {
1819
lock.lock();
19-
InvocationHandler handler = new InvocationHandler(action, classes);
20+
InvocationHandler handler = new InvocationHandler(action, types);
2021
if (!handlers.containsKey(target)) {
2122
handlers.put(target, new ArrayList<>());
2223
}

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/CancelInvocationMessage.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@
33

44
package com.microsoft.signalr;
55

6+
import java.util.Map;
7+
68
final class CancelInvocationMessage extends HubMessage {
79
private final int type = HubMessageType.CANCEL_INVOCATION.value;
10+
private Map<String, String> headers;
811
private final String invocationId;
9-
10-
public CancelInvocationMessage(String invocationId) {
12+
13+
public CancelInvocationMessage(Map<String, String> headers, String invocationId) {
14+
if (headers != null && !headers.isEmpty()) {
15+
this.headers = headers;
16+
}
1117
this.invocationId = invocationId;
1218
}
1319

20+
public Map<String, String> getHeaders() {
21+
return headers;
22+
}
23+
24+
public String getInvocationId() {
25+
return invocationId;
26+
}
27+
1428
@Override
1529
public HubMessageType getMessageType() {
1630
return HubMessageType.CANCEL_INVOCATION;

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/CloseMessage.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,35 @@
55

66
final class CloseMessage extends HubMessage {
77
private final String error;
8+
private final boolean allowReconnect;
89

910
@Override
1011
public HubMessageType getMessageType() {
1112
return HubMessageType.CLOSE;
1213
}
1314

1415
public CloseMessage() {
15-
this(null);
16+
this(null, false);
1617
}
1718

1819
public CloseMessage(String error) {
20+
this(error, false);
21+
}
22+
23+
public CloseMessage(boolean allowReconnect) {
24+
this(null, allowReconnect);
25+
}
26+
27+
public CloseMessage(String error, boolean allowReconnect) {
1928
this.error = error;
29+
this.allowReconnect = allowReconnect;
2030
}
2131

2232
public String getError() {
2333
return this.error;
2434
}
35+
36+
public boolean getAllowReconnect() {
37+
return this.allowReconnect;
38+
}
2539
}

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/CompletionMessage.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,30 @@
33

44
package com.microsoft.signalr;
55

6+
import java.util.Map;
7+
68
final class CompletionMessage extends HubMessage {
79
private final int type = HubMessageType.COMPLETION.value;
10+
private Map<String, String> headers;
811
private final String invocationId;
912
private final Object result;
1013
private final String error;
11-
12-
public CompletionMessage(String invocationId, Object result, String error) {
14+
15+
public CompletionMessage(Map<String, String> headers, String invocationId, Object result, String error) {
16+
if (headers != null && !headers.isEmpty()) {
17+
this.headers = headers;
18+
}
1319
if (error != null && result != null) {
1420
throw new IllegalArgumentException("Expected either 'error' or 'result' to be provided, but not both.");
1521
}
1622
this.invocationId = invocationId;
1723
this.result = result;
1824
this.error = error;
1925
}
26+
27+
public Map<String, String> getHeaders() {
28+
return headers;
29+
}
2030

2131
public Object getResult() {
2232
return result;

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/DefaultHttpClient.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.microsoft.signalr;
55

66
import java.io.IOException;
7+
import java.nio.ByteBuffer;
78
import java.util.ArrayList;
89
import java.util.Collection;
910
import java.util.List;
@@ -15,6 +16,7 @@
1516
import io.reactivex.Single;
1617
import io.reactivex.subjects.SingleSubject;
1718
import okhttp3.*;
19+
import okio.ByteString;
1820

1921
final class DefaultHttpClient extends HttpClient {
2022
private OkHttpClient client = null;
@@ -104,7 +106,7 @@ public Single<HttpResponse> send(HttpRequest httpRequest) {
104106
}
105107

106108
@Override
107-
public Single<HttpResponse> send(HttpRequest httpRequest, String bodyContent) {
109+
public Single<HttpResponse> send(HttpRequest httpRequest, ByteBuffer bodyContent) {
108110
Request.Builder requestBuilder = new Request.Builder().url(httpRequest.getUrl());
109111

110112
switch (httpRequest.getMethod()) {
@@ -114,7 +116,7 @@ public Single<HttpResponse> send(HttpRequest httpRequest, String bodyContent) {
114116
case "POST":
115117
RequestBody body;
116118
if (bodyContent != null) {
117-
body = RequestBody.create(MediaType.parse("text/plain"), bodyContent);
119+
body = RequestBody.create(MediaType.parse("text/plain"), ByteString.of(bodyContent));
118120
} else {
119121
body = RequestBody.create(null, new byte[]{});
120122
}
@@ -150,7 +152,7 @@ public void onFailure(Call call, IOException e) {
150152
@Override
151153
public void onResponse(Call call, Response response) throws IOException {
152154
try (ResponseBody body = response.body()) {
153-
HttpResponse httpResponse = new HttpResponse(response.code(), response.message(), body.string());
155+
HttpResponse httpResponse = new HttpResponse(response.code(), response.message(), ByteBuffer.wrap(body.bytes()));
154156
responseSubject.onSuccess(httpResponse);
155157
}
156158
}

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HandshakeProtocol.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33

44
package com.microsoft.signalr;
55

6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.ByteBuffer;
8+
69
import com.google.gson.Gson;
710

811
final class HandshakeProtocol {
912
private static final Gson gson = new Gson();
1013
private static final String RECORD_SEPARATOR = "\u001e";
1114

12-
public static String createHandshakeRequestMessage(HandshakeRequestMessage message) {
15+
public static ByteBuffer createHandshakeRequestMessage(HandshakeRequestMessage message) {
1316
// The handshake request is always in the JSON format
14-
return gson.toJson(message) + RECORD_SEPARATOR;
17+
return ByteBuffer.wrap((gson.toJson(message) + RECORD_SEPARATOR).getBytes(StandardCharsets.UTF_8));
1518
}
1619

1720
public static HandshakeResponseMessage parseHandshakeResponse(String message) {

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpClient.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.microsoft.signalr;
55

6+
import java.nio.ByteBuffer;
67
import java.util.HashMap;
78
import java.util.Map;
89

@@ -45,23 +46,23 @@ public Map<String, String> getHeaders() {
4546
class HttpResponse {
4647
private final int statusCode;
4748
private final String statusText;
48-
private final String content;
49+
private final ByteBuffer content;
4950

5051
public HttpResponse(int statusCode) {
5152
this(statusCode, "");
5253
}
5354

5455
public HttpResponse(int statusCode, String statusText) {
55-
this(statusCode, statusText, "");
56+
this(statusCode, statusText, ByteBuffer.wrap(new byte[] {}));
5657
}
5758

58-
public HttpResponse(int statusCode, String statusText, String content) {
59+
public HttpResponse(int statusCode, String statusText, ByteBuffer content) {
5960
this.statusCode = statusCode;
6061
this.statusText = statusText;
6162
this.content = content;
6263
}
6364

64-
public String getContent() {
65+
public ByteBuffer getContent() {
6566
return content;
6667
}
6768

@@ -95,7 +96,7 @@ public Single<HttpResponse> post(String url) {
9596
return this.send(request);
9697
}
9798

98-
public Single<HttpResponse> post(String url, String body, HttpRequest options) {
99+
public Single<HttpResponse> post(String url, ByteBuffer body, HttpRequest options) {
99100
options.setUrl(url);
100101
options.setMethod("POST");
101102
return this.send(options, body);
@@ -122,7 +123,7 @@ public Single<HttpResponse> delete(String url, HttpRequest options) {
122123

123124
public abstract Single<HttpResponse> send(HttpRequest request);
124125

125-
public abstract Single<HttpResponse> send(HttpRequest request, String body);
126+
public abstract Single<HttpResponse> send(HttpRequest request, ByteBuffer body);
126127

127128
public abstract WebSocketWrapper createWebSocket(String url, Map<String, String> headers);
128129

src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpHubConnectionBuilder.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class HttpHubConnectionBuilder {
1616
private final String url;
1717
private Transport transport;
1818
private HttpClient httpClient;
19+
private HubProtocol protocol = new JsonHubProtocol();
1920
private boolean skipNegotiate;
2021
private Single<String> accessTokenProvider;
2122
private long handshakeResponseTimeout = 0;
@@ -54,6 +55,16 @@ HttpHubConnectionBuilder withHttpClient(HttpClient httpClient) {
5455
this.httpClient = httpClient;
5556
return this;
5657
}
58+
59+
/**
60+
* Sets MessagePack as the {@link HubProtocol} to be used by the {@link HubConnection}.
61+
*
62+
* @return This instance of the HttpHubConnectionBuilder.
63+
*/
64+
public HttpHubConnectionBuilder withMessagePackHubProtocol() {
65+
this.protocol = new MessagePackHubProtocol();
66+
return this;
67+
}
5768

5869
/**
5970
* Indicates to the {@link HubConnection} that it should skip the negotiate process.
@@ -133,7 +144,7 @@ public HttpHubConnectionBuilder setHttpClientBuilderCallback(Action1<OkHttpClien
133144
* @return A new instance of {@link HubConnection}.
134145
*/
135146
public HubConnection build() {
136-
return new HubConnection(url, transport, skipNegotiate, httpClient, accessTokenProvider,
147+
return new HubConnection(url, transport, skipNegotiate, httpClient, protocol, accessTokenProvider,
137148
handshakeResponseTimeout, headers, transportEnum, configureBuilder);
138149
}
139150
}

0 commit comments

Comments
 (0)