Add support for negotiateVersion query string parameter and ConnectionToken (#13880)

This commit is contained in:
Mikael Mengistu 2019-09-18 14:14:26 -07:00 committed by Brennan Conroy
parent 7d59ae1720
commit de15b0c0cb
7 changed files with 159 additions and 49 deletions

View File

@ -57,6 +57,8 @@ public class HubConnection {
private Map<String, Observable> streamMap = new ConcurrentHashMap<>(); private Map<String, Observable> streamMap = new ConcurrentHashMap<>();
private TransportEnum transportEnum = TransportEnum.ALL; private TransportEnum transportEnum = TransportEnum.ALL;
private String connectionId; private String connectionId;
private String connectionToken;
private final int negotiateVersion = 1;
private final Logger logger = LoggerFactory.getLogger(HubConnection.class); private final Logger logger = LoggerFactory.getLogger(HubConnection.class);
/** /**
@ -339,11 +341,12 @@ public class HubConnection {
}); });
stopError = null; stopError = null;
String urlWithQS = Utils.appendQueryString(baseUrl, "negotiateVersion=" + negotiateVersion);
Single<NegotiateResponse> negotiate = null; Single<NegotiateResponse> negotiate = null;
if (!skipNegotiate) { if (!skipNegotiate) {
negotiate = tokenCompletable.andThen(Single.defer(() -> startNegotiate(baseUrl, 0))); negotiate = tokenCompletable.andThen(Single.defer(() -> startNegotiate(urlWithQS, 0)));
} else { } else {
negotiate = tokenCompletable.andThen(Single.defer(() -> Single.just(new NegotiateResponse(baseUrl)))); negotiate = tokenCompletable.andThen(Single.defer(() -> Single.just(new NegotiateResponse(urlWithQS))));
} }
CompletableSubject start = CompletableSubject.create(); CompletableSubject start = CompletableSubject.create();
@ -376,7 +379,6 @@ public class HubConnection {
hubConnectionStateLock.lock(); hubConnectionStateLock.lock();
try { try {
hubConnectionState = HubConnectionState.CONNECTED; hubConnectionState = HubConnectionState.CONNECTED;
this.connectionId = negotiateResponse.getConnectionId();
logger.info("HubConnection started."); logger.info("HubConnection started.");
resetServerTimeout(); resetServerTimeout();
//Don't send pings if we're using long polling. //Don't send pings if we're using long polling.
@ -446,19 +448,21 @@ public class HubConnection {
throw new RuntimeException("There were no compatible transports on the server."); throw new RuntimeException("There were no compatible transports on the server.");
} }
String finalUrl = url; if (response.getVersion() > 0) {
if (response.getConnectionId() != null) { this.connectionId = response.getConnectionId();
if (url.contains("?")) { this.connectionToken = response.getConnectionToken();
finalUrl = url + "&id=" + response.getConnectionId(); } else {
} else { this.connectionToken = this.connectionId = response.getConnectionId();
finalUrl = url + "?id=" + response.getConnectionId();
}
} }
String finalUrl = Utils.appendQueryString(url, "id=" + this.connectionToken);
response.setFinalUrl(finalUrl); response.setFinalUrl(finalUrl);
return Single.just(response); return Single.just(response);
} }
return startNegotiate(response.getRedirectUrl(), negotiateAttempts + 1); String redirectUrl = Utils.appendQueryString(response.getRedirectUrl(), "negotiateVersion=" + negotiateVersion);
return startNegotiate(redirectUrl, negotiateAttempts + 1);
}); });
} }
@ -520,6 +524,7 @@ public class HubConnection {
handshakeResponseSubject.onComplete(); handshakeResponseSubject.onComplete();
redirectAccessTokenProvider = null; redirectAccessTokenProvider = null;
connectionId = null; connectionId = null;
connectionToken = null;
transportEnum = TransportEnum.ALL; transportEnum = TransportEnum.ALL;
this.localHeaders.clear(); this.localHeaders.clear();
this.streamMap.clear(); this.streamMap.clear();

View File

@ -10,7 +10,7 @@ class Negotiate {
// Check if we have a query string. If we do then we ignore it for now. // Check if we have a query string. If we do then we ignore it for now.
int queryStringIndex = url.indexOf('?'); int queryStringIndex = url.indexOf('?');
if (queryStringIndex > 0) { if (queryStringIndex > 0) {
negotiateUrl = url.substring(0, url.indexOf('?')); negotiateUrl = url.substring(0, queryStringIndex);
} else { } else {
negotiateUrl = url; negotiateUrl = url;
} }
@ -24,7 +24,7 @@ class Negotiate {
// Add the query string back if it existed. // Add the query string back if it existed.
if (queryStringIndex > 0) { if (queryStringIndex > 0) {
negotiateUrl += url.substring(url.indexOf('?')); negotiateUrl += url.substring(queryStringIndex);
} }
return negotiateUrl; return negotiateUrl;

View File

@ -11,11 +11,13 @@ import com.google.gson.stream.JsonReader;
class NegotiateResponse { class NegotiateResponse {
private String connectionId; private String connectionId;
private String connectionToken;
private Set<String> availableTransports = new HashSet<>(); private Set<String> availableTransports = new HashSet<>();
private String redirectUrl; private String redirectUrl;
private String accessToken; private String accessToken;
private String error; private String error;
private String finalUrl; private String finalUrl;
private int version;
public NegotiateResponse(JsonReader reader) { public NegotiateResponse(JsonReader reader) {
try { try {
@ -30,6 +32,12 @@ class NegotiateResponse {
case "ProtocolVersion": case "ProtocolVersion":
this.error = "Detected an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details."; this.error = "Detected an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.";
return; return;
case "negotiateVersion":
this.version = reader.nextInt();
break;
case "connectionToken":
this.connectionToken = reader.nextString();
break;
case "url": case "url":
this.redirectUrl = reader.nextString(); this.redirectUrl = reader.nextString();
break; break;
@ -106,6 +114,14 @@ class NegotiateResponse {
return finalUrl; return finalUrl;
} }
public int getVersion() {
return version;
}
public String getConnectionToken() {
return connectionToken;
}
public void setFinalUrl(String url) { public void setFinalUrl(String url) {
this.finalUrl = url; this.finalUrl = url;
} }

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
package com.microsoft.signalr;
class Utils {
public static String appendQueryString(String original, String queryStringValue) {
if (original.contains("?")) {
return original + "&" + queryStringValue;
} else {
return original + "?" + queryStringValue;
}
}
}

View File

@ -1714,12 +1714,12 @@ class HubConnectionTest {
List<HttpRequest> sentRequests = client.getSentRequests(); List<HttpRequest> sentRequests = client.getSentRequests();
assertEquals(1, sentRequests.size()); assertEquals(1, sentRequests.size());
assertEquals("http://example.com/negotiate", sentRequests.get(0).getUrl()); assertEquals("http://example.com/negotiate?negotiateVersion=1", sentRequests.get(0).getUrl());
} }
@Test @Test
public void negotiateThatRedirectsForeverFailsAfter100Tries() { public void negotiateThatRedirectsForeverFailsAfter100Tries() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://example.com\"}"))); (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://example.com\"}")));
HubConnection hubConnection = HubConnectionBuilder HubConnection hubConnection = HubConnectionBuilder
@ -1752,7 +1752,7 @@ class HubConnectionTest {
@Test @Test
public void connectionIdIsAvailableAfterStart() { public void connectionIdIsAvailableAfterStart() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", (req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"))); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
@ -1775,9 +1775,62 @@ class HubConnectionTest {
assertNull(hubConnection.getConnectionId()); assertNull(hubConnection.getConnectionId());
} }
@Test
public void connectionTokenAppearsInQSConnectionIdIsOnConnectionInstance() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\"," +
"\"negotiateVersion\": 1," +
"\"connectionToken\":\"connection-token-value\"," +
"\"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
MockTransport transport = new MockTransport(true);
HubConnection hubConnection = HubConnectionBuilder
.create("http://example.com")
.withTransportImplementation(transport)
.withHttpClient(client)
.build();
assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState());
assertNull(hubConnection.getConnectionId());
hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait();
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", hubConnection.getConnectionId());
assertEquals("http://example.com?negotiateVersion=1&id=connection-token-value", transport.getUrl());
hubConnection.stop().timeout(1, TimeUnit.SECONDS).blockingAwait();
assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState());
assertNull(hubConnection.getConnectionId());
}
@Test
public void connectionTokenIsIgnoredIfNegotiateVersionIsNotPresentInNegotiateResponse() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\"," +
"\"connectionToken\":\"connection-token-value\"," +
"\"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
MockTransport transport = new MockTransport(true);
HubConnection hubConnection = HubConnectionBuilder
.create("http://example.com")
.withTransportImplementation(transport)
.withHttpClient(client)
.build();
assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState());
assertNull(hubConnection.getConnectionId());
hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait();
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", hubConnection.getConnectionId());
assertEquals("http://example.com?negotiateVersion=1&id=bVOiRPG8-6YiJ6d7ZcTOVQ", transport.getUrl());
hubConnection.stop().timeout(1, TimeUnit.SECONDS).blockingAwait();
assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState());
assertNull(hubConnection.getConnectionId());
}
@Test @Test
public void afterSuccessfulNegotiateConnectsWithWebsocketsTransport() { public void afterSuccessfulNegotiateConnectsWithWebsocketsTransport() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", (req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"))); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
@ -1798,7 +1851,7 @@ class HubConnectionTest {
@Test @Test
public void afterSuccessfulNegotiateConnectsWithLongPollingTransport() { public void afterSuccessfulNegotiateConnectsWithLongPollingTransport() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", (req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"LongPolling\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"))); + "availableTransports\":[{\"transport\":\"LongPolling\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
@ -1891,7 +1944,7 @@ class HubConnectionTest {
@Test @Test
public void receivingServerSentEventsTransportFromNegotiateFails() { public void receivingServerSentEventsTransportFromNegotiateFails() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", (req) -> Single.just(new HttpResponse(200, "",
"{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"ServerSentEvents\",\"transferFormats\":[\"Text\"]}]}"))); + "availableTransports\":[{\"transport\":\"ServerSentEvents\",\"transferFormats\":[\"Text\"]}]}")));
@ -1911,7 +1964,7 @@ class HubConnectionTest {
@Test @Test
public void negotiateThatReturnsErrorThrowsFromStart() { public void negotiateThatReturnsErrorThrowsFromStart() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", "{\"error\":\"Test error.\"}"))); (req) -> Single.just(new HttpResponse(200, "", "{\"error\":\"Test error.\"}")));
MockTransport transport = new MockTransport(true); MockTransport transport = new MockTransport(true);
@ -1928,7 +1981,7 @@ class HubConnectionTest {
@Test @Test
public void DetectWhenTryingToConnectToClassicSignalRServer() { public void DetectWhenTryingToConnectToClassicSignalRServer() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", "{\"Url\":\"/signalr\"," + (req) -> Single.just(new HttpResponse(200, "", "{\"Url\":\"/signalr\"," +
"\"ConnectionToken\":\"X97dw3uxW4NPPggQsYVcNcyQcuz4w2\"," + "\"ConnectionToken\":\"X97dw3uxW4NPPggQsYVcNcyQcuz4w2\"," +
"\"ConnectionId\":\"05265228-1e2c-46c5-82a1-6a5bcc3f0143\"," + "\"ConnectionId\":\"05265228-1e2c-46c5-82a1-6a5bcc3f0143\"," +
@ -1954,9 +2007,9 @@ class HubConnectionTest {
@Test @Test
public void negotiateRedirectIsFollowed() { public void negotiateRedirectIsFollowed() {
TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate", TestHttpClient client = new TestHttpClient().on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}"))) (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}")))
.on("POST", "http://testexample.com/negotiate", .on("POST", "http://testexample.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" (req) -> Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"))); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
@ -1978,11 +2031,11 @@ class HubConnectionTest {
AtomicReference<String> beforeRedirectToken = new AtomicReference<>(); AtomicReference<String> beforeRedirectToken = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", (req) -> { .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> {
beforeRedirectToken.set(req.getHeaders().get("Authorization")); beforeRedirectToken.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}")); return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}"));
}) })
.on("POST", "http://testexample.com/negotiate", (req) -> { .on("POST", "http://testexample.com/negotiate?negotiateVersion=1", (req) -> {
token.set(req.getHeaders().get("Authorization")); token.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"));
@ -2018,7 +2071,7 @@ class HubConnectionTest {
public void accessTokenProviderIsUsedForNegotiate() { public void accessTokenProviderIsUsedForNegotiate() {
AtomicReference<String> token = new AtomicReference<>(); AtomicReference<String> token = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
token.set(req.getHeaders().get("Authorization")); token.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
@ -2043,11 +2096,13 @@ class HubConnectionTest {
public void accessTokenProviderIsOverriddenFromRedirectNegotiate() { public void accessTokenProviderIsOverriddenFromRedirectNegotiate() {
AtomicReference<String> token = new AtomicReference<>(); AtomicReference<String> token = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}"))) .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}")))
.on("POST", "http://testexample.com/negotiate", (req) -> { .on("POST", "http://testexample.com/negotiate?negotiateVersion=1", (req) -> {
token.set(req.getHeaders().get("Authorization")); token.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\","
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")); + "\"connectionToken\":\"connection-token-value\","
+ "\"negotiateVersion\":1,"
+ "\"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"));
}); });
MockTransport transport = new MockTransport(true); MockTransport transport = new MockTransport(true);
@ -2060,7 +2115,7 @@ class HubConnectionTest {
hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait(); hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait();
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
assertEquals("http://testexample.com/?id=bVOiRPG8-6YiJ6d7ZcTOVQ", transport.getUrl()); assertEquals("http://testexample.com/?negotiateVersion=1&id=connection-token-value", transport.getUrl());
hubConnection.stop(); hubConnection.stop();
assertEquals("Bearer newToken", token.get()); assertEquals("Bearer newToken", token.get());
} }
@ -2071,14 +2126,14 @@ class HubConnectionTest {
AtomicReference<String> beforeRedirectToken = new AtomicReference<>(); AtomicReference<String> beforeRedirectToken = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", (req) -> { .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> {
beforeRedirectToken.set(req.getHeaders().get("Authorization")); beforeRedirectToken.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}")); return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"newToken\"}"));
}) })
.on("POST", "http://testexample.com/negotiate", (req) -> { .on("POST", "http://testexample.com/negotiate?negotiateVersion=1", (req) -> {
token.set(req.getHeaders().get("Authorization")); token.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\","
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")); + "\"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"));
}); });
MockTransport transport = new MockTransport(true); MockTransport transport = new MockTransport(true);
@ -2112,7 +2167,7 @@ class HubConnectionTest {
AtomicInteger redirectCount = new AtomicInteger(); AtomicInteger redirectCount = new AtomicInteger();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", (req) -> { .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> {
if (redirectCount.get() == 0) { if (redirectCount.get() == 0) {
redirectCount.incrementAndGet(); redirectCount.incrementAndGet();
redirectToken.set(req.getHeaders().get("Authorization")); redirectToken.set(req.getHeaders().get("Authorization"));
@ -2122,7 +2177,7 @@ class HubConnectionTest {
return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"secondRedirectToken\"}")); return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"secondRedirectToken\"}"));
} }
}) })
.on("POST", "http://testexample.com/negotiate", (req) -> { .on("POST", "http://testexample.com/negotiate?negotiateVersion=1", (req) -> {
token.set(req.getHeaders().get("Authorization")); token.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"));
@ -2189,7 +2244,7 @@ class HubConnectionTest {
public void headersAreSetAndSentThroughBuilder() { public void headersAreSetAndSentThroughBuilder() {
AtomicReference<String> header = new AtomicReference<>(); AtomicReference<String> header = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
header.set(req.getHeaders().get("ExampleHeader")); header.set(req.getHeaders().get("ExampleHeader"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
@ -2214,7 +2269,7 @@ class HubConnectionTest {
public void headersAreNotClearedWhenConnectionIsRestarted() { public void headersAreNotClearedWhenConnectionIsRestarted() {
AtomicReference<String> header = new AtomicReference<>(); AtomicReference<String> header = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
header.set(req.getHeaders().get("Authorization")); header.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
@ -2244,12 +2299,12 @@ class HubConnectionTest {
AtomicReference<String> afterRedirectHeader = new AtomicReference<>(); AtomicReference<String> afterRedirectHeader = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
beforeRedirectHeader.set(req.getHeaders().get("Authorization")); beforeRedirectHeader.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"redirectToken\"}\"}")); return Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\",\"accessToken\":\"redirectToken\"}\"}"));
}) })
.on("POST", "http://testexample.com/negotiate", .on("POST", "http://testexample.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
afterRedirectHeader.set(req.getHeaders().get("Authorization")); afterRedirectHeader.set(req.getHeaders().get("Authorization"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
@ -2287,7 +2342,7 @@ class HubConnectionTest {
public void sameHeaderSetTwiceGetsOverwritten() { public void sameHeaderSetTwiceGetsOverwritten() {
AtomicReference<String> header = new AtomicReference<>(); AtomicReference<String> header = new AtomicReference<>();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> { (req) -> {
header.set(req.getHeaders().get("ExampleHeader")); header.set(req.getHeaders().get("ExampleHeader"));
return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" return Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
@ -2332,8 +2387,8 @@ class HubConnectionTest {
public void hubConnectionCanBeStartedAfterBeingStoppedAndRedirected() { public void hubConnectionCanBeStartedAfterBeingStoppedAndRedirected() {
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}"))) .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> Single.just(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}")))
.on("POST", "http://testexample.com/negotiate", (req) -> Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" .on("POST", "http://testexample.com/negotiate?negotiateVersion=1", (req) -> Single.just(new HttpResponse(200, "", "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\""
+ "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"))); + "availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}]}")));
HubConnection hubConnection = HubConnectionBuilder HubConnection hubConnection = HubConnectionBuilder
@ -2355,7 +2410,7 @@ class HubConnectionTest {
@Test @Test
public void non200FromNegotiateThrowsError() { public void non200FromNegotiateThrowsError() {
TestHttpClient client = new TestHttpClient() TestHttpClient client = new TestHttpClient()
.on("POST", "http://example.com/negotiate", .on("POST", "http://example.com/negotiate?negotiateVersion=1",
(req) -> Single.just(new HttpResponse(500, "Internal server error", ""))); (req) -> Single.just(new HttpResponse(500, "Internal server error", "")));
MockTransport transport = new MockTransport(); MockTransport transport = new MockTransport();

View File

@ -15,8 +15,9 @@ import com.google.gson.stream.JsonReader;
class NegotiateResponseTest { class NegotiateResponseTest {
@Test @Test
public void VerifyNegotiateResponse() { public void VerifyNegotiateResponse() {
String stringNegotiateResponse = "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\",\"" + String stringNegotiateResponse = "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\"," +
"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}," + "\"negotiateVersion\": 99, \"connectionToken\":\"connection-token-value\"," +
"\"availableTransports\":[{\"transport\":\"WebSockets\",\"transferFormats\":[\"Text\",\"Binary\"]}," +
"{\"transport\":\"ServerSentEvents\",\"transferFormats\":[\"Text\"]}," + "{\"transport\":\"ServerSentEvents\",\"transferFormats\":[\"Text\"]}," +
"{\"transport\":\"LongPolling\",\"transferFormats\":[\"Text\",\"Binary\"]}]}"; "{\"transport\":\"LongPolling\",\"transferFormats\":[\"Text\",\"Binary\"]}]}";
NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse))); NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse)));
@ -26,6 +27,8 @@ class NegotiateResponseTest {
assertNull(negotiateResponse.getAccessToken()); assertNull(negotiateResponse.getAccessToken());
assertNull(negotiateResponse.getRedirectUrl()); assertNull(negotiateResponse.getRedirectUrl());
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId()); assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId());
assertEquals("connection-token-value", negotiateResponse.getConnectionToken());
assertEquals(99, negotiateResponse.getVersion());
} }
@Test @Test
@ -56,4 +59,23 @@ class NegotiateResponseTest {
NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse))); NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse)));
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId()); assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId());
} }
@Test
public void NegotiateResponseWithNegotiateVersion() {
String stringNegotiateResponse = "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\"," +
"\"negotiateVersion\": 99}";
NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse)));
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId());
assertEquals(99, negotiateResponse.getVersion());
}
@Test
public void NegotiateResponseWithConnectionToken() {
String stringNegotiateResponse = "{\"connectionId\":\"bVOiRPG8-6YiJ6d7ZcTOVQ\"," +
"\"negotiateVersion\": 99, \"connectionToken\":\"connection-token-value\"}";
NegotiateResponse negotiateResponse = new NegotiateResponse(new JsonReader(new StringReader(stringNegotiateResponse)));
assertEquals("bVOiRPG8-6YiJ6d7ZcTOVQ", negotiateResponse.getConnectionId());
assertEquals("connection-token-value", negotiateResponse.getConnectionToken());
assertEquals(99, negotiateResponse.getVersion());
}
} }

View File

@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
[InlineData("{\"url\": \"http://foo.com/chat\"}", null, null, "http://foo.com/chat", null, 0, null)] [InlineData("{\"url\": \"http://foo.com/chat\"}", null, null, "http://foo.com/chat", null, 0, null)]
[InlineData("{\"url\": \"http://foo.com/chat\", \"accessToken\": \"token\"}", null, null, "http://foo.com/chat", "token", 0, null)] [InlineData("{\"url\": \"http://foo.com/chat\", \"accessToken\": \"token\"}", null, null, "http://foo.com/chat", "token", 0, null)]
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0, null)] [InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0, null)]
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0, null)]
[InlineData("{\"negotiateVersion\":123,\"connectionId\":\"123\",\"connectionToken\":\"789\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 123, "789")]
[InlineData("{\"negotiateVersion\":123,\"negotiateVersion\":321, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 321, "789")] [InlineData("{\"negotiateVersion\":123,\"negotiateVersion\":321, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 321, "789")]
[InlineData("{\"ignore\":123,\"negotiateVersion\":123, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 123, "789")] [InlineData("{\"ignore\":123,\"negotiateVersion\":123, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 123, "789")]
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123, \"connectionToken\":\"789\"}", "123", new string[0], null, null, 123, "789")] [InlineData("{\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123, \"connectionToken\":\"789\"}", "123", new string[0], null, null, 123, "789")]