private void encodeChunkedContent( ChannelHandlerContext ctx, Object msg, int contentLength, List<Object> out) { if (contentLength > 0) { byte[] length = Integer.toHexString(contentLength).getBytes(CharsetUtil.US_ASCII); ByteBuf buf = ctx.alloc().buffer(length.length + 2); buf.writeBytes(length); buf.writeBytes(CRLF); out.add(buf); out.add(encodeAndRetain(msg)); out.add(CRLF_BUF.duplicate()); } if (msg instanceof LastHttpContent) { HttpHeaders headers = ((LastHttpContent) msg).trailingHeaders(); if (headers.isEmpty()) { out.add(ZERO_CRLF_CRLF_BUF.duplicate()); } else { ByteBuf buf = ctx.alloc().buffer(); buf.writeBytes(ZERO_CRLF); HttpHeaders.encode(headers, buf); buf.writeBytes(CRLF); out.add(buf); } state = ST_INIT; } else { if (contentLength == 0) { // Need to produce some output otherwise an // IllegalstateException will be thrown out.add(EMPTY_BUFFER); } } }
/** * Sets the value of the {@code "Connection"} header depending on the protocol version of the * specified message. This getMethod sets or removes the {@code "Connection"} header depending on * what the default keep alive mode of the message's protocol version is, as specified by {@link * HttpVersion#isKeepAliveDefault()}. * * <ul> * <li>If the connection is kept alive by default: * <ul> * <li>set to {@code "close"} if {@code keepAlive} is {@code false}. * <li>remove otherwise. * </ul> * <li>If the connection is closed by default: * <ul> * <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}. * <li>remove otherwise. * </ul> * </ul> */ public static void setKeepAlive(HttpMessage message, boolean keepAlive) { HttpHeaders h = message.headers(); if (message.getProtocolVersion().isKeepAliveDefault()) { if (keepAlive) { h.remove(Names.CONNECTION); } else { h.set(Names.CONNECTION, Values.CLOSE); } } else { if (keepAlive) { h.set(Names.CONNECTION, Values.KEEP_ALIVE); } else { h.remove(Names.CONNECTION); } } }
/** * Returns the content length of the specified web socket message. If the specified message is not * a web socket message, {@code -1} is returned. */ private static int getWebSocketContentLength(HttpMessage message) { // WebSockset messages have constant content-lengths. HttpHeaders h = message.headers(); if (message instanceof HttpRequest) { HttpRequest req = (HttpRequest) message; if (HttpMethod.GET.equals(req.getMethod()) && h.contains(Names.SEC_WEBSOCKET_KEY1) && h.contains(Names.SEC_WEBSOCKET_KEY2)) { return 8; } } else if (message instanceof HttpResponse) { HttpResponse res = (HttpResponse) message; if (res.getStatus().code() == 101 && h.contains(Names.SEC_WEBSOCKET_ORIGIN) && h.contains(Names.SEC_WEBSOCKET_LOCATION)) { return 16; } } // Not a web socket message return -1; }
@Override protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception { ByteBuf buf = null; if (msg instanceof HttpMessage) { if (state != ST_INIT) { throw new IllegalStateException( "unexpected message type: " + StringUtil.simpleClassName(msg)); } @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"}) H m = (H) msg; buf = ctx.alloc().buffer(); // Encode the message. encodeInitialLine(buf, m); HttpHeaders.encode(m.headers(), buf); buf.writeBytes(CRLF); state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK; } if (msg instanceof HttpContent || msg instanceof ByteBuf || msg instanceof FileRegion) { if (state == ST_INIT) { throw new IllegalStateException( "unexpected message type: " + StringUtil.simpleClassName(msg)); } int contentLength = contentLength(msg); if (state == ST_CONTENT_NON_CHUNK) { if (contentLength > 0) { if (buf != null && buf.writableBytes() >= contentLength && msg instanceof HttpContent) { // merge into other buffer for performance reasons buf.writeBytes(((HttpContent) msg).content()); out.add(buf); } else { if (buf != null) { out.add(buf); } out.add(encodeAndRetain(msg)); } } else { if (buf != null) { out.add(buf); } else { // Need to produce some output otherwise an // IllegalStateException will be thrown out.add(EMPTY_BUFFER); } } if (msg instanceof LastHttpContent) { state = ST_INIT; } } else if (state == ST_CONTENT_CHUNK) { if (buf != null) { out.add(buf); } encodeChunkedContent(ctx, msg, contentLength, out); } else { throw new Error(); } } else { if (buf != null) { out.add(buf); } } }
@Deprecated protected static void encodeAscii(String s, ByteBuf buf) { HttpHeaders.encodeAscii0(s, buf); }
@Override protected void encode(ChannelHandlerContext ctx, HttpObject msg, ByteBuf out) throws Exception { if (msg instanceof HttpMessage) { if (state != ST_INIT) { throw new IllegalStateException( "unexpected message type: " + msg.getClass().getSimpleName()); } @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"}) H m = (H) msg; // Encode the message. encodeInitialLine(out, m); encodeHeaders(out, m); out.writeByte(CR); out.writeByte(LF); state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK; } if (msg instanceof HttpContent) { if (state == ST_INIT) { throw new IllegalStateException( "unexpected message type: " + msg.getClass().getSimpleName()); } HttpContent chunk = (HttpContent) msg; ByteBuf content = chunk.content(); int contentLength = content.readableBytes(); if (state == ST_CONTENT_NON_CHUNK) { if (contentLength > 0) { out.writeBytes(content, content.readerIndex(), content.readableBytes()); } if (chunk instanceof LastHttpContent) { state = ST_INIT; } } else if (state == ST_CONTENT_CHUNK) { if (contentLength > 0) { out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII)); out.writeByte(CR); out.writeByte(LF); out.writeBytes(content, content.readerIndex(), contentLength); out.writeByte(CR); out.writeByte(LF); } if (chunk instanceof LastHttpContent) { out.writeByte((byte) '0'); out.writeByte(CR); out.writeByte(LF); encodeTrailingHeaders(out, (LastHttpContent) chunk); out.writeByte(CR); out.writeByte(LF); state = ST_INIT; } } else { throw new Error(); } } }