/**
  * crates a websocket with client role
  *
  * @param socket may be unbound
  */
 public WebSocketImpl(WebSocketListener listener, Draft draft) {
   if (listener == null
       || (draft == null
           && role
               == Role
                   .SERVER)) // socket can be null because we want do be able to create the object
                             // without already having a bound channel
   throw new IllegalArgumentException("parameters must not be null");
   this.outQueue = new LinkedBlockingQueue<ByteBuffer>();
   inQueue = new LinkedBlockingQueue<ByteBuffer>();
   this.wsl = listener;
   this.role = Role.CLIENT;
   if (draft != null) this.draft = draft.copyInstance();
 }
  /**
   * Returns whether the handshake phase has is completed. In case of a broken handshake this will
   * be never the case.
   */
  private boolean decodeHandshake(ByteBuffer socketBufferNew) {
    ByteBuffer socketBuffer;
    if (tmpHandshakeBytes.capacity() == 0) {
      socketBuffer = socketBufferNew;
    } else {
      if (tmpHandshakeBytes.remaining() < socketBufferNew.remaining()) {
        ByteBuffer buf =
            ByteBuffer.allocate(tmpHandshakeBytes.capacity() + socketBufferNew.remaining());
        tmpHandshakeBytes.flip();
        buf.put(tmpHandshakeBytes);
        tmpHandshakeBytes = buf;
      }

      tmpHandshakeBytes.put(socketBufferNew);
      tmpHandshakeBytes.flip();
      socketBuffer = tmpHandshakeBytes;
    }
    socketBuffer.mark();
    try {
      if (draft == null) {
        HandshakeState isflashedgecase = isFlashEdgeCase(socketBuffer);
        if (isflashedgecase == HandshakeState.MATCHED) {
          try {
            write(ByteBuffer.wrap(Charsetfunctions.utf8Bytes(wsl.getFlashPolicy(this))));
            close(CloseFrame.FLASHPOLICY, "");
          } catch (InvalidDataException e) {
            close(
                CloseFrame.ABNORMAL_CLOSE,
                "remote peer closed connection before flashpolicy could be transmitted",
                true);
          }
          return false;
        }
      }
      HandshakeState handshakestate = null;

      try {
        if (role == Role.SERVER) {
          if (draft == null) {
            for (Draft d : knownDrafts) {
              d = d.copyInstance();
              try {
                d.setParseMode(role);
                socketBuffer.reset();
                Handshakedata tmphandshake = d.translateHandshake(socketBuffer);
                if (tmphandshake instanceof ClientHandshake == false) {
                  flushAndClose(CloseFrame.PROTOCOL_ERROR, "wrong http function", false);
                  return false;
                }
                ClientHandshake handshake = (ClientHandshake) tmphandshake;
                handshakestate = d.acceptHandshakeAsServer(handshake);
                if (handshakestate == HandshakeState.MATCHED) {
                  resourceDescriptor = handshake.getResourceDescriptor();
                  ServerHandshakeBuilder response;
                  try {
                    response = wsl.onWebsocketHandshakeReceivedAsServer(this, d, handshake);
                  } catch (InvalidDataException e) {
                    flushAndClose(e.getCloseCode(), e.getMessage(), false);
                    return false;
                  } catch (RuntimeException e) {
                    wsl.onWebsocketError(this, e);
                    flushAndClose(CloseFrame.NEVER_CONNECTED, e.getMessage(), false);
                    return false;
                  }
                  write(
                      d.createHandshake(
                          d.postProcessHandshakeResponseAsServer(handshake, response), role));
                  draft = d;
                  open(handshake);
                  return true;
                }
              } catch (InvalidHandshakeException e) {
                // go on with an other draft
              }
            }
            if (draft == null) {
              close(CloseFrame.PROTOCOL_ERROR, "no draft matches");
            }
            return false;
          } else {
            // special case for multiple step handshakes
            Handshakedata tmphandshake = draft.translateHandshake(socketBuffer);
            if (tmphandshake instanceof ClientHandshake == false) {
              flushAndClose(CloseFrame.PROTOCOL_ERROR, "wrong http function", false);
              return false;
            }
            ClientHandshake handshake = (ClientHandshake) tmphandshake;
            handshakestate = draft.acceptHandshakeAsServer(handshake);

            if (handshakestate == HandshakeState.MATCHED) {
              open(handshake);
              return true;
            } else {
              close(CloseFrame.PROTOCOL_ERROR, "the handshake did finaly not match");
            }
            return false;
          }
        } else if (role == Role.CLIENT) {
          draft.setParseMode(role);
          Handshakedata tmphandshake = draft.translateHandshake(socketBuffer);
          if (tmphandshake instanceof ServerHandshake == false) {
            flushAndClose(CloseFrame.PROTOCOL_ERROR, "wrong http function", false);
            return false;
          }
          ServerHandshake handshake = (ServerHandshake) tmphandshake;
          handshakestate = draft.acceptHandshakeAsClient(handshakerequest, handshake);
          if (handshakestate == HandshakeState.MATCHED) {
            try {
              wsl.onWebsocketHandshakeReceivedAsClient(this, handshakerequest, handshake);
            } catch (InvalidDataException e) {
              flushAndClose(e.getCloseCode(), e.getMessage(), false);
              return false;
            } catch (RuntimeException e) {
              wsl.onWebsocketError(this, e);
              flushAndClose(CloseFrame.NEVER_CONNECTED, e.getMessage(), false);
              return false;
            }
            open(handshake);
            return true;
          } else {
            close(CloseFrame.PROTOCOL_ERROR, "draft " + draft + " refuses handshake");
          }
        }
      } catch (InvalidHandshakeException e) {
        close(e);
      }
    } catch (IncompleteHandshakeException e) {
      if (tmpHandshakeBytes.capacity() == 0) {
        socketBuffer.reset();
        int newsize = e.getPreferedSize();
        if (newsize == 0) {
          newsize = socketBuffer.capacity() + 16;
        } else {
          assert (e.getPreferedSize() >= socketBuffer.remaining());
        }
        tmpHandshakeBytes = ByteBuffer.allocate(newsize);

        tmpHandshakeBytes.put(socketBufferNew);
        // tmpHandshakeBytes.flip();
      } else {
        tmpHandshakeBytes.position(tmpHandshakeBytes.limit());
        tmpHandshakeBytes.limit(tmpHandshakeBytes.capacity());
      }
    }
    return false;
  }