Пример #1
0
  /** 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();
 }
Пример #5
0
  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();
    }
  }
Пример #6
0
  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;
  }
Пример #8
0
  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);
  }
Пример #10
0
  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());
  }
Пример #12
0
  @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();
    }
  }
Пример #13
0
  @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);
  }
Пример #14
0
  @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.");
    }
  }
Пример #17
0
 @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;
  }
Пример #19
0
  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;
  }
Пример #20
0
  @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.
        }
      }
    }
  }