예제 #1
0
  /*
   * Quick lookahead for the start state looking for a request method or a
   * HTTP version, otherwise skip white space until something else to parse.
   */
  private boolean quickStart(ByteBuffer buffer) {
    if (_requestHandler != null) {
      _method = HttpMethod.lookAheadGet(buffer);
      if (_method != null) {
        _methodString = _method.asString();
        buffer.position(buffer.position() + _methodString.length() + 1);

        setState(State.SPACE1);
        return false;
      }
    } else if (_responseHandler != null) {
      _version = HttpVersion.lookAheadGet(buffer);
      if (_version != null) {
        buffer.position(buffer.position() + _version.asString().length() + 1);
        setState(State.SPACE1);
        return false;
      }
    }

    // Quick start look
    while (_state == State.START && buffer.hasRemaining()) {
      int ch = next(buffer);

      if (ch > SPACE) {
        _string.setLength(0);
        _string.append((char) ch);
        setState(_requestHandler != null ? State.METHOD : State.RESPONSE_VERSION);
        return false;
      } else if (ch == 0) break;
      else if (ch < 0) throw new BadMessageException();

      // count this white space as a header byte to avoid DOS
      if (_maxHeaderBytes > 0 && ++_headerBytes > _maxHeaderBytes) {
        log.warn("padding is too large > {}", _maxHeaderBytes);
        throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
      }
    }
    return false;
  }
예제 #2
0
  /*
   * Parse a request or response line
   */
  private boolean parseLine(ByteBuffer buffer) {
    boolean handle = false;

    // Process headers
    while (_state.ordinal() < State.HEADER.ordinal() && buffer.hasRemaining() && !handle) {
      // process each character
      byte ch = next(buffer);
      if (ch == 0) break;

      if (_maxHeaderBytes > 0 && ++_headerBytes > _maxHeaderBytes) {
        if (_state == State.URI) {
          log.warn("URI is too large > {}", _maxHeaderBytes);
          throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
        } else {
          if (_requestHandler != null) log.warn("request is too large > {}", _maxHeaderBytes);
          else log.warn("response is too large > {}", _maxHeaderBytes);
          throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
        }
      }

      switch (_state) {
        case METHOD:
          if (ch == SPACE) {
            _length = _string.length();
            _methodString = takeString();
            HttpMethod method = HttpMethod.CACHE.get(_methodString);
            if (method != null && !_strict) _methodString = method.asString();
            setState(State.SPACE1);
          } else if (ch < SPACE) {
            if (ch == LINE_FEED) throw new BadMessageException("No URI");
            else throw new IllegalCharacterException(_state, ch, buffer);
          } else _string.append((char) ch);
          break;

        case RESPONSE_VERSION:
          if (ch == HttpTokens.SPACE) {
            _length = _string.length();
            String version = takeString();
            _version = HttpVersion.CACHE.get(version);
            if (_version == null)
              throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Unknown Version");
            setState(State.SPACE1);
          } else if (ch < HttpTokens.SPACE) throw new IllegalCharacterException(_state, ch, buffer);
          else _string.append((char) ch);
          break;

        case SPACE1:
          if (ch > HttpTokens.SPACE || ch < 0) {
            if (_responseHandler != null) {
              setState(State.STATUS);
              setResponseStatus(ch - '0');
            } else {
              _uri.reset();
              setState(State.URI);
              // quick scan for space or EoBuffer
              if (buffer.hasArray()) {
                byte[] array = buffer.array();
                int p = buffer.arrayOffset() + buffer.position();
                int l = buffer.arrayOffset() + buffer.limit();
                int i = p;
                while (i < l && array[i] > HttpTokens.SPACE) i++;

                int len = i - p;
                _headerBytes += len;

                if (_maxHeaderBytes > 0 && ++_headerBytes > _maxHeaderBytes) {
                  log.warn("URI is too large > {}", _maxHeaderBytes);
                  throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
                }
                _uri.append(array, p - 1, len + 1);
                buffer.position(i - buffer.arrayOffset());
              } else _uri.append(ch);
            }
          } else if (ch < HttpTokens.SPACE) {
            throw new BadMessageException(
                HttpStatus.BAD_REQUEST_400, _requestHandler != null ? "No URI" : "No Status");
          }
          break;

        case STATUS:
          if (ch == HttpTokens.SPACE) {
            setState(State.SPACE2);
          } else if (ch >= '0' && ch <= '9') {
            _responseStatus = _responseStatus * 10 + (ch - '0');
          } else if (ch < HttpTokens.SPACE && ch >= 0) {
            handle = _responseHandler.startResponse(_version, _responseStatus, null) || handle;
            setState(State.HEADER);
          } else {
            throw new BadMessageException();
          }
          break;

        case URI:
          if (ch == HttpTokens.SPACE) {
            setState(State.SPACE2);
          } else if (ch < HttpTokens.SPACE && ch >= 0) {
            // HTTP/0.9
            throw new BadMessageException("HTTP/0.9 not supported");
          } else {
            _uri.append(ch);
          }
          break;

        case SPACE2:
          if (ch > HttpTokens.SPACE) {
            _string.setLength(0);
            _string.append((char) ch);
            if (_responseHandler != null) {
              _length = 1;
              setState(State.REASON);
            } else {
              setState(State.REQUEST_VERSION);

              // try quick look ahead for HTTP Version
              HttpVersion version;
              if (buffer.position() > 0 && buffer.hasArray())
                version =
                    HttpVersion.lookAheadGet(
                        buffer.array(),
                        buffer.arrayOffset() + buffer.position() - 1,
                        buffer.arrayOffset() + buffer.limit());
              else version = HttpVersion.CACHE.getBest(buffer, 0, buffer.remaining());

              if (version != null) {
                int pos = buffer.position() + version.asString().length() - 1;
                if (pos < buffer.limit()) {
                  byte n = buffer.get(pos);
                  if (n == HttpTokens.CARRIAGE_RETURN) {
                    _cr = true;
                    _version = version;
                    _string.setLength(0);
                    buffer.position(pos + 1);
                  } else if (n == HttpTokens.LINE_FEED) {
                    _version = version;
                    _string.setLength(0);
                    buffer.position(pos);
                  }
                }
              }
            }
          } else if (ch == HttpTokens.LINE_FEED) {
            if (_responseHandler != null) {
              handle = _responseHandler.startResponse(_version, _responseStatus, null) || handle;
              setState(State.HEADER);
            } else {
              // HTTP/0.9
              throw new BadMessageException("HTTP/0.9 not supported");
            }
          } else if (ch < 0) throw new BadMessageException();
          break;

        case REQUEST_VERSION:
          if (ch == HttpTokens.LINE_FEED) {
            if (_version == null) {
              _length = _string.length();
              _version = HttpVersion.CACHE.get(takeString());
            }
            if (_version == null)
              throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Unknown Version");

            // Should we try to cache header fields?
            if (_connectionFields == null
                && _version.getVersion() >= HttpVersion.HTTP_1_1.getVersion()
                && _handler.getHeaderCacheSize() > 0) {
              int header_cache = _handler.getHeaderCacheSize();
              _connectionFields = new ArrayTernaryTrie<>(header_cache);
            }

            setState(State.HEADER);

            handle =
                _requestHandler.startRequest(_methodString, _uri.toString(), _version) || handle;
            continue;
          } else if (ch >= HttpTokens.SPACE) _string.append((char) ch);
          else throw new BadMessageException();

          break;

        case REASON:
          if (ch == HttpTokens.LINE_FEED) {
            String reason = takeString();

            setState(State.HEADER);
            handle = _responseHandler.startResponse(_version, _responseStatus, reason) || handle;
            continue;
          } else if (ch >= HttpTokens.SPACE) {
            _string.append((char) ch);
            if (ch != ' ' && ch != '\t') _length = _string.length();
          } else throw new BadMessageException();
          break;

        default:
          throw new IllegalStateException(_state.toString());
      }
    }

    return handle;
  }