I must admit MesageList was pain in the ass. Instead of forcing a handler always loop over the list of messages, this commit splits messageReceived(ctx, list) into two event handlers: - messageReceived(ctx, msg) - mmessageReceivedLast(ctx) When Netty reads one or more messages, messageReceived(ctx, msg) event is triggered for each message. Once the current read operation is finished, messageReceivedLast() is triggered to tell the handler that the last messageReceived() was the last message in the current batch. Similarly, for outbound, write(ctx, list) has been split into two: - write(ctx, msg) - flush(ctx, promise) Instead of writing a list of message with a promise, a user is now supposed to call write(msg) multiple times and then call flush() to actually flush the buffered messages. Please note that write() doesn't have a promise with it. You must call flush() to get notified on completion. (or you can use writeAndFlush()) Other changes: - Because MessageList is completely hidden, codec framework uses List<Object> instead of MessageList as an output parameter.
101 lines
4.2 KiB
Java
101 lines
4.2 KiB
Java
/*
|
|
* Copyright 2012 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you 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 io.netty.handler.codec.http.websocketx;
|
|
|
|
import io.netty.channel.ChannelFuture;
|
|
import io.netty.channel.ChannelFutureListener;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
import io.netty.channel.ChannelPipeline;
|
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
|
import io.netty.handler.codec.http.FullHttpRequest;
|
|
import io.netty.handler.codec.http.HttpHeaders;
|
|
import io.netty.handler.codec.http.HttpRequest;
|
|
import io.netty.handler.codec.http.HttpResponse;
|
|
import io.netty.handler.ssl.SslHandler;
|
|
|
|
import static io.netty.handler.codec.http.HttpHeaders.*;
|
|
import static io.netty.handler.codec.http.HttpMethod.*;
|
|
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
|
|
|
/**
|
|
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
|
|
*/
|
|
class WebSocketServerProtocolHandshakeHandler
|
|
extends ChannelInboundHandlerAdapter {
|
|
|
|
private final String websocketPath;
|
|
private final String subprotocols;
|
|
private final boolean allowExtensions;
|
|
|
|
public WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols,
|
|
boolean allowExtensions) {
|
|
this.websocketPath = websocketPath;
|
|
this.subprotocols = subprotocols;
|
|
this.allowExtensions = allowExtensions;
|
|
}
|
|
|
|
@Override
|
|
public void messageReceived(final ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
FullHttpRequest req = (FullHttpRequest) msg;
|
|
if (req.getMethod() != GET) {
|
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
|
return;
|
|
}
|
|
|
|
final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
|
|
getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions);
|
|
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
|
|
if (handshaker == null) {
|
|
WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
|
|
} else {
|
|
final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);
|
|
handshakeFuture.addListener(new ChannelFutureListener() {
|
|
@Override
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
if (!future.isSuccess()) {
|
|
ctx.fireExceptionCaught(future.cause());
|
|
} else {
|
|
ctx.fireUserEventTriggered(
|
|
WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
|
|
}
|
|
}
|
|
});
|
|
WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker);
|
|
ctx.pipeline().replace(this, "WS403Responder",
|
|
WebSocketServerProtocolHandler.forbiddenHttpRequestResponder());
|
|
}
|
|
}
|
|
|
|
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
|
ChannelFuture f = ctx.channel().write(res).flush();
|
|
if (!isKeepAlive(req) || res.getStatus().code() != 200) {
|
|
f.addListener(ChannelFutureListener.CLOSE);
|
|
}
|
|
}
|
|
|
|
private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) {
|
|
String protocol = "ws";
|
|
if (cp.get(SslHandler.class) != null) {
|
|
// SSL in use so use Secure WebSockets
|
|
protocol = "wss";
|
|
}
|
|
return protocol + "://" + req.headers().get(HttpHeaders.Names.HOST) + path;
|
|
}
|
|
|
|
}
|