// We don't need to synchronize as replacing the "ws-decoder" will // process using the same thread. private void invokeOnSucces(Channel channel, WebSocketUpgradeHandler h) { if (!h.touchSuccess()) { try { h.onSuccess(nettyConfig.getNettyWebSocketFactory().newNettyWebSocket(channel, config)); } catch (Exception ex) { logger.warn("onSuccess unexpected exception", ex); } } }
@Override public void handle(Channel channel, NettyResponseFuture<?> future, Object e) throws Exception { if (e instanceof HttpResponse) { HttpResponse response = (HttpResponse) e; Channels.setAttribute(channel, new UpgradeCallback(future, channel, response)); } else if (e instanceof WebSocketFrame) { final WebSocketFrame frame = (WebSocketFrame) e; WebSocketUpgradeHandler handler = WebSocketUpgradeHandler.class.cast(future.getAsyncHandler()); NettyWebSocket webSocket = NettyWebSocket.class.cast(handler.onCompleted()); invokeOnSucces(channel, handler); if (webSocket != null) { if (frame instanceof CloseWebSocketFrame) { Channels.setDiscard(channel); CloseWebSocketFrame closeFrame = CloseWebSocketFrame.class.cast(frame); webSocket.onClose(closeFrame.statusCode(), closeFrame.reasonText()); } else { ByteBuf buf = frame.content(); if (buf != null && buf.readableBytes() > 0) { try { NettyResponseBodyPart part = nettyConfig .getBodyPartFactory() .newResponseBodyPart(buf, frame.isFinalFragment()); handler.onBodyPartReceived(part); if (frame instanceof BinaryWebSocketFrame) { webSocket.onBinaryFragment(part); } else if (frame instanceof TextWebSocketFrame) { webSocket.onTextFragment(part); } else if (frame instanceof PingWebSocketFrame) { webSocket.onPing(part); } else if (frame instanceof PongWebSocketFrame) { webSocket.onPong(part); } } finally { buf.release(); } } } } else { logger.debug("UpgradeHandler returned a null NettyWebSocket "); } } else { logger.error("Invalid message {}", e); } }
@Override public void onClose(NettyResponseFuture<?> future) { logger.trace("onClose"); try { WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler(); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); logger.trace("Connection was closed abnormally (that is, with no close frame being sent)."); if (webSocket != null) webSocket.close( 1006, "Connection was closed abnormally (that is, with no close frame being sent)."); } catch (Throwable t) { logger.error("onError", t); } }
@Override public void onError(NettyResponseFuture<?> future, Throwable e) { logger.warn("onError {}", e); try { WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler(); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); if (webSocket != null) { webSocket.onError(e.getCause()); webSocket.close(); } } catch (Throwable t) { logger.error("onError", t); } }
@Override public void call() throws Exception { WebSocketUpgradeHandler handler = WebSocketUpgradeHandler.class.cast(future.getAsyncHandler()); Request request = future.getRequest(); HttpResponseStatus status = new NettyResponseStatus(future.getUri(), config, response, channel); HttpResponseHeaders responseHeaders = new NettyResponseHeaders(response.headers()); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); if (exitAfterProcessingFilters(channel, future, handler, status, responseHeaders)) { return; } future.setHttpHeaders(response.headers()); if (exitAfterHandlingRedirect( channel, future, response, request, response.getStatus().code(), realm)) return; boolean validStatus = response.getStatus().equals(SWITCHING_PROTOCOLS); boolean validUpgrade = response.headers().get(HttpHeaders.Names.UPGRADE) != null; String connection = response.headers().get(HttpHeaders.Names.CONNECTION); if (connection == null) connection = response.headers().get(HttpHeaders.Names.CONNECTION.toLowerCase(Locale.ENGLISH)); boolean validConnection = HttpHeaders.Values.UPGRADE.equalsIgnoreCase(connection); boolean statusReceived = handler.onStatusReceived(status) == State.UPGRADE; if (!statusReceived) { try { handler.onCompleted(); } finally { future.done(); } return; } final boolean headerOK = handler.onHeadersReceived(responseHeaders) == State.CONTINUE; if (!headerOK || !validStatus || !validUpgrade || !validConnection) { requestSender.abort(channel, future, new IOException("Invalid handshake response")); return; } String accept = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT); String key = getAcceptKey( future .getNettyRequest() .getHttpRequest() .headers() .get(HttpHeaders.Names.SEC_WEBSOCKET_KEY)); if (accept == null || !accept.equals(key)) { requestSender.abort( channel, future, new IOException( String.format("Invalid challenge. Actual: %s. Expected: %s", accept, key))); } channelManager.upgradePipelineForWebSockets(channel.pipeline()); invokeOnSucces(channel, handler); future.done(); // set back the future so the protocol gets notified of frames Channels.setAttribute(channel, future); }