private State readHeaders(ChannelBuffer buffer) throws TooLongFrameException { headerSize = 0; String line = readHeader(buffer); int match = 0; String name = null; String value = null; boolean nameToInsert = false; boolean hasContentLength = false; if (line.length() != 0) { do { char firstChar = line.charAt(0); if (name != null && (firstChar == ' ' || firstChar == '\t')) { value = value + ' ' + line.trim(); } else { if (name != null && nameToInsert) { match++; message.addHeader(name, value); } if (match < 3 && firstChar == 'C') { if (line.startsWith("Connection: ")) { name = "Connection"; value = line.substring(12); nameToInsert = true; } else if (line.startsWith("Cookie: ")) { name = "Cookie"; value = line.substring(8); nameToInsert = true; } else if (line.startsWith("Content-Length: ")) { name = "Content-Length"; value = line.substring(16); nameToInsert = true; hasContentLength = true; } else { nameToInsert = false; } } } line = readHeader(buffer); } while (line.length() != 0); // Add the last header. if (name != null && nameToInsert) { match++; message.addHeader(name, value); } } if (hasContentLength) { return State.READ_FIXED_LENGTH_CONTENT; } else { return State.READ_VARIABLE_LENGTH_CONTENT; } }
private Object reset() { HttpMessage message = this.message; ChannelBuffer content = this.content; if (content != null) { message.setContent(content); this.content = null; } this.message = null; checkpoint(State.SKIP_CONTROL_CHARS); return message; }
private long getContentLength(HttpMessage message, int defaultValue) { String value = message.getHeader("Content-Length"); if (FastIntegerParser.isNumber(value)) { return FastIntegerParser.parseLong(value); } return defaultValue; }
static boolean isTransferEncodingChunked(HttpMessage m) { List<String> chunked = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); if (chunked.isEmpty()) { return false; } for (String v : chunked) { if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { return true; } } return false; }
@Override protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception { switch (state) { case SKIP_CONTROL_CHARS: { try { skipControlCharacters(buffer); checkpoint(State.READ_INITIAL); } finally { checkpoint(); } } case READ_INITIAL: { final String[] initialLine = splitInitialLine(readLine(buffer, maxInitialLineLength)); if (initialLine.length < 3) { // Invalid initial line - ignore. checkpoint(State.SKIP_CONTROL_CHARS); return null; } message = createMessage(initialLine); checkpoint(State.READ_HEADER); } case READ_HEADER: { State nextState = readHeaders(buffer); checkpoint(nextState); if (nextState == State.SKIP_CONTROL_CHARS) { return message; } else { long contentLength = getContentLength(message, -1); if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) { content = ChannelBuffers.EMPTY_BUFFER; return reset(); } switch (nextState) { case READ_FIXED_LENGTH_CONTENT: if (contentLength > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) { // Generate HttpMessage first. HttpChunks will follow. checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS); message.setChunked(true); // chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS // state reads data chunk by chunk. chunkSize = getContentLength(message, -1); return message; } break; case READ_VARIABLE_LENGTH_CONTENT: if (buffer.readableBytes() > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) { // Generate HttpMessage first. HttpChunks will follow. checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS); message.setChunked(true); return message; } break; default: throw new IllegalStateException("Unexpected state: " + nextState); } } // We return null here, this forces decode to be called again where we will decode the // content return null; } case READ_VARIABLE_LENGTH_CONTENT: { if (content == null) { content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory()); } // this will cause a replay error until the channel is closed where this will read what's // left in the buffer content.writeBytes(buffer.readBytes(buffer.readableBytes())); return reset(); } case READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS: { // Keep reading data as a chunk until the end of connection is reached. int chunkSize = Math.min(maxChunkSize, buffer.readableBytes()); HttpChunk chunk = new DefaultHttpChunk(buffer.readBytes(chunkSize)); if (!buffer.readable()) { // Reached to the end of the connection. reset(); if (!chunk.isLast()) { // Append the last chunk. return new Object[] {chunk, HttpChunk.LAST_CHUNK}; } } return chunk; } case READ_FIXED_LENGTH_CONTENT: { // we have a content-length so we just read the correct number of bytes readFixedLengthContent(buffer); return reset(); } case READ_FIXED_LENGTH_CONTENT_AS_CHUNKS: { long chunkSize = this.chunkSize; HttpChunk chunk; if (chunkSize > maxChunkSize) { chunk = new DefaultHttpChunk(buffer.readBytes(maxChunkSize)); chunkSize -= maxChunkSize; } else { assert chunkSize <= Integer.MAX_VALUE; chunk = new DefaultHttpChunk(buffer.readBytes((int) chunkSize)); chunkSize = 0; } this.chunkSize = chunkSize; if (chunkSize == 0) { // Read all content. reset(); if (!chunk.isLast()) { // Append the last chunk. return new Object[] {chunk, HttpChunk.LAST_CHUNK}; } } return chunk; } /** * everything else after this point takes care of reading chunked content. basically, read * chunk size, read chunk, read and ignore the CRLF and repeat until 0 */ case READ_CHUNK_SIZE: { String line = readLine(buffer, maxInitialLineLength); int chunkSize = getChunkSize(line); this.chunkSize = chunkSize; if (chunkSize == 0) { checkpoint(State.READ_CHUNK_FOOTER); return null; } else if (chunkSize > maxChunkSize) { // A chunk is too large. Split them into multiple chunks again. checkpoint(State.READ_CHUNKED_CONTENT_AS_CHUNKS); } else { checkpoint(State.READ_CHUNKED_CONTENT); } } case READ_CHUNKED_CONTENT: { assert chunkSize <= Integer.MAX_VALUE; HttpChunk chunk = new DefaultHttpChunk(buffer.readBytes((int) chunkSize)); checkpoint(State.READ_CHUNK_DELIMITER); return chunk; } case READ_CHUNKED_CONTENT_AS_CHUNKS: { long chunkSize = this.chunkSize; HttpChunk chunk; if (chunkSize > maxChunkSize) { chunk = new DefaultHttpChunk(buffer.readBytes(maxChunkSize)); chunkSize -= maxChunkSize; } else { assert chunkSize <= Integer.MAX_VALUE; chunk = new DefaultHttpChunk(buffer.readBytes((int) chunkSize)); chunkSize = 0; } this.chunkSize = chunkSize; if (chunkSize == 0) { // Read all content. checkpoint(State.READ_CHUNK_DELIMITER); } if (!chunk.isLast()) { return chunk; } } case READ_CHUNK_DELIMITER: { for (; ; ) { byte next = buffer.readByte(); if (next == HttpCodecUtil.CR) { if (buffer.readByte() == HttpCodecUtil.LF) { checkpoint(State.READ_CHUNK_SIZE); return null; } } else if (next == HttpCodecUtil.LF) { checkpoint(State.READ_CHUNK_SIZE); return null; } } } case READ_CHUNK_FOOTER: { HttpChunkTrailer trailer = readTrailingHeaders(buffer); if (maxChunkSize == 0) { // Chunked encoding disabled. return reset(); } else { reset(); // The last chunk, which is empty return trailer; } } default: { throw new Error("Shouldn't reach here."); } } }
private long getContentLength(HttpMessage message) { return FastIntegerParser.parseInt(message.getHeader("Content-Length")); }
public static void setContentLength(HttpMessage message, long contentLength) { message.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(contentLength)); }
public static boolean isKeepAlive(HttpMessage message) { return HttpHeaders.isKeepAlive(message) && message.getProtocolVersion().equals(HttpVersion.HTTP_1_1); }