@Override protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (log.isDebugEnabled()) log.debug("encode"); if (msg instanceof SipMessage) { SipMessage m = (SipMessage) msg; ChannelBuffer header = dynamicBuffer(channel.getConfig().getBufferFactory()); encodeInitialLine(header, m); encodeHeaders(header, m); // always add a single white line between headers and content header.writeByte(CR); header.writeByte(LF); ChannelBuffer content = m.getContent(); if (content != null && !content.readable()) { if (log.isDebugEnabled()) log.debug("no content available"); // no content available return header; } else { if (log.isDebugEnabled()) log.debug("content available"); // TODO write content depending on content type return wrappedBuffer(header, content); } } // Unknown message type. return msg; }
@Test public void testNettyConfigBuilder() { NettyConfigBuilder configBuilder = new NettyConfigBuilder(); configBuilder.getServerSocketChannelConfig().setReceiveBufferSize(10000); configBuilder.getServerSocketChannelConfig().setBacklog(1000); configBuilder.getServerSocketChannelConfig().setReuseAddress(true); ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory()); bootstrap.setOptions(configBuilder.getOptions()); bootstrap.setPipelineFactory(Channels.pipelineFactory(Channels.pipeline())); Channel serverChannel = bootstrap.bind(new InetSocketAddress(port)); Assert.assertEquals( ((ServerSocketChannelConfig) serverChannel.getConfig()).getReceiveBufferSize(), 10000); Assert.assertEquals(((ServerSocketChannelConfig) serverChannel.getConfig()).getBacklog(), 1000); Assert.assertTrue(((ServerSocketChannelConfig) serverChannel.getConfig()).isReuseAddress()); }
@Override protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (!(msg instanceof ChannelBuffer)) { return msg; } ChannelBuffer body = (ChannelBuffer) msg; ChannelBuffer header = channel.getConfig().getBufferFactory().getBuffer(body.order(), lengthFieldLength); int length = lengthIncludesLengthFieldLength ? body.readableBytes() + lengthFieldLength : body.readableBytes(); switch (lengthFieldLength) { case 1: if (length >= 256) { throw new IllegalArgumentException("length does not fit into a byte: " + length); } header.writeByte((byte) length); break; case 2: if (length >= 65536) { throw new IllegalArgumentException("length does not fit into a short integer: " + length); } header.writeShort((short) length); break; case 3: if (length >= 16777216) { throw new IllegalArgumentException( "length does not fit into a medium integer: " + length); } header.writeMedium(length); break; case 4: header.writeInt(length); break; case 8: header.writeLong(length); break; default: throw new Error("should not reach here"); } return wrappedBuffer(header, body); }
/** Set the max size for the write queue, see {@link WriteStream#setWriteQueueMaxSize} */ public void setWriteQueueMaxSize(int size) { NioSocketChannelConfig conf = (NioSocketChannelConfig) channel.getConfig(); conf.setWriteBufferLowWaterMark(size / 2); conf.setWriteBufferHighWaterMark(size); }
@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."); } } }