Exemple #1
0
  private byte next(ByteBuffer buffer) {
    byte ch = buffer.get();

    CharState s = __charState[0xff & ch];
    switch (s) {
      case ILLEGAL:
        throw new IllegalCharacterException(_state, ch, buffer);

      case LF:
        _cr = false;
        break;

      case CR:
        if (_cr) throw new BadMessageException("Bad EOL");

        _cr = true;
        if (buffer.hasRemaining()) {
          if (_maxHeaderBytes > 0 && _state.ordinal() < State.END.ordinal()) _headerBytes++;
          return next(buffer);
        }

        // Can return 0 here to indicate the need for more characters,
        // because a real 0 in the buffer would cause a BadMessage below
        return 0;

      case LEGAL:
        if (_cr) throw new BadMessageException("Bad EOL");
    }

    return ch;
  }
Exemple #2
0
  /* ------------------------------------------------------------------------------- */
  private byte next(ByteBuffer buffer) {
    byte ch = buffer.get();

    if (_cr) {
      if (ch != HttpTokens.LINE_FEED) throw new BadMessage("Bad EOL");
      _cr = false;
      return ch;
    }

    if (ch >= 0 && ch < HttpTokens.SPACE) {
      if (ch == HttpTokens.CARRIAGE_RETURN) {
        if (buffer.hasRemaining()) {
          if (_maxHeaderBytes > 0 && _state.ordinal() < State.END.ordinal()) _headerBytes++;
          ch = buffer.get();
          if (ch != HttpTokens.LINE_FEED) throw new BadMessage("Bad EOL");
        } else {
          _cr = true;
          // Can return 0 here to indicate the need for more characters,
          // because a real 0 in the buffer would cause a BadMessage below
          return 0;
        }
      }
      // Only LF or TAB acceptable special characters
      else if (!(ch == HttpTokens.LINE_FEED || ch == HttpTokens.TAB))
        throw new BadMessage("Illegal character");
    }

    return ch;
  }
Exemple #3
0
 /* ------------------------------------------------------------------------------- */
 public boolean inContentState() {
   return _state.ordinal() >= State.CONTENT.ordinal() && _state.ordinal() < State.END.ordinal();
 }
Exemple #4
0
  protected boolean parseContent(ByteBuffer buffer) {
    // Handle _content
    byte ch;
    while (_state.ordinal() < State.END.ordinal() && buffer.hasRemaining()) {
      switch (_state) {
        case EOF_CONTENT:
          _contentChunk = buffer.asReadOnlyBuffer();
          _contentPosition += _contentChunk.remaining();
          buffer.position(buffer.position() + _contentChunk.remaining());
          if (_handler.content(_contentChunk)) return true;
          break;

        case CONTENT:
          {
            long remaining = _contentLength - _contentPosition;
            if (remaining == 0) {
              setState(State.END);
              if (_handler.messageComplete()) return true;
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              // limit content by expected size
              if (_contentChunk.remaining() > remaining) {
                // We can cast remaining to an int as we know that it is smaller than
                // or equal to length which is already an int.
                _contentChunk.limit(_contentChunk.position() + (int) remaining);
              }

              _contentPosition += _contentChunk.remaining();
              buffer.position(buffer.position() + _contentChunk.remaining());

              boolean handle = _handler.content(_contentChunk);
              if (_contentPosition == _contentLength) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              }
              if (handle) return true;
            }
            break;
          }

        case CHUNKED_CONTENT:
          {
            ch = next(buffer);
            if (ch > HttpTokens.SPACE) {
              _chunkLength = TypeUtil.convertHexDigit(ch);
              _chunkPosition = 0;
              setState(State.CHUNK_SIZE);
            }

            break;
          }

        case CHUNK_SIZE:
          {
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              } else setState(State.CHUNK);
            } else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
              setState(State.CHUNK_PARAMS);
            else _chunkLength = _chunkLength * 16 + TypeUtil.convertHexDigit(ch);
            break;
          }

        case CHUNK_PARAMS:
          {
            ch = next(buffer);
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              } else setState(State.CHUNK);
            }
            break;
          }

        case CHUNK:
          {
            int remaining = _chunkLength - _chunkPosition;
            if (remaining == 0) {
              setState(State.CHUNKED_CONTENT);
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              if (_contentChunk.remaining() > remaining)
                _contentChunk.limit(_contentChunk.position() + remaining);
              remaining = _contentChunk.remaining();

              _contentPosition += remaining;
              _chunkPosition += remaining;
              buffer.position(buffer.position() + remaining);
              if (_handler.content(_contentChunk)) return true;
            }
            break;
          }

        case CLOSED:
          {
            BufferUtil.clear(buffer);
            return false;
          }

        default:
          break;
      }
    }
    return false;
  }
Exemple #5
0
  /**
   * Parse until next Event.
   *
   * @return True if an {@link RequestHandler} method was called and it returned true;
   */
  public boolean parseNext(ByteBuffer buffer) {
    if (DEBUG) LOG.debug("parseNext s={} {}", _state, BufferUtil.toDetailString(buffer));
    try {
      boolean handle = false;

      // Start a request/response
      if (_state == State.START) {
        _version = null;
        _method = null;
        _methodString = null;
        _endOfContent = EndOfContent.UNKNOWN_CONTENT;
        _header = null;
        handle = quickStart(buffer);
      }

      // Request/response line
      if (!handle
          && _state.ordinal() >= State.START.ordinal()
          && _state.ordinal() < State.HEADER.ordinal()) handle = parseLine(buffer);

      // parse headers
      if (!handle
          && _state.ordinal() >= State.HEADER.ordinal()
          && _state.ordinal() < State.CONTENT.ordinal()) handle = parseHeaders(buffer);

      // parse content
      if (!handle
          && _state.ordinal() >= State.CONTENT.ordinal()
          && _state.ordinal() < State.END.ordinal()) {
        // Handle HEAD response
        if (_responseStatus > 0 && _headResponse) {
          setState(State.END);
          handle = _handler.messageComplete();
        } else handle = parseContent(buffer);
      }

      // handle end states
      if (_state == State.END) {
        // eat white space
        while (buffer.remaining() > 0 && buffer.get(buffer.position()) <= HttpTokens.SPACE)
          buffer.get();
      } else if (_state == State.CLOSED) {
        if (BufferUtil.hasContent(buffer)) {
          // Just ignore data when closed
          _headerBytes += buffer.remaining();
          BufferUtil.clear(buffer);
          if (_headerBytes > _maxHeaderBytes) {
            // Don't want to waste time reading data of a closed request
            throw new IllegalStateException("too much data after closed");
          }
        }
      }

      // Handle EOF
      if (_eof && !buffer.hasRemaining()) {
        switch (_state) {
          case CLOSED:
            break;

          case START:
            _handler.earlyEOF();
            setState(State.CLOSED);
            break;

          case END:
            setState(State.CLOSED);
            break;

          case EOF_CONTENT:
            handle = _handler.messageComplete() || handle;
            setState(State.CLOSED);
            break;

          case CONTENT:
          case CHUNKED_CONTENT:
          case CHUNK_SIZE:
          case CHUNK_PARAMS:
          case CHUNK:
            _handler.earlyEOF();
            setState(State.CLOSED);
            break;

          default:
            if (DEBUG) LOG.debug("{} EOF in {}", this, _state);
            _handler.badMessage(400, null);
            setState(State.CLOSED);
            break;
        }
      }

      return handle;
    } catch (BadMessage e) {
      BufferUtil.clear(buffer);

      LOG.warn(
          "badMessage: "
              + e._code
              + (e._message != null ? " " + e._message : "")
              + " for "
              + _handler);
      if (DEBUG) LOG.debug(e);
      setState(State.CLOSED);
      _handler.badMessage(e._code, e._message);
      return false;
    } catch (Exception e) {
      BufferUtil.clear(buffer);

      LOG.warn("badMessage: " + e.toString() + " for " + _handler);
      if (DEBUG) LOG.debug(e);

      if (_state.ordinal() <= State.END.ordinal()) {
        setState(State.CLOSED);
        _handler.badMessage(400, null);
      } else {
        _handler.earlyEOF();
        setState(State.CLOSED);
      }

      return false;
    }
  }
Exemple #6
0
  protected boolean parseContent(ByteBuffer buffer) {
    int remaining = buffer.remaining();
    if (remaining == 0 && _state == State.CONTENT) {
      long content = _contentLength - _contentPosition;
      if (content == 0) {
        setState(State.END);
        return _handler.messageComplete();
      }
    }

    // Handle _content
    byte ch;
    while (_state.ordinal() < State.END.ordinal() && remaining > 0) {
      switch (_state) {
        case EOF_CONTENT:
          _contentChunk = buffer.asReadOnlyBuffer();
          _contentPosition += remaining;
          buffer.position(buffer.position() + remaining);
          if (_handler.content(_contentChunk)) return true;
          break;

        case CONTENT:
          {
            long content = _contentLength - _contentPosition;
            if (content == 0) {
              setState(State.END);
              return _handler.messageComplete();
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              // limit content by expected size
              if (remaining > content) {
                // We can cast remaining to an int as we know that it is
                // smaller than
                // or equal to length which is already an int.
                _contentChunk.limit(_contentChunk.position() + (int) content);
              }

              _contentPosition += _contentChunk.remaining();
              buffer.position(buffer.position() + _contentChunk.remaining());

              if (_handler.content(_contentChunk)) return true;

              if (_contentPosition == _contentLength) {
                setState(State.END);
                return _handler.messageComplete();
              }
            }
            break;
          }

        case CHUNKED_CONTENT:
          {
            ch = next(buffer);
            if (ch > HttpTokens.SPACE) {
              _chunkLength = TypeUtils.convertHexDigit(ch);
              _chunkPosition = 0;
              setState(State.CHUNK_SIZE);
            }

            break;
          }

        case CHUNK_SIZE:
          {
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) setState(State.CHUNK_END);
              else setState(State.CHUNK);
            } else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
              setState(State.CHUNK_PARAMS);
            else _chunkLength = _chunkLength * 16 + TypeUtils.convertHexDigit(ch);
            break;
          }

        case CHUNK_PARAMS:
          {
            ch = next(buffer);
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) setState(State.CHUNK_END);
              else setState(State.CHUNK);
            }
            break;
          }

        case CHUNK:
          {
            int chunk = _chunkLength - _chunkPosition;
            if (chunk == 0) {
              setState(State.CHUNKED_CONTENT);
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              if (remaining > chunk) _contentChunk.limit(_contentChunk.position() + chunk);
              chunk = _contentChunk.remaining();

              _contentPosition += chunk;
              _chunkPosition += chunk;
              buffer.position(buffer.position() + chunk);
              if (_handler.content(_contentChunk)) return true;
            }
            break;
          }

        case CHUNK_END:
          {
            // TODO handle chunk trailer
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              setState(State.END);
              return _handler.messageComplete();
            }
            throw new IllegalCharacterException(_state, ch, buffer);
          }

        case CLOSED:
          {
            BufferUtils.clear(buffer);
            return false;
          }

        default:
          break;
      }

      remaining = buffer.remaining();
    }
    return false;
  }
Exemple #7
0
  /**
   * Parse until next Event.
   *
   * @param buffer the buffer to parse
   * @return True if an {@link RequestHandler} method was called and it returned true;
   */
  public boolean parseNext(ByteBuffer buffer) {
    if (DEBUG) log.debug("parseNext s={} {}", _state, BufferUtils.toDetailString(buffer));
    try {
      // Start a request/response
      if (_state == State.START) {
        _version = null;
        _method = null;
        _methodString = null;
        _endOfContent = EndOfContent.UNKNOWN_CONTENT;
        _header = null;
        if (quickStart(buffer)) return true;
      }

      // Request/response line
      if (_state.ordinal() >= State.START.ordinal() && _state.ordinal() < State.HEADER.ordinal()) {
        if (parseLine(buffer)) return true;
      }

      // parse headers
      if (_state.ordinal() >= State.HEADER.ordinal()
          && _state.ordinal() < State.CONTENT.ordinal()) {
        if (parseHeaders(buffer)) return true;
      }

      // parse content
      if (_state.ordinal() >= State.CONTENT.ordinal() && _state.ordinal() < State.END.ordinal()) {
        // Handle HEAD response
        if (_responseStatus > 0 && _headResponse) {
          setState(State.END);
          return _handler.messageComplete();
        } else {
          if (parseContent(buffer)) return true;
        }
      }

      // handle end states
      if (_state == State.END) {
        // eat white space
        while (buffer.remaining() > 0 && buffer.get(buffer.position()) <= HttpTokens.SPACE)
          buffer.get();
      } else if (_state == State.CLOSE) {
        // Seeking EOF
        if (BufferUtils.hasContent(buffer)) {
          // Just ignore data when closed
          _headerBytes += buffer.remaining();
          BufferUtils.clear(buffer);
          if (_maxHeaderBytes > 0 && _headerBytes > _maxHeaderBytes) {
            // Don't want to waste time reading data of a closed
            // request
            throw new IllegalStateException("too much data seeking EOF");
          }
        }
      } else if (_state == State.CLOSED) {
        BufferUtils.clear(buffer);
      }

      // Handle EOF
      if (_eof && !buffer.hasRemaining()) {
        switch (_state) {
          case CLOSED:
            break;

          case START:
            setState(State.CLOSED);
            _handler.earlyEOF();
            break;

          case END:
          case CLOSE:
            setState(State.CLOSED);
            break;

          case EOF_CONTENT:
            setState(State.CLOSED);
            return _handler.messageComplete();

          case CONTENT:
          case CHUNKED_CONTENT:
          case CHUNK_SIZE:
          case CHUNK_PARAMS:
          case CHUNK:
            setState(State.CLOSED);
            _handler.earlyEOF();
            break;

          default:
            if (DEBUG) log.debug("{} EOF in {}", this, _state);
            setState(State.CLOSED);
            _handler.badMessage(400, null);
            break;
        }
      }
    } catch (BadMessageException e) {
      BufferUtils.clear(buffer);

      Throwable cause = e.getCause();
      boolean stack =
          log.isDebugEnable()
              || (!(cause instanceof NumberFormatException)
                  && (cause instanceof RuntimeException || cause instanceof Error));

      if (stack)
        log.warn(
            "bad HTTP parsed: "
                + e._code
                + (e.getReason() != null ? " " + e.getReason() : "")
                + " for "
                + _handler,
            e);
      else
        log.warn(
            "bad HTTP parsed: "
                + e._code
                + (e.getReason() != null ? " " + e.getReason() : "")
                + " for "
                + _handler);
      setState(State.CLOSE);
      _handler.badMessage(e.getCode(), e.getReason());
    } catch (NumberFormatException | IllegalStateException e) {
      BufferUtils.clear(buffer);
      log.warn("parse exception: {} in {} for {}", e.toString(), _state, _handler);
      if (DEBUG) log.debug("parse exception", e);

      switch (_state) {
        case CLOSED:
          break;
        case CLOSE:
          _handler.earlyEOF();
          break;
        default:
          setState(State.CLOSE);
          _handler.badMessage(400, null);
      }
    } catch (Exception | Error e) {
      BufferUtils.clear(buffer);

      log.warn("parse exception: " + e.toString() + " for " + _handler, e);

      switch (_state) {
        case CLOSED:
          break;
        case CLOSE:
          _handler.earlyEOF();
          break;
        default:
          setState(State.CLOSE);
          _handler.badMessage(400, null);
      }
    }
    return false;
  }