4 changed files with 198 additions and 103 deletions
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* 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.adapter; |
||||
|
||||
import java.net.URI; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import io.netty.buffer.ByteBuf; |
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; |
||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; |
||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; |
||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; |
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; |
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrame; |
||||
import reactor.core.publisher.Flux; |
||||
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer; |
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
import org.springframework.web.reactive.socket.WebSocketMessage; |
||||
import org.springframework.web.reactive.socket.WebSocketSession; |
||||
|
||||
/** |
||||
* Base class for Netty-based {@link WebSocketSession} adapters. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.0 |
||||
*/ |
||||
public abstract class NettyWebSocketSessionSupport<T> extends WebSocketSessionSupport<T> { |
||||
|
||||
private static final Map<Class<?>, WebSocketMessage.Type> MESSAGE_TYPES; |
||||
|
||||
static { |
||||
MESSAGE_TYPES = new HashMap<>(4); |
||||
MESSAGE_TYPES.put(TextWebSocketFrame.class, WebSocketMessage.Type.TEXT); |
||||
MESSAGE_TYPES.put(BinaryWebSocketFrame.class, WebSocketMessage.Type.BINARY); |
||||
MESSAGE_TYPES.put(PingWebSocketFrame.class, WebSocketMessage.Type.PING); |
||||
MESSAGE_TYPES.put(PongWebSocketFrame.class, WebSocketMessage.Type.PONG); |
||||
} |
||||
|
||||
|
||||
protected final String id; |
||||
|
||||
protected final URI uri; |
||||
|
||||
protected final NettyDataBufferFactory bufferFactory; |
||||
|
||||
|
||||
protected NettyWebSocketSessionSupport(T delegate, URI uri, NettyDataBufferFactory factory) { |
||||
super(delegate); |
||||
Assert.notNull(uri, "'uri' is required."); |
||||
Assert.notNull(uri, "'bufferFactory' is required."); |
||||
this.uri = uri; |
||||
this.bufferFactory = factory; |
||||
this.id = ObjectUtils.getIdentityHexString(getDelegate()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String getId() { |
||||
return this.id; |
||||
} |
||||
|
||||
@Override |
||||
public URI getUri() { |
||||
return this.uri; |
||||
} |
||||
|
||||
protected Flux<WebSocketMessage> toMessageFlux(Flux<WebSocketFrame> frameFlux) { |
||||
return frameFlux |
||||
.filter(frame -> !(frame instanceof CloseWebSocketFrame)) |
||||
.window() |
||||
.concatMap(flux -> flux.takeUntil(WebSocketFrame::isFinalFragment).buffer()) |
||||
.map(this::toMessage); |
||||
} |
||||
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent") |
||||
private WebSocketMessage toMessage(List<WebSocketFrame> frames) { |
||||
Class<?> frameType = frames.get(0).getClass(); |
||||
if (frames.size() == 1) { |
||||
NettyDataBuffer buffer = this.bufferFactory.wrap(frames.get(0).content()); |
||||
return WebSocketMessage.create(MESSAGE_TYPES.get(frameType), buffer); |
||||
} |
||||
return frames.stream() |
||||
.map(socketFrame -> bufferFactory.wrap(socketFrame.content())) |
||||
.reduce(NettyDataBuffer::write) |
||||
.map(buffer -> WebSocketMessage.create(MESSAGE_TYPES.get(frameType), buffer)) |
||||
.get(); |
||||
} |
||||
|
||||
protected WebSocketFrame toFrame(WebSocketMessage message) { |
||||
ByteBuf byteBuf = NettyDataBufferFactory.toByteBuf(message.getPayload()); |
||||
if (WebSocketMessage.Type.TEXT.equals(message.getType())) { |
||||
return new TextWebSocketFrame(byteBuf); |
||||
} |
||||
else if (WebSocketMessage.Type.BINARY.equals(message.getType())) { |
||||
return new BinaryWebSocketFrame(byteBuf); |
||||
} |
||||
else if (WebSocketMessage.Type.PING.equals(message.getType())) { |
||||
return new PingWebSocketFrame(byteBuf); |
||||
} |
||||
else if (WebSocketMessage.Type.PONG.equals(message.getType())) { |
||||
return new PongWebSocketFrame(byteBuf); |
||||
} |
||||
else { |
||||
throw new IllegalArgumentException("Unexpected message type: " + message.getType()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
/* |
||||
* 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.adapter; |
||||
|
||||
import java.net.URI; |
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.reactive.socket.WebSocketHandler; |
||||
|
||||
/** |
||||
* Base class for {@link WebSocketHandler} implementations. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.0 |
||||
*/ |
||||
public abstract class WebSocketHandlerAdapterSupport { |
||||
|
||||
private final URI uri; |
||||
|
||||
private final WebSocketHandler delegate; |
||||
|
||||
|
||||
protected WebSocketHandlerAdapterSupport(ServerHttpRequest request, WebSocketHandler handler) { |
||||
Assert.notNull("'request' is required"); |
||||
Assert.notNull("'handler' handler is required"); |
||||
this.uri = request.getURI(); |
||||
this.delegate = handler; |
||||
} |
||||
|
||||
|
||||
public URI getUri() { |
||||
return this.uri; |
||||
} |
||||
|
||||
public WebSocketHandler getDelegate() { |
||||
return this.delegate; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue