/* * Parse the message headers and return true if the handler has signaled for a return */ protected boolean parseHeaders(ByteBuffer buffer) { boolean handle = false; // Process headers while (_state.ordinal() < State.CONTENT.ordinal() && buffer.hasRemaining() && !handle) { // process each character byte ch = next(buffer); if (ch == 0) break; if (_maxHeaderBytes > 0 && ++_headerBytes > _maxHeaderBytes) { LOG.warn("Header is too large >" + _maxHeaderBytes); throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413); } switch (_state) { case HEADER: switch (ch) { case HttpTokens.COLON: case HttpTokens.SPACE: case HttpTokens.TAB: { // header value without name - continuation? if (_valueString == null) { _string.setLength(0); _length = 0; } else { setString(_valueString); _string.append(' '); _length++; _valueString = null; } setState(State.HEADER_VALUE); break; } default: { // handler last header if any. Delayed to here just in case there was a // continuation line (above) if (_headerString != null || _valueString != null) { // Handle known headers if (_header != null && handleKnownHeaders(buffer)) { _headerString = _valueString = null; _header = null; _value = null; _field = null; return true; } handle = _handler.parsedHeader( _field != null ? _field : new HttpField(_header, _headerString, _valueString)) || handle; } _headerString = _valueString = null; _header = null; _value = null; _field = null; // now handle the ch if (ch == HttpTokens.LINE_FEED) { _contentPosition = 0; // End of headers! // Was there a required host header? if (!_host && _version != HttpVersion.HTTP_1_0 && _requestHandler != null) { throw new BadMessage(HttpStatus.BAD_REQUEST_400, "No Host"); } // is it a response that cannot have a body? if (_responseHandler != null && // response (_responseStatus == 304 || // not-modified response _responseStatus == 204 || // no-content response _responseStatus < 200)) // 1xx response _endOfContent = EndOfContent.NO_CONTENT; // ignore any other headers set // else if we don't know framing else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT) { if (_responseStatus == 0 // request || _responseStatus == 304 // not-modified response || _responseStatus == 204 // no-content response || _responseStatus < 200) // 1xx response _endOfContent = EndOfContent.NO_CONTENT; else _endOfContent = EndOfContent.EOF_CONTENT; } // How is the message ended? switch (_endOfContent) { case EOF_CONTENT: setState(State.EOF_CONTENT); handle = _handler.headerComplete() || handle; break; case CHUNKED_CONTENT: setState(State.CHUNKED_CONTENT); handle = _handler.headerComplete() || handle; break; case NO_CONTENT: handle = _handler.headerComplete() || handle; setState(State.END); handle = _handler.messageComplete() || handle; break; default: setState(State.CONTENT); handle = _handler.headerComplete() || handle; break; } } else if (ch <= HttpTokens.SPACE) throw new BadMessage(); else { if (buffer.hasRemaining()) { // Try a look ahead for the known header name and value. HttpField field = _connectionFields == null ? null : _connectionFields.getBest(buffer, -1, buffer.remaining()); if (field == null) field = CACHE.getBest(buffer, -1, buffer.remaining()); if (field != null) { final String n; final String v; if (_strict) { // Have to get the fields exactly from the buffer to match case String fn = field.getName(); String fv = field.getValue(); n = BufferUtil.toString( buffer, buffer.position() - 1, fn.length(), StandardCharsets.US_ASCII); if (fv == null) v = null; else { v = BufferUtil.toString( buffer, buffer.position() + fn.length() + 1, fv.length(), StandardCharsets.ISO_8859_1); field = new HttpField(field.getHeader(), n, v); } } else { n = field.getName(); v = field.getValue(); } _header = field.getHeader(); _headerString = n; if (v == null) { // Header only setState(State.HEADER_VALUE); _string.setLength(0); _length = 0; buffer.position(buffer.position() + n.length() + 1); break; } else { // Header and value int pos = buffer.position() + n.length() + v.length() + 1; byte b = buffer.get(pos); if (b == HttpTokens.CARRIAGE_RETURN || b == HttpTokens.LINE_FEED) { _field = field; _valueString = v; setState(State.HEADER_IN_VALUE); if (b == HttpTokens.CARRIAGE_RETURN) { _cr = true; buffer.position(pos + 1); } else buffer.position(pos); break; } else { setState(State.HEADER_IN_VALUE); setString(v); buffer.position(pos); break; } } } } // New header setState(State.HEADER_IN_NAME); _string.setLength(0); _string.append((char) ch); _length = 1; } } } break; case HEADER_IN_NAME: if (ch == HttpTokens.COLON || ch == HttpTokens.LINE_FEED) { if (_headerString == null) { _headerString = takeString(); _header = HttpHeader.CACHE.get(_headerString); } _length = -1; setState(ch == HttpTokens.LINE_FEED ? State.HEADER : State.HEADER_VALUE); break; } if (ch >= HttpTokens.SPACE || ch == HttpTokens.TAB) { if (_header != null) { setString(_header.asString()); _header = null; _headerString = null; } _string.append((char) ch); if (ch > HttpTokens.SPACE) _length = _string.length(); break; } throw new BadMessage("Illegal character"); case HEADER_VALUE: if (ch > HttpTokens.SPACE || ch < 0) { _string.append((char) (0xff & ch)); _length = _string.length(); setState(State.HEADER_IN_VALUE); break; } if (ch == HttpTokens.SPACE || ch == HttpTokens.TAB) break; if (ch == HttpTokens.LINE_FEED) { if (_length > 0) { _value = null; _valueString = (_valueString == null) ? takeString() : (_valueString + " " + takeString()); } setState(State.HEADER); break; } throw new BadMessage("Illegal character"); case HEADER_IN_VALUE: if (ch >= HttpTokens.SPACE || ch < 0) { if (_valueString != null) { setString(_valueString); _valueString = null; _field = null; } _string.append((char) (0xff & ch)); if (ch > HttpTokens.SPACE || ch < 0) _length = _string.length(); break; } if (ch == HttpTokens.LINE_FEED) { if (_length > 0) { _value = null; _valueString = takeString(); _length = -1; } setState(State.HEADER); break; } throw new BadMessage("Illegal character"); default: throw new IllegalStateException(_state.toString()); } } return handle; }
/* 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 BadMessage(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 BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413); } } switch (_state) { case METHOD: if (ch == HttpTokens.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 < HttpTokens.SPACE) throw new BadMessage(ch < 0 ? "Illegal character" : "No URI"); 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 BadMessage(HttpStatus.BAD_REQUEST_400, "Unknown Version"); setState(State.SPACE1); } else if (ch < HttpTokens.SPACE) throw new BadMessage(ch < 0 ? "Illegal character" : "No Status"); 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.clear(); 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 BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414); } if (_uri.remaining() <= len) { ByteBuffer uri = ByteBuffer.allocate(_uri.capacity() + 2 * len); _uri.flip(); uri.put(_uri); _uri = uri; } _uri.put(array, p - 1, len + 1); buffer.position(i - buffer.arrayOffset()); } else _uri.put(ch); } } else if (ch < HttpTokens.SPACE) { throw new BadMessage( 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 BadMessage(); } break; case URI: if (ch == HttpTokens.SPACE) { setState(State.SPACE2); } else if (ch < HttpTokens.SPACE && ch >= 0) { // HTTP/0.9 _uri.flip(); handle = _requestHandler.startRequest(_method, _methodString, _uri, null) || handle; setState(State.END); BufferUtil.clear(buffer); handle = _handler.headerComplete() || handle; handle = _handler.messageComplete() || handle; } else { if (!_uri.hasRemaining()) { ByteBuffer uri = ByteBuffer.allocate(_uri.capacity() * 2); _uri.flip(); uri.put(_uri); _uri = uri; } _uri.put(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 _uri.flip(); handle = _requestHandler.startRequest(_method, _methodString, _uri, null) || handle; setState(State.END); BufferUtil.clear(buffer); handle = _handler.headerComplete() || handle; handle = _handler.messageComplete() || handle; } } else if (ch < 0) throw new BadMessage(); 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 BadMessage(HttpStatus.BAD_REQUEST_400, "Unknown Version"); // Should we try to cache header fields? if (_connectionFields == null && _version.getVersion() >= HttpVersion.HTTP_1_1.getVersion()) { int header_cache = _handler.getHeaderCacheSize(); if (header_cache > 0) _connectionFields = new ArrayTernaryTrie<>(header_cache); } setState(State.HEADER); _uri.flip(); handle = _requestHandler.startRequest(_method, _methodString, _uri, _version) || handle; continue; } else if (ch >= HttpTokens.SPACE) _string.append((char) ch); else throw new BadMessage(); 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 BadMessage(); break; default: throw new IllegalStateException(_state.toString()); } } return handle; }