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; }
/* ------------------------------------------------------------------------------- */ 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; }
/* ------------------------------------------------------------------------------- */ public boolean inContentState() { return _state.ordinal() >= State.CONTENT.ordinal() && _state.ordinal() < State.END.ordinal(); }
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; }
/** * 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; } }
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; }
/** * 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; }