BAEL-4061: Implementing a Simple HTTP Server with Netty (#9416)
* BAEL-4061: Implementing a Simple HTTP Server with Netty * BAEL-4061: Added live test * BAEL-4061: moved helper class from test to main folder * BAEL-4061: updated tests to follow BDD
This commit is contained in:
parent
6df3b2b6fb
commit
ee146f5a13
|
@ -0,0 +1,116 @@
|
||||||
|
package com.baeldung.http.server;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
|
||||||
|
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
|
||||||
|
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
import io.netty.handler.codec.http.cookie.Cookie;
|
||||||
|
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||||
|
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
public class CustomHttpServerHandler extends SimpleChannelInboundHandler<Object> {
|
||||||
|
|
||||||
|
private HttpRequest request;
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||||
|
ctx.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
|
||||||
|
if (msg instanceof HttpRequest) {
|
||||||
|
HttpRequest request = this.request = (HttpRequest) msg;
|
||||||
|
|
||||||
|
if (HttpUtil.is100ContinueExpected(request)) {
|
||||||
|
writeResponse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData.setLength(0);
|
||||||
|
responseData.append(ResponseBuilder.addRequestAttributes(request));
|
||||||
|
responseData.append(ResponseBuilder.addHeaders(request));
|
||||||
|
responseData.append(ResponseBuilder.addParams(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData.append(ResponseBuilder.addDecoderResult(request));
|
||||||
|
|
||||||
|
if (msg instanceof HttpContent) {
|
||||||
|
HttpContent httpContent = (HttpContent) msg;
|
||||||
|
|
||||||
|
responseData.append(ResponseBuilder.addBody(httpContent));
|
||||||
|
responseData.append(ResponseBuilder.addDecoderResult(request));
|
||||||
|
|
||||||
|
if (msg instanceof LastHttpContent) {
|
||||||
|
LastHttpContent trailer = (LastHttpContent) msg;
|
||||||
|
responseData.append(ResponseBuilder.addLastResponse(request, trailer));
|
||||||
|
writeResponse(ctx, trailer, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeResponse(ChannelHandlerContext ctx) {
|
||||||
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER);
|
||||||
|
ctx.write(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeResponse(ChannelHandlerContext ctx, LastHttpContent trailer, StringBuilder responseData) {
|
||||||
|
boolean keepAlive = HttpUtil.isKeepAlive(request);
|
||||||
|
|
||||||
|
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HTTP_1_1, ((HttpObject) trailer).decoderResult()
|
||||||
|
.isSuccess() ? OK : BAD_REQUEST, Unpooled.copiedBuffer(responseData.toString(), CharsetUtil.UTF_8));
|
||||||
|
|
||||||
|
httpResponse.headers()
|
||||||
|
.set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||||
|
|
||||||
|
if (keepAlive) {
|
||||||
|
httpResponse.headers()
|
||||||
|
.setInt(HttpHeaderNames.CONTENT_LENGTH, httpResponse.content()
|
||||||
|
.readableBytes());
|
||||||
|
httpResponse.headers()
|
||||||
|
.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
String cookieString = request.headers()
|
||||||
|
.get(HttpHeaderNames.COOKIE);
|
||||||
|
if (cookieString != null) {
|
||||||
|
Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString);
|
||||||
|
if (!cookies.isEmpty()) {
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
httpResponse.headers()
|
||||||
|
.add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.write(httpResponse);
|
||||||
|
|
||||||
|
if (!keepAlive) {
|
||||||
|
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
|
||||||
|
.addListener(ChannelFutureListener.CLOSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.baeldung.http.server;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
import io.netty.handler.logging.LogLevel;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
|
||||||
|
public class HttpServer {
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
static Logger logger = LoggerFactory.getLogger(HttpServer.class);
|
||||||
|
|
||||||
|
public HttpServer(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
int port = args.length > 0 ? Integer.parseInt(args[0]) : 8080;
|
||||||
|
|
||||||
|
new HttpServer(port).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() throws Exception {
|
||||||
|
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||||
|
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||||
|
try {
|
||||||
|
ServerBootstrap b = new ServerBootstrap();
|
||||||
|
b.group(bossGroup, workerGroup)
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.handler(new LoggingHandler(LogLevel.INFO))
|
||||||
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ChannelPipeline p = ch.pipeline();
|
||||||
|
p.addLast(new HttpRequestDecoder());
|
||||||
|
p.addLast(new HttpResponseEncoder());
|
||||||
|
p.addLast(new CustomHttpServerHandler());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ChannelFuture f = b.bind(port)
|
||||||
|
.sync();
|
||||||
|
f.channel()
|
||||||
|
.closeFuture()
|
||||||
|
.sync();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
bossGroup.shutdownGracefully();
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package com.baeldung.http.server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.handler.codec.DecoderResult;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
class ResponseBuilder {
|
||||||
|
|
||||||
|
static StringBuilder addRequestAttributes(HttpRequest request) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
responseData.append("Version: ")
|
||||||
|
.append(request.protocolVersion())
|
||||||
|
.append("\r\n");
|
||||||
|
responseData.append("Host: ")
|
||||||
|
.append(request.headers()
|
||||||
|
.get(HttpHeaderNames.HOST, "unknown"))
|
||||||
|
.append("\r\n");
|
||||||
|
responseData.append("URI: ")
|
||||||
|
.append(request.uri())
|
||||||
|
.append("\r\n\r\n");
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringBuilder addParams(HttpRequest request) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
|
||||||
|
Map<String, List<String>> params = queryStringDecoder.parameters();
|
||||||
|
if (!params.isEmpty()) {
|
||||||
|
for (Entry<String, List<String>> p : params.entrySet()) {
|
||||||
|
String key = p.getKey();
|
||||||
|
List<String> vals = p.getValue();
|
||||||
|
for (String val : vals) {
|
||||||
|
responseData.append("Parameter: ")
|
||||||
|
.append(key)
|
||||||
|
.append(" = ")
|
||||||
|
.append(val)
|
||||||
|
.append("\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseData.append("\r\n");
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringBuilder addHeaders(HttpRequest request) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
HttpHeaders headers = request.headers();
|
||||||
|
if (!headers.isEmpty()) {
|
||||||
|
for (Map.Entry<String, String> header : headers) {
|
||||||
|
CharSequence key = header.getKey();
|
||||||
|
CharSequence value = header.getValue();
|
||||||
|
responseData.append(key)
|
||||||
|
.append(" = ")
|
||||||
|
.append(value)
|
||||||
|
.append("\r\n");
|
||||||
|
}
|
||||||
|
responseData.append("\r\n");
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringBuilder addBody(HttpContent httpContent) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
ByteBuf content = httpContent.content();
|
||||||
|
if (content.isReadable()) {
|
||||||
|
responseData.append(content.toString(CharsetUtil.UTF_8)
|
||||||
|
.toUpperCase());
|
||||||
|
responseData.append("\r\n");
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringBuilder addDecoderResult(HttpObject o) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
DecoderResult result = o.decoderResult();
|
||||||
|
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
responseData.append("..Decoder Failure: ");
|
||||||
|
responseData.append(result.cause());
|
||||||
|
responseData.append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringBuilder addLastResponse(HttpRequest request, LastHttpContent trailer) {
|
||||||
|
StringBuilder responseData = new StringBuilder();
|
||||||
|
responseData.append("Good Bye!\r\n");
|
||||||
|
|
||||||
|
if (!trailer.trailingHeaders()
|
||||||
|
.isEmpty()) {
|
||||||
|
responseData.append("\r\n");
|
||||||
|
for (CharSequence name : trailer.trailingHeaders()
|
||||||
|
.names()) {
|
||||||
|
for (CharSequence value : trailer.trailingHeaders()
|
||||||
|
.getAll(name)) {
|
||||||
|
responseData.append("P.S. Trailing Header: ");
|
||||||
|
responseData.append(name)
|
||||||
|
.append(" = ")
|
||||||
|
.append(value)
|
||||||
|
.append("\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseData.append("\r\n");
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
package com.baeldung.http.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpClientCodec;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
|
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
|
||||||
|
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
//Ensure the server class - HttpServer.java is already started before running this test
|
||||||
|
public class HttpServerLiveTest {
|
||||||
|
|
||||||
|
private static final String HOST = "127.0.0.1";
|
||||||
|
private static final int PORT = 8080;
|
||||||
|
private Channel channel;
|
||||||
|
private EventLoopGroup group = new NioEventLoopGroup();
|
||||||
|
ResponseAggregator response = new ResponseAggregator();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
Bootstrap b = new Bootstrap();
|
||||||
|
b.group(group)
|
||||||
|
.channel(NioSocketChannel.class)
|
||||||
|
.handler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ChannelPipeline p = ch.pipeline();
|
||||||
|
p.addLast(new HttpClientCodec());
|
||||||
|
p.addLast(new HttpContentDecompressor());
|
||||||
|
p.addLast(new SimpleChannelInboundHandler<HttpObject>() {
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||||
|
response = prepareResponse(ctx, msg, response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
channel = b.connect(HOST, PORT)
|
||||||
|
.sync()
|
||||||
|
.channel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenPostSent_thenContentReceivedInUppercase() throws Exception {
|
||||||
|
String body = "Hello World!";
|
||||||
|
|
||||||
|
DefaultFullHttpRequest request = createRequest(body);
|
||||||
|
|
||||||
|
channel.writeAndFlush(request);
|
||||||
|
Thread.sleep(200);
|
||||||
|
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
assertEquals("HTTP/1.1", response.getVersion());
|
||||||
|
|
||||||
|
assertTrue(response.getContent()
|
||||||
|
.contains(body.toUpperCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenGetSent_thenCookieReceivedInResponse() throws Exception {
|
||||||
|
DefaultFullHttpRequest request = createRequest(null);
|
||||||
|
|
||||||
|
channel.writeAndFlush(request);
|
||||||
|
Thread.sleep(200);
|
||||||
|
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
assertEquals("HTTP/1.1", response.getVersion());
|
||||||
|
|
||||||
|
Map<String, String> headers = response.getHeaders();
|
||||||
|
String cookies = headers.get("set-cookie");
|
||||||
|
assertTrue(cookies.contains("my-cookie"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() throws InterruptedException {
|
||||||
|
channel.closeFuture()
|
||||||
|
.sync();
|
||||||
|
group.shutdownGracefully();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultFullHttpRequest createRequest(final CharSequence body) throws Exception {
|
||||||
|
DefaultFullHttpRequest request;
|
||||||
|
if (body != null) {
|
||||||
|
request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
|
||||||
|
request.content()
|
||||||
|
.writeBytes(body.toString()
|
||||||
|
.getBytes(CharsetUtil.UTF_8.name()));
|
||||||
|
request.headers()
|
||||||
|
.set(HttpHeaderNames.CONTENT_TYPE, "application/json");
|
||||||
|
request.headers()
|
||||||
|
.set(HttpHeaderNames.CONTENT_LENGTH, request.content()
|
||||||
|
.readableBytes());
|
||||||
|
} else {
|
||||||
|
request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER);
|
||||||
|
request.headers()
|
||||||
|
.set(HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(new DefaultCookie("my-cookie", "foo")));
|
||||||
|
}
|
||||||
|
|
||||||
|
request.headers()
|
||||||
|
.set(HttpHeaderNames.HOST, HOST);
|
||||||
|
request.headers()
|
||||||
|
.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResponseAggregator prepareResponse(ChannelHandlerContext ctx, HttpObject msg, ResponseAggregator responseAgg) {
|
||||||
|
|
||||||
|
if (msg instanceof HttpResponse) {
|
||||||
|
HttpResponse response = (HttpResponse) msg;
|
||||||
|
|
||||||
|
responseAgg.setStatus(response.status()
|
||||||
|
.code());
|
||||||
|
|
||||||
|
responseAgg.setVersion(response.protocolVersion()
|
||||||
|
.text());
|
||||||
|
|
||||||
|
if (!response.headers()
|
||||||
|
.isEmpty()) {
|
||||||
|
Map<String, String> headers = new HashMap<String, String>();
|
||||||
|
for (CharSequence name : response.headers()
|
||||||
|
.names()) {
|
||||||
|
for (CharSequence value : response.headers()
|
||||||
|
.getAll(name)) {
|
||||||
|
headers.put(name.toString(), value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseAgg.setHeaders(headers);
|
||||||
|
}
|
||||||
|
if (HttpUtil.isTransferEncodingChunked(response)) {
|
||||||
|
responseAgg.setChunked(true);
|
||||||
|
} else {
|
||||||
|
responseAgg.setChunked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg instanceof HttpContent) {
|
||||||
|
HttpContent content = (HttpContent) msg;
|
||||||
|
String responseData = content.content()
|
||||||
|
.toString(CharsetUtil.UTF_8);
|
||||||
|
|
||||||
|
if (content instanceof LastHttpContent) {
|
||||||
|
responseAgg.setContent(responseData + "} End Of Content");
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responseAgg;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.baeldung.http.server;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ResponseAggregator {
|
||||||
|
int status;
|
||||||
|
String version;
|
||||||
|
Map<String, String> headers;
|
||||||
|
boolean isChunked;
|
||||||
|
String content;
|
||||||
|
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaders(Map<String, String> headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChunked() {
|
||||||
|
return isChunked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChunked(boolean isChunked) {
|
||||||
|
this.isChunked = isChunked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue