/** Notify all the handshake futures about the failure during the handshake. */ private void setHandshakeFailure(Throwable cause) { // Release all resources such as internal buffers that SSLEngine // is managing. engine.closeOutbound(); try { engine.closeInbound(); } catch (SSLException e) { if (logger.isDebugEnabled()) { logger.debug( "SSLEngine.closeInbound() raised an exception after " + "a handshake failure.", e); } } if (cause == null) { cause = new ClosedChannelException(); } for (; ; ) { ChannelFuture f = handshakeFutures.poll(); if (f == null) { break; } f.setFailure(cause); } flush0(ctx, 0, cause); }
private void callAfterAdd(ChannelHandlerContext ctx) { try { ctx.handler().afterAdd(ctx); } catch (Throwable t) { boolean removed = false; try { remove((DefaultChannelHandlerContext) ctx, false); removed = true; } catch (Throwable t2) { if (logger.isWarnEnabled()) { logger.warn("Failed to remove a handler: " + ctx.name(), t2); } } if (removed) { throw new ChannelPipelineException( ctx.handler().getClass().getName() + ".afterAdd() has thrown an exception; removed.", t); } else { throw new ChannelPipelineException( ctx.handler().getClass().getName() + ".afterAdd() has thrown an exception; also failed to remove.", t); } } }
private void replace0( DefaultChannelHandlerContext ctx, String newName, DefaultChannelHandlerContext newCtx, boolean forward) { boolean sameName = ctx.name().equals(newName); DefaultChannelHandlerContext prev = ctx.prev; DefaultChannelHandlerContext next = ctx.next; newCtx.prev = prev; newCtx.next = next; callBeforeRemove(ctx); callBeforeAdd(newCtx); prev.next = newCtx; next.prev = newCtx; if (!sameName) { name2ctx.remove(ctx.name()); } name2ctx.put(newName, newCtx); ChannelPipelineException removeException = null; ChannelPipelineException addException = null; boolean removed = false; try { callAfterRemove(ctx, forward); removed = true; } catch (ChannelPipelineException e) { removeException = e; } boolean added = false; try { callAfterAdd(newCtx); added = true; } catch (ChannelPipelineException e) { addException = e; } if (!removed && !added) { logger.warn(removeException.getMessage(), removeException); logger.warn(addException.getMessage(), addException); throw new ChannelPipelineException( "Both " + ctx.handler().getClass().getName() + ".afterRemove() and " + newCtx.handler().getClass().getName() + ".afterAdd() failed; see logs."); } else if (!removed) { throw removeException; } else if (!added) { throw addException; } }
@Override protected void messageReceived(ChannelHandlerContext ctx, Freeable msg) throws Exception { if (logger.isWarnEnabled()) { logger.warn( "Freeable reached end-of-pipeline, call " + msg + ".free() to" + " guard against resource leakage!"); } msg.free(); }
void register(SctpChannelImpl channel, ChannelFuture future) { boolean server = !(channel instanceof SctpClientChannel); Runnable registerTask = new RegisterTask(channel, future, server); notificationHandler = new SctpNotificationHandler(channel); Selector selector; synchronized (startStopLock) { if (!started) { // Open a selector if this worker didn't start yet. try { this.selector = selector = Selector.open(); } catch (Throwable t) { throw new ChannelException("Failed to create a selector.", t); } // Start the worker thread with the new Selector. boolean success = false; try { DeadLockProofWorker.start(executor, this); success = true; } finally { if (!success) { // Release the Selector if the execution fails. try { selector.close(); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn("Failed to close a selector.", t); } } this.selector = selector = null; // The method will return to the caller at this point. } } } else { // Use the existing selector if this worker has been started. selector = this.selector; } assert selector != null && selector.isOpen(); started = true; boolean offered = registerTaskQueue.offer(registerTask); assert offered; } if (wakenUp.compareAndSet(false, true)) { selector.wakeup(); } }
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { logger.debug( String.format( "Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName())); if (frame instanceof CloseWebSocketFrame) { this.handshaker.performClosingHandshake(ctx, (CloseWebSocketFrame) frame); } else if (frame instanceof PingWebSocketFrame) { ctx.getChannel() .write( new PongWebSocketFrame( frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData())); } else if (frame instanceof TextWebSocketFrame) { // String text = ((TextWebSocketFrame) frame).getText(); ctx.getChannel() .write( new TextWebSocketFrame( frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData())); } else if (frame instanceof BinaryWebSocketFrame) { ctx.getChannel() .write( new BinaryWebSocketFrame( frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData())); } else if (frame instanceof ContinuationWebSocketFrame) { ctx.getChannel() .write( new ContinuationWebSocketFrame( frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData())); } else if (frame instanceof PongWebSocketFrame) { // Ignore } else { throw new UnsupportedOperationException( String.format("%s frame types not supported", frame.getClass().getName())); } }
/** * Handle the web socket handshake for the web socket specification <a href= * "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17">HyBi versions 13-17</a>. * Versions 13-17 share the same wire protocol. * * <p>Browser request to the server: * * <pre> * GET /chat HTTP/1.1 * Host: server.example.com * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== * Sec-WebSocket-Origin: http://example.com * Sec-WebSocket-Protocol: chat, superchat * Sec-WebSocket-Version: 13 * </pre> * * <p>Server response: * * <pre> * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= * Sec-WebSocket-Protocol: chat * </pre> * * @param channel Channel * @param req HTTP request */ @Override public ChannelFuture handshake(Channel channel, HttpRequest req) { if (logger.isDebugEnabled()) { logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.getId())); } HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID; byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); String accept = WebSocketUtil.base64(sha1); if (logger.isDebugEnabled()) { logger.debug( String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept)); } res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); res.addHeader(Names.CONNECTION, Names.UPGRADE); res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); } ChannelFuture future = channel.write(res); // Upgrade the connection and send the handshake response. ChannelPipeline p = channel.getPipeline(); if (p.get(HttpChunkAggregator.class) != null) { p.remove(HttpChunkAggregator.class); } p.replace( HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, allowExtensions, this.getMaxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); return future; }
public void run() { String scheme = uri.getScheme() == null ? "http" : uri.getScheme(); String host = uri.getHost() == null ? "localhost" : uri.getHost(); int port = uri.getPort(); if (port == -1) { if (scheme.equalsIgnoreCase("http")) { port = 80; } else if (scheme.equalsIgnoreCase("https")) { port = 443; } } if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) { logger.error("Only HTTP(S) is supported."); return; } boolean ssl = scheme.equalsIgnoreCase("https"); // Configure the client. ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool())); // Set up the event pipeline factory. bootstrap.setPipelineFactory(new HttpSnoopClientPipelineFactory(ssl)); // Start the connection attempt. ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { future.getCause().printStackTrace(); bootstrap.releaseExternalResources(); return; } // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath()); request.setHeader(HttpHeaders.Names.HOST, host); request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); // Set some example cookies. CookieEncoder httpCookieEncoder = new CookieEncoder(false); httpCookieEncoder.addCookie("my-cookie", "foo"); httpCookieEncoder.addCookie("another-cookie", "bar"); request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); // Send the HTTP request. channel.write(request); // Wait for the server to close the connection. channel.getCloseFuture().awaitUninterruptibly(); // Shut down executor threads to exit. bootstrap.releaseExternalResources(); }
void notifyHandlerException(Throwable cause) { if (!(cause instanceof ChannelPipelineException)) { cause = new ChannelPipelineException(cause); } if (inExceptionCaught(cause)) { if (logger.isWarnEnabled()) { logger.warn( "An exception was thrown by a user handler " + "while handling an exceptionCaught event", cause); } return; } fireExceptionCaught(cause); }
public static void main(String[] args) throws Exception { if (args.length != 1) { logger.error("Usage: " + HttpSnoopClient.class.getSimpleName() + " <URL>"); return; } URI uri = new URI(args[0]); new HttpSnoopClient(uri).run(); }
public NioSocketChannel(Channel parent, Integer id, SocketChannel socket) { // socket就是ch super(parent, id, socket); try { // 设置socketchannel非阻塞 socket.configureBlocking(false); } catch (IOException e) { try { socket.close(); } catch (IOException e2) { if (logger.isWarnEnabled()) { logger.warn("Failed to close a partially initialized socket.", e2); } } throw new ChannelException("Failed to enter non-blocking mode.", e); } config = new DefaultSocketChannelConfig(socket.socket()); }
@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // Make sure the handshake future is notified when a connection has // been closed during handshake. setHandshakeFailure(null); try { inboundBufferUpdated(ctx); } finally { engine.closeOutbound(); try { engine.closeInbound(); } catch (SSLException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to clean up SSLEngine.", ex); } } ctx.fireChannelInactive(); } }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (ignoreException(cause)) { // It is safe to ignore the 'connection reset by peer' or // 'broken pipe' error after sending closure_notify. if (logger.isDebugEnabled()) { logger.debug( "Swallowing a 'connection reset by peer / " + "broken pipe' error occurred while writing " + "'closure_notify'", cause); } // Close the connection explicitly just in case the transport // did not close the connection automatically. if (ctx.channel().isActive()) { ctx.close(); } return; } super.exceptionCaught(ctx, cause); }
@Override protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { if (localAddress != null) { socket.bind(localAddress); } boolean success = false; try { socket.connect(remoteAddress); success = true; } finally { if (!success) { try { socket.close(); } catch (Throwable t) { logger.warn("Failed to close a socket.", t); } } } }
@Override public void setBroadcast(boolean broadcast) { try { // See: https://github.com/netty/netty/issues/576 if (broadcast && !DetectionUtil.isWindows() && !DetectionUtil.isRoot() && !socket.getLocalAddress().isAnyLocalAddress()) { // Warn a user about the fact that a non-root user can't receive a // broadcast packet on *nix if the socket is bound on non-wildcard address. logger.warn( "A non-root user can't receive a broadcast packet if the socket " + "is not bound to a wildcard address; setting the SO_BROADCAST flag " + "anyway as requested on the socket which is bound to " + socket.getLocalSocketAddress() + '.'); } socket.setBroadcast(broadcast); } catch (SocketException e) { throw new ChannelException(e); } }
@Override public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { in.skipBytes(actualReadableBytes()); return null; } switch (state()) { case FRAME_START: framePayloadBytesRead = 0; framePayloadLength = -1; framePayload = null; // FIN, RSV, OPCODE byte b = in.readByte(); frameFinalFlag = (b & 0x80) != 0; frameRsv = (b & 0x70) >> 4; frameOpcode = b & 0x0F; if (logger.isDebugEnabled()) { logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); } // MASK, PAYLOAD LEN 1 b = in.readByte(); boolean frameMasked = (b & 0x80) != 0; int framePayloadLen1 = b & 0x7F; if (frameRsv != 0 && !allowExtensions) { protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); return null; } if (maskedPayload && !frameMasked) { protocolViolation(ctx, "unmasked client to server frame"); return null; } if (frameOpcode > 7) { // control frame (have MSB in opcode set) // control frames MUST NOT be fragmented if (!frameFinalFlag) { protocolViolation(ctx, "fragmented control frame"); return null; } // control frames MUST have payload 125 octets or less if (framePayloadLen1 > 125) { protocolViolation(ctx, "control frame with payload length > 125 octets"); return null; } // check for reserved control frame opcodes if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); return null; } // close frame : if there is a body, the first two bytes of the // body MUST be a 2-byte unsigned integer (in network byte // order) representing a getStatus code if (frameOpcode == 8 && framePayloadLen1 == 1) { protocolViolation(ctx, "received close control frame with payload len 1"); return null; } } else { // data frame // check for reserved data frame opcodes if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode); return null; } // check opcode vs message fragmentation state 1/2 if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { protocolViolation(ctx, "received continuation data frame outside fragmented message"); return null; } // check opcode vs message fragmentation state 2/2 if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { protocolViolation( ctx, "received non-continuation data frame while inside fragmented message"); return null; } } // Read frame payload length if (framePayloadLen1 == 126) { framePayloadLength = in.readUnsignedShort(); if (framePayloadLength < 126) { protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); return null; } } else if (framePayloadLen1 == 127) { framePayloadLength = in.readLong(); // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe // just check if it's negative? if (framePayloadLength < 65536) { protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); return null; } } else { framePayloadLength = framePayloadLen1; } if (framePayloadLength > maxFramePayloadLength) { protocolViolation( ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); return null; } if (logger.isDebugEnabled()) { logger.debug("Decoding WebSocket Frame length={}", framePayloadLength); } checkpoint(State.MASKING_KEY); case MASKING_KEY: if (maskedPayload) { maskingKey = in.readBytes(4); } checkpoint(State.PAYLOAD); case PAYLOAD: // Sometimes, the payload may not be delivered in 1 nice packet // We need to accumulate the data until we have it all int rbytes = actualReadableBytes(); ByteBuf payloadBuffer = null; long willHaveReadByteCount = framePayloadBytesRead + rbytes; // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" // + willHaveReadByteCount + " framePayloadLength=" + // framePayloadLength); if (willHaveReadByteCount == framePayloadLength) { // We have all our content so proceed to process payloadBuffer = ctx.alloc().buffer(rbytes); payloadBuffer.writeBytes(in, rbytes); } else if (willHaveReadByteCount < framePayloadLength) { // We don't have all our content so accumulate payload. // Returning null means we will get called back if (framePayload == null) { framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); } framePayload.writeBytes(in, rbytes); framePayloadBytesRead += rbytes; // Return null to wait for more bytes to arrive return null; } else if (willHaveReadByteCount > framePayloadLength) { // We have more than what we need so read up to the end of frame // Leave the remainder in the buffer for next frame if (framePayload == null) { framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); } framePayload.writeBytes(in, toFrameLength(framePayloadLength - framePayloadBytesRead)); } // Now we have all the data, the next checkpoint must be the next // frame checkpoint(State.FRAME_START); // Take the data that we have in this packet if (framePayload == null) { framePayload = payloadBuffer; } else if (payloadBuffer != null) { framePayload.writeBytes(payloadBuffer); } // Unmask data if needed if (maskedPayload) { unmask(framePayload); } // Processing ping/pong/close frames because they cannot be // fragmented if (frameOpcode == OPCODE_PING) { return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload); } if (frameOpcode == OPCODE_PONG) { return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload); } if (frameOpcode == OPCODE_CLOSE) { checkCloseFrameBody(ctx, framePayload); receivedClosingHandshake = true; return new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload); } // Processing for possible fragmented messages for text and binary // frames String aggregatedText = null; if (frameFinalFlag) { // Final frame of the sequence. Apparently ping frames are // allowed in the middle of a fragmented message if (frameOpcode != OPCODE_PING) { fragmentedFramesCount = 0; // Check text for UTF8 correctness if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { // Check UTF-8 correctness for this payload checkUTF8String(ctx, framePayload); // This does a second check to make sure UTF-8 // correctness for entire text message aggregatedText = fragmentedFramesText.toString(); fragmentedFramesText = null; } } } else { // Not final frame so we can expect more frames in the // fragmented sequence if (fragmentedFramesCount == 0) { // First text or binary frame for a fragmented set fragmentedFramesText = null; if (frameOpcode == OPCODE_TEXT) { checkUTF8String(ctx, framePayload); } } else { // Subsequent frames - only check if init frame is text if (fragmentedFramesText != null) { checkUTF8String(ctx, framePayload); } } // Increment counter fragmentedFramesCount++; } // Return the frame if (frameOpcode == OPCODE_TEXT) { return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload); } else if (frameOpcode == OPCODE_BINARY) { return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload); } else if (frameOpcode == OPCODE_CONT) { return new ContinuationWebSocketFrame( frameFinalFlag, frameRsv, framePayload, aggregatedText); } else { throw new UnsupportedOperationException( "Cannot decode web socket frame with opcode: " + frameOpcode); } case CORRUPT: // If we don't keep reading Netty will throw an exception saying // we can't return null if no bytes read and state not changed. in.readByte(); return null; default: throw new Error("Shouldn't reach here."); } }
@Override public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { logger.info(String.format("Received mesage: %s", msg)); }
/** * Handle the web socket handshake for the web socket specification <a href= * "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00">HyBi version 0</a> and * lower. This standard is really a rehash of <a * href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76" >hixie-76</a> and <a * href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75" >hixie-75</a>. * * <p>Browser request to the server: * * <pre> * GET /demo HTTP/1.1 * Upgrade: WebSocket * Connection: Upgrade * Host: example.com * Origin: http://example.com * Sec-WebSocket-Protocol: chat, sample * Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 * Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 * * ^n:ds[4U * </pre> * * <p>Server response: * * <pre> * HTTP/1.1 101 WebSocket Protocol Handshake * Upgrade: WebSocket * Connection: Upgrade * Sec-WebSocket-Origin: http://example.com * Sec-WebSocket-Location: ws://example.com/demo * Sec-WebSocket-Protocol: sample * * 8jKS'y:G*Co,Wxa- * </pre> * * @param channel Channel * @param req HTTP request */ @Override public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) { if (logger.isDebugEnabled()) { logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id())); } // Serve the WebSocket handshake request. if (!Values.UPGRADE.equalsIgnoreCase(req.headers().get(CONNECTION)) || !WEBSOCKET.equalsIgnoreCase(req.headers().get(Names.UPGRADE))) { throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); } // Hixie 75 does not contain these headers while Hixie 76 does boolean isHixie76 = req.headers().contains(SEC_WEBSOCKET_KEY1) && req.headers().contains(SEC_WEBSOCKET_KEY2); // Create the WebSocket handshake response. FullHttpResponse res = new DefaultFullHttpResponse( HTTP_1_1, new HttpResponseStatus( 101, isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")); res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(CONNECTION, Values.UPGRADE); // Fill in the headers and contents depending on handshake getMethod. if (isHixie76) { // New handshake getMethod with a challenge: res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(SEC_WEBSOCKET_LOCATION, uri()); String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { throw new WebSocketHandshakeException( "Requested subprotocol(s) not supported: " + subprotocols); } else { res.headers().add(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol); } } // Calculate the answer of the challenge. String key1 = req.headers().get(SEC_WEBSOCKET_KEY1); String key2 = req.headers().get(SEC_WEBSOCKET_KEY2); int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / BEGINNING_SPACE.matcher(key1).replaceAll("").length()); int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / BEGINNING_SPACE.matcher(key2).replaceAll("").length()); long c = req.data().readLong(); ByteBuf input = Unpooled.buffer(16); input.writeInt(a); input.writeInt(b); input.writeLong(c); res.data().writeBytes(WebSocketUtil.md5(input.array())); } else { // Old Hixie 75 handshake getMethod with no challenge: res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(WEBSOCKET_LOCATION, uri()); String protocol = req.headers().get(WEBSOCKET_PROTOCOL); if (protocol != null) { res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); } } // Upgrade the connection and send the handshake response. channel.write(res, promise); promise.addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { ChannelPipeline p = future.channel().pipeline(); if (p.get(HttpObjectAggregator.class) != null) { p.remove(HttpObjectAggregator.class); } p.replaceAndForward( HttpRequestDecoder.class, "wsdecoder", new WebSocket00FrameDecoder(maxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder()); } }); return promise; }
private boolean read(SelectionKey k) { final SctpChannelImpl channel = (SctpChannelImpl) k.attachment(); final ReceiveBufferSizePredictor predictor = channel.getConfig().getReceiveBufferSizePredictor(); final int predictedRecvBufSize = predictor.nextReceiveBufferSize(); boolean messageReceived = false; boolean failure = true; MessageInfo messageInfo = null; ByteBuffer bb = recvBufferPool.acquire(predictedRecvBufSize); try { messageInfo = channel.channel.receive(bb, null, notificationHandler); if (messageInfo != null) { messageReceived = true; if (!messageInfo.isUnordered()) { failure = false; } else { if (logger.isErrorEnabled()) { logger.error("Received unordered SCTP Packet"); } failure = true; } } else { messageReceived = false; failure = false; } } catch (ClosedChannelException e) { // Can happen, and does not need a user attention. } catch (Throwable t) { fireExceptionCaught(channel, t); } if (messageReceived) { bb.flip(); final ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory(); final int receivedBytes = bb.remaining(); final ChannelBuffer buffer = bufferFactory.getBuffer(receivedBytes); buffer.setBytes(0, bb); buffer.writerIndex(receivedBytes); recvBufferPool.release(bb); // Update the predictor. predictor.previousReceiveBufferSize(receivedBytes); // Fire the event. fireMessageReceived(channel, new SctpFrame(messageInfo, buffer), messageInfo.address()); } else { recvBufferPool.release(bb); } if (channel.channel.isBlocking() && !messageReceived || failure) { k.cancel(); // Some JDK implementations run into an infinite loop without this. close(channel, succeededFuture(channel)); return false; } return true; }
@Override public void run() { thread = Thread.currentThread(); boolean shutdown = false; Selector selector = this.selector; for (; ; ) { wakenUp.set(false); if (CONSTRAINT_LEVEL != 0) { selectorGuard.writeLock().lock(); // This empty synchronization block prevents the selector // from acquiring its lock. selectorGuard.writeLock().unlock(); } try { SelectorUtil.select(selector); // 'wakenUp.compareAndSet(false, true)' is always evaluated // before calling 'selector.wakeup()' to reduce the wake-up // overhead. (Selector.wakeup() is an expensive operation.) // // However, there is a race condition in this approach. // The race condition is triggered when 'wakenUp' is set to // true too early. // // 'wakenUp' is set to true too early if: // 1) Selector is waken up between 'wakenUp.set(false)' and // 'selector.select(...)'. (BAD) // 2) Selector is waken up between 'selector.select(...)' and // 'if (wakenUp.get()) { ... }'. (OK) // // In the first case, 'wakenUp' is set to true and the // following 'selector.select(...)' will wake up immediately. // Until 'wakenUp' is set to false again in the next round, // 'wakenUp.compareAndSet(false, true)' will fail, and therefore // any attempt to wake up the Selector will fail, too, causing // the following 'selector.select(...)' call to block // unnecessarily. // // To fix this problem, we wake up the selector again if wakenUp // is true immediately after selector.select(...). // It is inefficient in that it wakes up the selector for both // the first case (BAD - wake-up required) and the second case // (OK - no wake-up required). if (wakenUp.get()) { selector.wakeup(); } cancelledKeys = 0; processRegisterTaskQueue(); processEventQueue(); processWriteTaskQueue(); processSelectedKeys(selector.selectedKeys()); // Exit the loop when there's nothing to handle. // The shutdown flag is used to delay the shutdown of this // loop to avoid excessive Selector creation when // connections are registered in a one-by-one manner instead of // concurrent manner. if (selector.keys().isEmpty()) { if (shutdown || executor instanceof ExecutorService && ((ExecutorService) executor).isShutdown()) { synchronized (startStopLock) { if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) { started = false; try { selector.close(); } catch (IOException e) { if (logger.isWarnEnabled()) { logger.warn("Failed to close a selector.", e); } } finally { this.selector = null; } break; } else { shutdown = false; } } } else { // Give one more second. shutdown = true; } } else { shutdown = false; } } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn("Unexpected exception in the selector loop.", t); } // Prevent possible consecutive immediate failures that lead to // excessive CPU consumption. try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore. } } } }