private void encodeFrame(WsURLConnectionImpl connection, Frame frame) throws IOException {
    OutputStream out = connection.getTcpOutputStream();
    int offset = frame.offset();
    ByteBuffer buf = frame.buffer();
    int payloadLength = frame.payloadLength();
    int payloadOffset = frame.payloadOffset();
    int mask = 0;

    if (payloadLength > 0) {
      mask = 1 + connection.getRandom().nextInt(Integer.MAX_VALUE);
    }

    int metadataLength = payloadOffset - offset;

    switch (metadataLength) {
      case 2:
        frameBuffer.putShort(buf.getShort(offset));
        break;
      case 4:
        frameBuffer.putInt(buf.getInt(offset));
        break;
      case 10:
        frameBuffer.putShort(buf.getShort(offset));
        frameBuffer.putLong(buf.getLong(offset + 2));
        break;
      default:
        throw new IllegalStateException(format(MSG_INVALID_METADATA_LENGTH, metadataLength));
    }

    // Set the mask bit.
    byte maskByte = frameBuffer.get(1);
    frameBuffer.put(1, (byte) (maskByte | 0x80));

    encodeMaskAndPayload(buf, payloadOffset, payloadLength, mask);

    out.write(frameBuffer.array(), 0, frameBuffer.position());
    frameBuffer.rewind();
  }
  private void validateAndEncodeCloseFrame(WsURLConnectionImpl connection, Frame frame)
      throws IOException {
    OutputStream out = connection.getTcpOutputStream();
    int len = 0;
    int code = 0;
    int closeCode = 0;
    ByteBuffer buffer = frame.buffer();
    int closePayloadLen = frame.payloadLength();
    int payloadOffset = frame.payloadOffset();
    IOException exception = null;

    closePayloadRO.wrap(buffer, payloadOffset, payloadOffset + closePayloadLen);

    if (closePayloadLen >= 2) {
      closeCode = closePayloadRO.statusCode();
    }

    if (closeCode != 0) {
      switch (closeCode) {
        case WS_MISSING_STATUS_CODE:
        case WS_ABNORMAL_CLOSE:
        case WS_UNSUCCESSFUL_TLS_HANDSHAKE:
          len += 2;
          code = WS_PROTOCOL_ERROR;
          exception =
              new IOException(format(MSG_CLOSE_FRAME_VIOLATION, closeCode, closePayloadLen));
          break;
        case WS_NORMAL_CLOSE:
        case WS_ENDPOINT_GOING_AWAY:
        case WS_PROTOCOL_ERROR:
        case WS_INCORRECT_MESSAGE_TYPE:
        case WS_INCONSISTENT_DATA_MESSAGE_TYPE:
        case WS_VIOLATE_POLICY:
        case WS_MESSAGE_TOO_BIG:
        case WS_UNSUCCESSFUL_EXTENSION_NEGOTIATION:
        case WS_SERVER_TERMINATED_CONNECTION:
          len += 2;
          code = closeCode;
          break;
        default:
          if ((closeCode >= 3000) && (closeCode <= 4999)) {
            len += 2;
            code = closeCode;
          }

          throw new IOException(format("Invalid CLOSE code %d", closeCode));
      }

      if ((code != WS_PROTOCOL_ERROR) && (closePayloadLen > 2)) {
        int reasonLength = closePayloadLen - 2;

        assert reasonLength == closePayloadRO.reasonLength();

        if (reasonLength > 0) {
          if (!validBytesUTF8(buffer, closePayloadRO.reasonOffset(), reasonLength)) {
            code = WS_PROTOCOL_ERROR;
            exception = new IOException(format(MSG_CLOSE_FRAME_VIOLATION, closeCode, reasonLength));
          } else if (reasonLength > 123) {
            code = WS_PROTOCOL_ERROR;
            exception = new IOException(format(MSG_CLOSE_FRAME_VIOLATION, closeCode, reasonLength));
            len += reasonLength;
          } else {
            if (code != WS_NORMAL_CLOSE) {
              reasonLength = 0;
            }

            len += reasonLength;
          }
        }
      }
    }

    frameBuffer.put((byte) 0x88);
    encodePayloadLength(len);

    if (len == 0) {
      frameBuffer.putInt(0);
    } else {
      assert len >= 2;

      int mask = 1 + connection.getRandom().nextInt(Integer.MAX_VALUE);
      int reasonOffset = closePayloadRO.reasonOffset();
      int roffset = reasonOffset;

      closePayload.putShort(0, (short) code);
      if (len > 2) {
        for (int i = 2; i < len; i++) {
          closePayload.put(i, frame.buffer().get(roffset++));
        }
      }

      encodeMaskAndPayload(closePayload, 0, len, mask);
    }

    out.write(frameBuffer.array(), 0, frameBuffer.position());
    frameBuffer.rewind();

    out.flush();
    out.close();

    if (exception != null) {
      throw exception;
    }
  }