4 changed files with 228 additions and 3 deletions
@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
|
||||
/* |
||||
* Copyright 2002-2016 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.reactive.socket.client; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Optional; |
||||
|
||||
import org.eclipse.jetty.websocket.api.Session; |
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse; |
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket; |
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
import reactor.core.publisher.MonoProcessor; |
||||
|
||||
import org.springframework.context.Lifecycle; |
||||
import org.springframework.core.io.buffer.DataBufferFactory; |
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.util.ObjectUtils; |
||||
import org.springframework.web.reactive.socket.HandshakeInfo; |
||||
import org.springframework.web.reactive.socket.WebSocketHandler; |
||||
import org.springframework.web.reactive.socket.adapter.JettyWebSocketHandlerAdapter; |
||||
|
||||
/** |
||||
* A Jetty based implementation of {@link WebSocketClient}. |
||||
* |
||||
* @author Violeta Georgieva |
||||
* @since 5.0 |
||||
*/ |
||||
public class JettyWebSocketClient extends WebSocketClientSupport implements WebSocketClient, Lifecycle { |
||||
|
||||
private final DataBufferFactory bufferFactory = new DefaultDataBufferFactory(); |
||||
|
||||
private final org.eclipse.jetty.websocket.client.WebSocketClient wsClient; |
||||
|
||||
private final Object lifecycleMonitor = new Object(); |
||||
|
||||
|
||||
/** |
||||
* Default constructor that creates an instance of |
||||
* {@link org.eclipse.jetty.websocket.client.WebSocketClient}. |
||||
*/ |
||||
public JettyWebSocketClient() { |
||||
this(new org.eclipse.jetty.websocket.client.WebSocketClient()); |
||||
} |
||||
|
||||
/** |
||||
* Constructor that accepts an existing |
||||
* {@link org.eclipse.jetty.websocket.client.WebSocketClient} instance. |
||||
* @param wsClient a web socket client |
||||
*/ |
||||
public JettyWebSocketClient(org.eclipse.jetty.websocket.client.WebSocketClient wsClient) { |
||||
this.wsClient = wsClient; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> execute(URI url, WebSocketHandler handler) { |
||||
return execute(url, new HttpHeaders(), handler); |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Void> execute(URI url, HttpHeaders headers, WebSocketHandler handler) { |
||||
return connectInternal(url, headers, handler); |
||||
} |
||||
|
||||
private Mono<Void> connectInternal(URI url, HttpHeaders headers, WebSocketHandler handler) { |
||||
MonoProcessor<Void> processor = MonoProcessor.create(); |
||||
return Mono.fromCallable( |
||||
() -> { |
||||
HandshakeInfo info = new HandshakeInfo(url, Mono.empty()); |
||||
Object adapter = new JettyClientAdapter(handler, info, this.bufferFactory, processor); |
||||
ClientUpgradeRequest request = createRequest(url, headers, handler); |
||||
return this.wsClient.connect(adapter, url, request); |
||||
}) |
||||
.then(processor); |
||||
} |
||||
|
||||
private ClientUpgradeRequest createRequest(URI url, HttpHeaders headers, WebSocketHandler handler) { |
||||
ClientUpgradeRequest request = new ClientUpgradeRequest(); |
||||
|
||||
String[] protocols = beforeHandshake(url, headers, handler); |
||||
if (!ObjectUtils.isEmpty(protocols)) { |
||||
request.setSubProtocols(protocols); |
||||
} |
||||
|
||||
headers.forEach((k, v) -> request.setHeader(k, v)); |
||||
|
||||
return request; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void start() { |
||||
synchronized (this.lifecycleMonitor) { |
||||
if (!isRunning()) { |
||||
try { |
||||
this.wsClient.start(); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("Failed to start Jetty WebSocketClient", ex); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
synchronized (this.lifecycleMonitor) { |
||||
if (isRunning()) { |
||||
try { |
||||
this.wsClient.stop(); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("Error stopping Jetty WebSocketClient", ex); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isRunning() { |
||||
synchronized (this.lifecycleMonitor) { |
||||
return this.wsClient.isStarted(); |
||||
} |
||||
} |
||||
|
||||
|
||||
@WebSocket |
||||
private static final class JettyClientAdapter extends JettyWebSocketHandlerAdapter { |
||||
|
||||
public JettyClientAdapter(WebSocketHandler delegate, |
||||
HandshakeInfo info, DataBufferFactory bufferFactory, MonoProcessor<Void> processor) { |
||||
super(delegate, info, bufferFactory, processor); |
||||
} |
||||
|
||||
@Override |
||||
public void onWebSocketConnect(Session session) { |
||||
UpgradeResponse response = session.getUpgradeResponse(); |
||||
|
||||
getHandshakeInfo().setHeaders(getResponseHeaders(response)); |
||||
getHandshakeInfo().setSubProtocol( |
||||
Optional.ofNullable(response.getAcceptedSubProtocol())); |
||||
|
||||
super.onWebSocketConnect(session); |
||||
} |
||||
|
||||
private HttpHeaders getResponseHeaders(UpgradeResponse response) { |
||||
HttpHeaders responseHeaders = new HttpHeaders(); |
||||
response.getHeaders().forEach((k, v) -> responseHeaders.put(k, v)); |
||||
return responseHeaders; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue