Example #1
0
  public List<ByteBuffer> createHandshake(
      Handshakedata handshakedata, Role ownrole, boolean withcontent) {
    StringBuilder bui = new StringBuilder(100);
    if (handshakedata instanceof ClientHandshake) {
      bui.append("GET ");
      bui.append(((ClientHandshake) handshakedata).getResourceDescriptor());
      bui.append(" HTTP/1.1");
    } else if (handshakedata instanceof ServerHandshake) {
      bui.append("HTTP/1.1 101 " + ((ServerHandshake) handshakedata).getHttpStatusMessage());
    } else {
      throw new RuntimeException("unknow role");
    }
    bui.append("\r\n");
    Iterator<String> it = handshakedata.iterateHttpFields();
    while (it.hasNext()) {
      String fieldname = it.next();
      String fieldvalue = handshakedata.getFieldValue(fieldname);
      bui.append(fieldname);
      bui.append(": ");
      bui.append(fieldvalue);
      bui.append("\r\n");
    }
    bui.append("\r\n");
    byte[] httpheader = Charsetfunctions.asciiBytes(bui.toString());

    byte[] content = withcontent ? handshakedata.getContent() : null;
    ByteBuffer bytebuffer =
        ByteBuffer.allocate((content == null ? 0 : content.length) + httpheader.length);
    bytebuffer.put(httpheader);
    if (content != null) bytebuffer.put(content);
    bytebuffer.flip();
    return Collections.singletonList(bytebuffer);
  }
Example #2
0
 @Override
 public String toString() {
   return "Framedata{ optcode:"
       + getOpcode()
       + ", fin:"
       + isFin()
       + ", payloadlength:"
       + unmaskedpayload.limit()
       + ", payload:"
       + Arrays.toString(Charsetfunctions.utf8Bytes(new String(unmaskedpayload.array())))
       + "}";
 }
Example #3
0
 @Override
 public List<Framedata> createFrames(String text, boolean mask) {
   FrameBuilder curframe = new FramedataImpl1();
   try {
     curframe.setPayload(ByteBuffer.wrap(Charsetfunctions.utf8Bytes(text)));
   } catch (InvalidDataException e) {
     throw new NotSendableException(e);
   }
   curframe.setFin(true);
   curframe.setOptcode(Opcode.TEXT);
   curframe.setTransferemasked(mask);
   return Collections.singletonList((Framedata) curframe);
 }
  private void decodeFrames(ByteBuffer socketBuffer) {
    if (flushandclosestate) return;

    List<Framedata> frames;
    try {
      frames = draft.translateFrame(socketBuffer);
      for (Framedata f : frames) {
        if (DEBUG) System.out.println("matched frame: " + f);
        if (flushandclosestate) return;
        Opcode curop = f.getOpcode();
        boolean fin = f.isFin();

        if (curop == Opcode.CLOSING) {
          int code = CloseFrame.NOCODE;
          String reason = "";
          if (f instanceof CloseFrame) {
            CloseFrame cf = (CloseFrame) f;
            code = cf.getCloseCode();
            reason = cf.getMessage();
          }
          if (readystate == READYSTATE.CLOSING) {
            // complete the close handshake by disconnecting
            closeConnection(code, reason, true);
          } else {
            // echo close handshake
            if (draft.getCloseHandshakeType() == CloseHandshakeType.TWOWAY)
              close(code, reason, true);
            else flushAndClose(code, reason, false);
          }
          continue;
        } else if (curop == Opcode.PING) {
          wsl.onWebsocketPing(this, f);
          continue;
        } else if (curop == Opcode.PONG) {
          wsl.onWebsocketPong(this, f);
          continue;
        } else if (!fin || curop == Opcode.CONTINUOUS) {
          if (curop != Opcode.CONTINUOUS) {
            if (current_continuous_frame_opcode != null)
              throw new InvalidDataException(
                  CloseFrame.PROTOCOL_ERROR, "Previous continuous frame sequence not completed.");
            current_continuous_frame_opcode = curop;
          } else if (fin) {
            if (current_continuous_frame_opcode == null)
              throw new InvalidDataException(
                  CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence was not started.");
            current_continuous_frame_opcode = null;
          } else if (current_continuous_frame_opcode == null) {
            throw new InvalidDataException(
                CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence was not started.");
          }
          try {
            wsl.onWebsocketMessageFragment(this, f);
          } catch (RuntimeException e) {
            wsl.onWebsocketError(this, e);
          }

        } else if (current_continuous_frame_opcode != null) {
          throw new InvalidDataException(
              CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence not completed.");
        } else if (curop == Opcode.TEXT) {
          try {
            wsl.onWebsocketMessage(this, Charsetfunctions.stringUtf8(f.getPayloadData()));
          } catch (RuntimeException e) {
            wsl.onWebsocketError(this, e);
          }
        } else if (curop == Opcode.BINARY) {
          try {
            wsl.onWebsocketMessage(this, f.getPayloadData());
          } catch (RuntimeException e) {
            wsl.onWebsocketError(this, e);
          }
        } else {
          throw new InvalidDataException(
              CloseFrame.PROTOCOL_ERROR, "non control or continious frame expected");
        }
      }
    } catch (InvalidDataException e1) {
      wsl.onWebsocketError(this, e1);
      close(e1);
      return;
    }
  }
  /**
   * 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 == null) {
      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) {
          write(ByteBuffer.wrap(Charsetfunctions.utf8Bytes(wsl.getFlashPolicy(this))));
          close(CloseFrame.FLASHPOLICY, "");
          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) {
                  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, "Wwrong 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 == null) {
        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;
  }
Example #6
0
 public static String readStringLine(ByteBuffer buf) {
   ByteBuffer b = readLine(buf);
   return b == null ? null : Charsetfunctions.stringAscii(b.array(), 0, b.limit());
 }
Example #7
0
/**
 * Base class for everything of a websocket specification which is not common such as the way the
 * handshake is read or frames are transfered.
 */
public abstract class Draft {

  public enum HandshakeState {
    /** Handshake matched this Draft successfully */
    MATCHED,
    /** Handshake is does not match this Draft */
    NOT_MATCHED
  }

  public enum CloseHandshakeType {
    NONE,
    ONEWAY,
    TWOWAY
  }

  public static int MAX_FAME_SIZE = 1000 * 1;
  public static int INITIAL_FAMESIZE = 64;

  public static final byte[] FLASH_POLICY_REQUEST =
      Charsetfunctions.utf8Bytes("<policy-file-request/>\0");

  /** In some cases the handshake will be parsed different depending on whether */
  protected Role role = null;

  public static ByteBuffer readLine(ByteBuffer buf) {
    ByteBuffer sbuf = ByteBuffer.allocate(buf.remaining());
    byte prev = '0';
    byte cur = '0';

    while (buf.hasRemaining()) {
      prev = cur;
      boolean p = false;
      if (buf.position() >= 159) p = true;
      cur = buf.get();
      sbuf.put(cur);

      if (prev == (byte) '\r' && cur == (byte) '\n') {
        sbuf.limit(sbuf.position() - 2);
        sbuf.position(0);
        return sbuf;
      }
    }

    // ensure that there wont be any bytes skipped
    buf.position(buf.position() - sbuf.position());
    return null;
  }

  public static String readStringLine(ByteBuffer buf) {
    ByteBuffer b = readLine(buf);
    return b == null ? null : Charsetfunctions.stringAscii(b.array(), 0, b.limit());
  }

  public static HandshakeBuilder translateHandshakeHttp(ByteBuffer buf, Role role)
      throws InvalidHandshakeException, IncompleteHandshakeException {
    HandshakeBuilder handshake;

    String line = readStringLine(buf);
    if (line == null) throw new IncompleteHandshakeException(buf.capacity() + 128);

    String[] firstLineTokens = line.split(" ", 3); // eg. HTTP/1.1 101 Switching the Protocols
    if (firstLineTokens.length != 3) {
      throw new InvalidHandshakeException();
    }

    if (role == Role.CLIENT) {
      // translating/parsing the response from the SERVER
      handshake = new HandshakeImpl1Server();
      ServerHandshakeBuilder serverhandshake = (ServerHandshakeBuilder) handshake;
      serverhandshake.setHttpStatus(Short.parseShort(firstLineTokens[1]));
      serverhandshake.setHttpStatusMessage(firstLineTokens[2]);
    } else {
      // translating/parsing the request from the CLIENT
      ClientHandshakeBuilder clienthandshake = new HandshakeImpl1Client();
      clienthandshake.setResourceDescriptor(firstLineTokens[1]);
      handshake = clienthandshake;
    }

    line = readStringLine(buf);
    while (line != null && line.length() > 0) {
      String[] pair = line.split(":", 2);
      if (pair.length != 2) throw new InvalidHandshakeException("not an http header");
      handshake.put(pair[0], pair[1].replaceFirst("^ +", ""));
      line = readStringLine(buf);
    }
    if (line == null) throw new IncompleteHandshakeException();
    return handshake;
  }

  public abstract HandshakeState acceptHandshakeAsClient(
      ClientHandshake request, ServerHandshake response) throws InvalidHandshakeException;

  public abstract HandshakeState acceptHandshakeAsServer(ClientHandshake handshakedata)
      throws InvalidHandshakeException;

  protected boolean basicAccept(Handshakedata handshakedata) {
    return handshakedata.getFieldValue("Upgrade").equalsIgnoreCase("websocket")
        && handshakedata
            .getFieldValue("Connection")
            .toLowerCase(Locale.ENGLISH)
            .contains("upgrade");
  }

  public abstract ByteBuffer createBinaryFrame(
      Framedata framedata); // TODO Allow to send data on the base of an Iterator or InputStream

  public abstract List<Framedata> createFrames(ByteBuffer binary, boolean mask);

  public abstract List<Framedata> createFrames(String text, boolean mask);

  public abstract void reset();

  public List<ByteBuffer> createHandshake(Handshakedata handshakedata, Role ownrole) {
    return createHandshake(handshakedata, ownrole, true);
  }

  public List<ByteBuffer> createHandshake(
      Handshakedata handshakedata, Role ownrole, boolean withcontent) {
    StringBuilder bui = new StringBuilder(100);
    if (handshakedata instanceof ClientHandshake) {
      bui.append("GET ");
      bui.append(((ClientHandshake) handshakedata).getResourceDescriptor());
      bui.append(" HTTP/1.1");
    } else if (handshakedata instanceof ServerHandshake) {
      bui.append("HTTP/1.1 101 " + ((ServerHandshake) handshakedata).getHttpStatusMessage());
    } else {
      throw new RuntimeException("unknow role");
    }
    bui.append("\r\n");
    Iterator<String> it = handshakedata.iterateHttpFields();
    while (it.hasNext()) {
      String fieldname = it.next();
      String fieldvalue = handshakedata.getFieldValue(fieldname);
      bui.append(fieldname);
      bui.append(": ");
      bui.append(fieldvalue);
      bui.append("\r\n");
    }
    bui.append("\r\n");
    byte[] httpheader = Charsetfunctions.asciiBytes(bui.toString());

    byte[] content = withcontent ? handshakedata.getContent() : null;
    ByteBuffer bytebuffer =
        ByteBuffer.allocate((content == null ? 0 : content.length) + httpheader.length);
    bytebuffer.put(httpheader);
    if (content != null) bytebuffer.put(content);
    bytebuffer.flip();
    return Collections.singletonList(bytebuffer);
  }

  public abstract ClientHandshakeBuilder postProcessHandshakeRequestAsClient(
      ClientHandshakeBuilder request) throws InvalidHandshakeException;

  public abstract HandshakeBuilder postProcessHandshakeResponseAsServer(
      ClientHandshake request, ServerHandshakeBuilder response) throws InvalidHandshakeException;

  public abstract List<Framedata> translateFrame(ByteBuffer buffer) throws InvalidDataException;

  public abstract CloseHandshakeType getCloseHandshakeType();

  public Handshakedata translateHandshake(ByteBuffer buf) throws InvalidHandshakeException {
    return translateHandshakeHttp(buf, role);
  }

  public int checkAlloc(int bytecount) throws LimitExedeedException, InvalidDataException {
    if (bytecount < 0) throw new InvalidDataException(CloseFrame.PROTOCOL_ERROR, "Negative count");
    return bytecount;
  }

  public void setParseMode(Role role) {
    this.role = role;
  }
}