/**
   * 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;
  }
  private static boolean isRequestTo(HttpRequest request, String uri) {
    URI decodedUri;
    try {
      decodedUri = new URI(request.getUri());
    } catch (URISyntaxException e) {
      return false;
    }

    return HttpVersion.HTTP_1_1.equals(request.getProtocolVersion())
        && USER_AGENT.equals(request.getHeader(HttpHeaders.Names.USER_AGENT))
        && HttpMethod.POST.equals(request.getMethod())
        && uri.equals(decodedUri.getPath());
  }
 private String getWebSocketLocation(HttpRequest req) {
   return "ws://" + req.getHeader(HttpHeaders.Names.HOST);
 }
 public static boolean checkHost(HttpRequest request, SocketAddress expectedHost) {
   String host = request.getHeader(HttpHeaders.Names.HOST);
   return expectedHost == null
       ? host == null
       : HttpTunnelMessageUtils.convertToHostString(expectedHost).equals(host);
 }
 public static String extractTunnelId(HttpRequest request) {
   return request.getHeader(HttpHeaders.Names.COOKIE);
 }