Skip to content

Commit 9f557cf

Browse files
committed
Polish SockJS client
1 parent 6a59d00 commit 9f557cf

File tree

9 files changed

+333
-248
lines changed

9 files changed

+333
-248
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.net.URI;
2020
import java.util.Arrays;
21+
import java.util.Collections;
2122
import java.util.List;
2223

2324
import org.apache.commons.logging.Log;
@@ -66,7 +67,7 @@ public abstract class AbstractXhrTransport implements XhrTransport {
6667
@Override
6768
public List<TransportType> getTransportTypes() {
6869
return (isXhrStreamingDisabled() ?
69-
Arrays.asList(TransportType.XHR) :
70+
Collections.singletonList(TransportType.XHR) :
7071
Arrays.asList(TransportType.XHR_STREAMING, TransportType.XHR));
7172
}
7273

@@ -111,6 +112,35 @@ public HttpHeaders getRequestHeaders() {
111112
return this.requestHeaders;
112113
}
113114

115+
116+
// Transport methods
117+
118+
@Override
119+
public ListenableFuture<WebSocketSession> connect(TransportRequest request, WebSocketHandler handler) {
120+
SettableListenableFuture<WebSocketSession> connectFuture = new SettableListenableFuture<WebSocketSession>();
121+
XhrClientSockJsSession session = new XhrClientSockJsSession(request, handler, this, connectFuture);
122+
request.addTimeoutTask(session.getTimeoutTask());
123+
124+
URI receiveUrl = request.getTransportUrl();
125+
if (logger.isDebugEnabled()) {
126+
logger.debug("Starting XHR " +
127+
(isXhrStreamingDisabled() ? "Polling" : "Streaming") + "session url=" + receiveUrl);
128+
}
129+
130+
HttpHeaders handshakeHeaders = new HttpHeaders();
131+
handshakeHeaders.putAll(request.getHandshakeHeaders());
132+
handshakeHeaders.putAll(getRequestHeaders());
133+
134+
connectInternal(request, handler, receiveUrl, handshakeHeaders, session, connectFuture);
135+
return connectFuture;
136+
}
137+
138+
protected abstract void connectInternal(TransportRequest request, WebSocketHandler handler,
139+
URI receiveUrl, HttpHeaders handshakeHeaders, XhrClientSockJsSession session,
140+
SettableListenableFuture<WebSocketSession> connectFuture);
141+
142+
// InfoReceiver methods
143+
114144
@Override
115145
public String executeInfoRequest(URI infoUrl) {
116146
if (logger.isDebugEnabled()) {
@@ -131,6 +161,8 @@ public String executeInfoRequest(URI infoUrl) {
131161

132162
protected abstract ResponseEntity<String> executeInfoRequestInternal(URI infoUrl);
133163

164+
// XhrTransport methods
165+
134166
@Override
135167
public void executeSendRequest(URI url, TextMessage message) {
136168
if (logger.isTraceEnabled()) {
@@ -150,30 +182,6 @@ public void executeSendRequest(URI url, TextMessage message) {
150182

151183
protected abstract ResponseEntity<String> executeSendRequestInternal(URI url, HttpHeaders headers, TextMessage message);
152184

153-
@Override
154-
public ListenableFuture<WebSocketSession> connect(TransportRequest request, WebSocketHandler handler) {
155-
SettableListenableFuture<WebSocketSession> connectFuture = new SettableListenableFuture<WebSocketSession>();
156-
XhrClientSockJsSession session = new XhrClientSockJsSession(request, handler, this, connectFuture);
157-
request.addTimeoutTask(session.getTimeoutTask());
158-
159-
URI receiveUrl = request.getTransportUrl();
160-
if (logger.isDebugEnabled()) {
161-
logger.debug("Starting XHR " +
162-
(isXhrStreamingDisabled() ? "Polling" : "Streaming") + "session url=" + receiveUrl);
163-
}
164-
165-
HttpHeaders handshakeHeaders = new HttpHeaders();
166-
handshakeHeaders.putAll(request.getHandshakeHeaders());
167-
handshakeHeaders.putAll(getRequestHeaders());
168-
169-
connectInternal(request, handler, receiveUrl, handshakeHeaders, session, connectFuture);
170-
return connectFuture;
171-
}
172-
173-
protected abstract void connectInternal(TransportRequest request, WebSocketHandler handler,
174-
URI receiveUrl, HttpHeaders handshakeHeaders, XhrClientSockJsSession session,
175-
SettableListenableFuture<WebSocketSession> connectFuture);
176-
177185

178186
@Override
179187
public String toString() {

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/InfoReceiver.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
1+
/*
2+
* Copyright 2002-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.springframework.web.socket.sockjs.client;
217

318
import java.net.URI;
419

520
/**
6-
* A simple contract for executing the SockJS "Info" request before the SockJS
7-
* session starts. The request is used to check server capabilities such as
8-
* whether it permits use of the WebSocket transport.
21+
* A component that can execute the SockJS "Info" request that needs to be
22+
* performed before the SockJS session starts in order to check server endpoint
23+
* capabilities such as whether the endpoint permits use of WebSocket.
24+
*
25+
* <p>Typically {@link XhrTransport} implementations are also implementations
26+
* of this contract.
927
*
1028
* @author Rossen Stoyanchev
1129
* @since 4.1
30+
* @see AbstractXhrTransport
1231
*/
1332
public interface InfoReceiver {
1433

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/JettyXhrTransport.java

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import org.springframework.web.socket.sockjs.SockJsTransportFailureException;
4545
import org.springframework.web.socket.sockjs.frame.SockJsFrame;
4646

47-
4847
/**
4948
* An XHR transport based on Jetty's {@link org.eclipse.jetty.client.HttpClient}.
5049
*
@@ -105,6 +104,25 @@ public boolean isRunning() {
105104
return this.httpClient.isRunning();
106105
}
107106

107+
108+
@Override
109+
protected void connectInternal(TransportRequest request, WebSocketHandler handler,
110+
URI url, HttpHeaders handshakeHeaders, XhrClientSockJsSession session,
111+
SettableListenableFuture<WebSocketSession> connectFuture) {
112+
113+
SockJsResponseListener listener = new SockJsResponseListener(url, getRequestHeaders(), session, connectFuture);
114+
executeReceiveRequest(url, handshakeHeaders, listener);
115+
}
116+
117+
private void executeReceiveRequest(URI url, HttpHeaders headers, SockJsResponseListener listener) {
118+
if (logger.isTraceEnabled()) {
119+
logger.trace("Starting XHR receive request, url=" + url);
120+
}
121+
Request httpRequest = this.httpClient.newRequest(url).method(HttpMethod.POST);
122+
addHttpHeaders(httpRequest, headers);
123+
httpRequest.send(listener);
124+
}
125+
108126
@Override
109127
protected ResponseEntity<String> executeInfoRequestInternal(URI infoUrl) {
110128
return executeRequest(infoUrl, HttpMethod.GET, getRequestHeaders(), null);
@@ -157,28 +175,11 @@ private static HttpHeaders toHttpHeaders(HttpFields httpFields) {
157175
return responseHeaders;
158176
}
159177

160-
@Override
161-
protected void connectInternal(TransportRequest request, WebSocketHandler handler,
162-
URI url, HttpHeaders handshakeHeaders, XhrClientSockJsSession session,
163-
SettableListenableFuture<WebSocketSession> connectFuture) {
164-
165-
SockJsResponseListener listener = new SockJsResponseListener(url, getRequestHeaders(), session, connectFuture);
166-
executeReceiveRequest(url, handshakeHeaders, listener);
167-
}
168-
169-
private void executeReceiveRequest(URI url, HttpHeaders headers, SockJsResponseListener listener) {
170-
if (logger.isTraceEnabled()) {
171-
logger.trace("Starting XHR receive request, url=" + url);
172-
}
173-
Request httpRequest = this.httpClient.newRequest(url).method(HttpMethod.POST);
174-
addHttpHeaders(httpRequest, headers);
175-
httpRequest.send(listener);
176-
}
177-
178178

179179
/**
180-
* Splits the body of an HTTP response into SockJS frames and delegates those
181-
* to an {@link XhrClientSockJsSession}.
180+
* Jetty client {@link org.eclipse.jetty.client.api.Response.Listener Response
181+
* Listener} that splits the body of the response into SockJS frames and
182+
* delegates them to the {@link XhrClientSockJsSession}.
182183
*/
183184
private class SockJsResponseListener extends Response.Listener.Adapter {
184185

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransport.java

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -93,18 +93,6 @@ public TaskExecutor getTaskExecutor() {
9393
}
9494

9595

96-
@Override
97-
public ResponseEntity<String> executeInfoRequestInternal(URI infoUrl) {
98-
RequestCallback requestCallback = new XhrRequestCallback(getRequestHeaders());
99-
return this.restTemplate.execute(infoUrl, HttpMethod.GET, requestCallback, textExtractor);
100-
}
101-
102-
@Override
103-
public ResponseEntity<String> executeSendRequestInternal(URI url, HttpHeaders headers, TextMessage message) {
104-
RequestCallback requestCallback = new XhrRequestCallback(headers, message.getPayload());
105-
return this.restTemplate.execute(url, HttpMethod.POST, requestCallback, textExtractor);
106-
}
107-
10896
@Override
10997
protected void connectInternal(final TransportRequest request, final WebSocketHandler handler,
11098
final URI receiveUrl, final HttpHeaders handshakeHeaders, final XhrClientSockJsSession session,
@@ -143,11 +131,23 @@ public void run() {
143131
});
144132
}
145133

134+
@Override
135+
public ResponseEntity<String> executeInfoRequestInternal(URI infoUrl) {
136+
RequestCallback requestCallback = new XhrRequestCallback(getRequestHeaders());
137+
return this.restTemplate.execute(infoUrl, HttpMethod.GET, requestCallback, textResponseExtractor);
138+
}
139+
140+
@Override
141+
public ResponseEntity<String> executeSendRequestInternal(URI url, HttpHeaders headers, TextMessage message) {
142+
RequestCallback requestCallback = new XhrRequestCallback(headers, message.getPayload());
143+
return this.restTemplate.execute(url, HttpMethod.POST, requestCallback, textResponseExtractor);
144+
}
145+
146146

147147
/**
148148
* A simple ResponseExtractor that reads the body into a String.
149149
*/
150-
private final static ResponseExtractor<ResponseEntity<String>> textExtractor =
150+
private final static ResponseExtractor<ResponseEntity<String>> textResponseExtractor =
151151
new ResponseExtractor<ResponseEntity<String>>() {
152152
@Override
153153
public ResponseEntity<String> extractData(ClientHttpResponse response) throws IOException {
@@ -161,7 +161,6 @@ public ResponseEntity<String> extractData(ClientHttpResponse response) throws IO
161161
}
162162
};
163163

164-
165164
/**
166165
* A RequestCallback to add the headers and (optionally) String content.
167166
*/
@@ -191,7 +190,6 @@ public void doWithRequest(ClientHttpRequest request) throws IOException {
191190
}
192191
}
193192

194-
195193
/**
196194
* Splits the body of an HTTP response into SockJS frames and delegates those
197195
* to an {@link XhrClientSockJsSession}.

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/SockJsUrlInfo.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
import org.springframework.web.util.UriComponentsBuilder;
2626

2727
/**
28-
* Given the base URL to a SockJS server endpoint, also provides methods to
29-
* generate and obtain session and a server id used for construct a transport URL.
28+
* Container for the base URL of a SockJS endpoint with additional helper methods
29+
* to derive related SockJS URLs as the {@link #getInfoUrl() info} URL and
30+
* {@link #getTransportUrl(TransportType) transport} URLs.
3031
*
3132
* @author Rossen Stoyanchev
3233
* @since 4.1

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/TransportRequest.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
1+
/*
2+
* Copyright 2002-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.springframework.web.socket.sockjs.client;
217

318
import java.net.URI;
419
import java.security.Principal;
520

621
import org.springframework.http.HttpHeaders;
22+
import org.springframework.web.socket.WebSocketHandler;
23+
import org.springframework.web.socket.WebSocketHttpHeaders;
724
import org.springframework.web.socket.sockjs.frame.SockJsMessageCodec;
825

926
/**
10-
* Represents a request to connect to a SockJS service using a specific
11-
* Transport. A single SockJS request however may require falling back
12-
* and therefore multiple TransportRequest instances.
27+
* Exposes information, typically to {@link Transport} and
28+
* {@link AbstractClientSockJsSession session} implementations, about a request
29+
* to connect to a SockJS server endpoint over a given transport.
30+
*
31+
* <p>Note that a single request to connect via {@link SockJsClient} may result
32+
* in multiple instances of {@link TransportRequest}, one for each transport
33+
* before a connection is successfully established.
1334
*
1435
* @author Rossen Stoyanchev
1536
* @since 4.1

0 commit comments

Comments
 (0)