/* * 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; }
/* * 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; }