private void parsedHeader() { // 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) { boolean add_to_connection_trie = false; switch (_header) { case CONTENT_LENGTH: if (_endOfContent != EndOfContent.CHUNKED_CONTENT) { try { _contentLength = Long.parseLong(_valueString); } catch (NumberFormatException e) { throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Bad Content-Length"); } if (_contentLength <= 0) _endOfContent = EndOfContent.NO_CONTENT; else _endOfContent = EndOfContent.CONTENT_LENGTH; } break; case TRANSFER_ENCODING: if (_value == HttpHeaderValue.CHUNKED) _endOfContent = EndOfContent.CHUNKED_CONTENT; else { if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString())) _endOfContent = EndOfContent.CHUNKED_CONTENT; else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString())) { throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Bad chunking"); } } break; case HOST: _host = true; if (!(_field instanceof HostPortHttpField)) { _field = new HostPortHttpField( _header, _strict ? _headerString : _header.asString(), _valueString); add_to_connection_trie = _connectionFields != null; } break; case CONNECTION: // Don't cache if not persistent if (_valueString != null && _valueString.contains("close")) _connectionFields = null; break; case AUTHORIZATION: case ACCEPT: case ACCEPT_CHARSET: case ACCEPT_ENCODING: case ACCEPT_LANGUAGE: case COOKIE: case CACHE_CONTROL: case USER_AGENT: add_to_connection_trie = _connectionFields != null && _field == null; break; default: break; } if (add_to_connection_trie && !_connectionFields.isFull() && _header != null && _valueString != null) { if (_field == null) _field = new HttpField(_header, _strict ? _headerString : _header.asString(), _valueString); _connectionFields.put(_field); } } _handler.parsedHeader( _field != null ? _field : new HttpField(_header, _headerString, _valueString)); } _headerString = _valueString = null; _header = null; _value = null; _field = null; }
/* * 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 BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413); } switch (_state) { case HEADER: switch (ch) { case HttpTokens.COLON: case HttpTokens.SPACE: case HttpTokens.TAB: throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Bad Continuation"); case HttpTokens.LINE_FEED: { _contentPosition = 0; // End of headers! // Was there a required host header? if (!_host && _version == HttpVersion.HTTP_1_1 && _requestHandler != null) { throw new BadMessageException(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; return handle; case CHUNKED_CONTENT: setState(State.CHUNKED_CONTENT); handle = _handler.headerComplete() || handle; return handle; case NO_CONTENT: handle = _handler.headerComplete() || handle; setState(State.END); handle = _handler.messageComplete() || handle; return handle; default: setState(State.CONTENT); handle = _handler.headerComplete() || handle; return handle; } } default: { // now handle the ch if (ch <= HttpTokens.SPACE) throw new BadMessageException(); 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 = BufferUtils.toString( buffer, buffer.position() - 1, fn.length(), StandardCharsets.US_ASCII); if (fv == null) v = null; else { v = BufferUtils.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) { if (_headerString == null) { _headerString = takeString(); _header = HttpHeader.CACHE.get(_headerString); } _length = -1; setState(State.HEADER_VALUE); break; } if (ch > HttpTokens.SPACE) { if (_header != null) { setString(_header.asString()); _header = null; _headerString = null; } _string.append((char) ch); if (ch > HttpTokens.SPACE) _length = _string.length(); break; } throw new IllegalCharacterException(_state, ch, buffer); 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) { _value = null; _string.setLength(0); _valueString = null; _length = -1; parsedHeader(); setState(State.HEADER); break; } throw new IllegalCharacterException(_state, ch, buffer); case HEADER_IN_VALUE: if (ch >= HttpTokens.SPACE || ch < 0 || ch == HttpTokens.TAB) { 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; } parsedHeader(); setState(State.HEADER); break; } throw new IllegalCharacterException(_state, ch, buffer); default: throw new IllegalStateException(_state.toString()); } } return handle; }
static { CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE)); CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE)); CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.UPGRADE)); CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip")); CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip, deflate")); CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip,deflate,sdch")); CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-US,en;q=0.5")); CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-GB,en-US;q=0.8,en;q=0.6")); CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.3")); CACHE.put(new HttpField(HttpHeader.ACCEPT, "*/*")); CACHE.put(new HttpField(HttpHeader.ACCEPT, "image/png,image/*;q=0.8,*/*;q=0.5")); CACHE.put( new HttpField( HttpHeader.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")); CACHE.put(new HttpField(HttpHeader.PRAGMA, "no-cache")); CACHE.put( new HttpField( HttpHeader.CACHE_CONTROL, "private, no-cache, no-cache=Set-Cookie, proxy-revalidate")); CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL, "no-cache")); CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH, "0")); CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING, "gzip")); CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING, "deflate")); CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING, "chunked")); CACHE.put(new HttpField(HttpHeader.EXPIRES, "Fri, 01 Jan 1990 00:00:00 GMT")); // Add common Content types as fields for (String type : new String[] { "text/plain", "text/html", "text/xml", "text/json", "application/json", "application/x-www-form-urlencoded" }) { HttpField field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type); CACHE.put(field); for (String charset : new String[] {"utf-8", "iso-8859-1"}) { CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset)); CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset)); CACHE.put( new PreEncodedHttpField( HttpHeader.CONTENT_TYPE, type + ";charset=" + charset.toUpperCase())); CACHE.put( new PreEncodedHttpField( HttpHeader.CONTENT_TYPE, type + "; charset=" + charset.toUpperCase())); } } // Add headers with null values so HttpParser can avoid looking up name // again for unknown values for (HttpHeader h : HttpHeader.values()) if (!CACHE.put(new HttpField(h, (String) null))) throw new IllegalStateException("CACHE FULL"); // Add some more common headers CACHE.put(new HttpField(HttpHeader.REFERER, (String) null)); CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE, (String) null)); CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH, (String) null)); CACHE.put(new HttpField(HttpHeader.AUTHORIZATION, (String) null)); CACHE.put(new HttpField(HttpHeader.COOKIE, (String) null)); }