JAVA-15787 Created new di-modules and server-modules
- Moved spring-freemarker to spring-web-modules
This commit is contained in:
+97
@@ -0,0 +1,97 @@
|
||||
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 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.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(RequestUtils.formatParams(request));
|
||||
}
|
||||
|
||||
responseData.append(RequestUtils.evaluateDecoderResult(request));
|
||||
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent httpContent = (HttpContent) msg;
|
||||
|
||||
responseData.append(RequestUtils.formatBody(httpContent));
|
||||
responseData.append(RequestUtils.evaluateDecoderResult(request));
|
||||
|
||||
if (msg instanceof LastHttpContent) {
|
||||
LastHttpContent trailer = (LastHttpContent) msg;
|
||||
responseData.append(RequestUtils.prepareLastResponse(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);
|
||||
}
|
||||
|
||||
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,86 @@
|
||||
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.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 RequestUtils {
|
||||
|
||||
static StringBuilder formatParams(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.toUpperCase())
|
||||
.append(" = ")
|
||||
.append(val.toUpperCase())
|
||||
.append("\r\n");
|
||||
}
|
||||
}
|
||||
responseData.append("\r\n");
|
||||
}
|
||||
return responseData;
|
||||
}
|
||||
|
||||
static StringBuilder formatBody(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 evaluateDecoderResult(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 prepareLastResponse(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,135 @@
|
||||
package com.baeldung.netty.http2;
|
||||
|
||||
import static io.netty.handler.logging.LogLevel.INFO;
|
||||
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import com.baeldung.netty.http2.client.Http2ClientResponseHandler;
|
||||
import com.baeldung.netty.http2.client.Http2SettingsHandler;
|
||||
import com.baeldung.netty.http2.server.Http2ServerResponseHandler;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
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.HttpScheme;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2Connection;
|
||||
import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
|
||||
import io.netty.handler.codec.http2.Http2Connection;
|
||||
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
|
||||
import io.netty.handler.codec.http2.Http2FrameLogger;
|
||||
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||
import io.netty.handler.codec.http2.HttpConversionUtil;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
||||
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNames;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
public class Http2Util {
|
||||
public static SslContext createSSLContext(boolean isServer) throws SSLException, CertificateException {
|
||||
|
||||
SslContext sslCtx;
|
||||
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
|
||||
if (isServer) {
|
||||
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(SslProvider.JDK)
|
||||
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN,
|
||||
SelectorFailureBehavior.NO_ADVERTISE,
|
||||
SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2, ApplicationProtocolNames.HTTP_1_1))
|
||||
.build();
|
||||
} else {
|
||||
sslCtx = SslContextBuilder.forClient()
|
||||
.sslProvider(SslProvider.JDK)
|
||||
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN,
|
||||
SelectorFailureBehavior.NO_ADVERTISE,
|
||||
SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2))
|
||||
.build();
|
||||
}
|
||||
return sslCtx;
|
||||
|
||||
}
|
||||
|
||||
public static ApplicationProtocolNegotiationHandler getServerAPNHandler() {
|
||||
ApplicationProtocolNegotiationHandler serverAPNHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_2) {
|
||||
|
||||
@Override
|
||||
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
|
||||
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
|
||||
ctx.pipeline()
|
||||
.addLast(Http2FrameCodecBuilder.forServer()
|
||||
.build(), new Http2ServerResponseHandler());
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Protocol: " + protocol + " not supported");
|
||||
}
|
||||
};
|
||||
return serverAPNHandler;
|
||||
|
||||
}
|
||||
|
||||
public static ApplicationProtocolNegotiationHandler getClientAPNHandler(int maxContentLength, Http2SettingsHandler settingsHandler, Http2ClientResponseHandler responseHandler) {
|
||||
final Http2FrameLogger logger = new Http2FrameLogger(INFO, Http2Util.class);
|
||||
final Http2Connection connection = new DefaultHttp2Connection(false);
|
||||
|
||||
HttpToHttp2ConnectionHandler connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
|
||||
.frameListener(new DelegatingDecompressorFrameListener(connection, new InboundHttp2ToHttpAdapterBuilder(connection).maxContentLength(maxContentLength)
|
||||
.propagateSettings(true)
|
||||
.build()))
|
||||
.frameLogger(logger)
|
||||
.connection(connection)
|
||||
.build();
|
||||
|
||||
ApplicationProtocolNegotiationHandler clientAPNHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_2) {
|
||||
@Override
|
||||
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
|
||||
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
|
||||
ChannelPipeline p = ctx.pipeline();
|
||||
p.addLast(connectionHandler);
|
||||
p.addLast(settingsHandler, responseHandler);
|
||||
return;
|
||||
}
|
||||
ctx.close();
|
||||
throw new IllegalStateException("Protocol: " + protocol + " not supported");
|
||||
}
|
||||
};
|
||||
|
||||
return clientAPNHandler;
|
||||
|
||||
}
|
||||
|
||||
public static FullHttpRequest createGetRequest(String host, int port) {
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.valueOf("HTTP/2.0"), HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER);
|
||||
request.headers()
|
||||
.add(HttpHeaderNames.HOST, new String(host + ":" + port));
|
||||
request.headers()
|
||||
.add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), HttpScheme.HTTPS);
|
||||
request.headers()
|
||||
.add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);
|
||||
request.headers()
|
||||
.add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.netty.http2.client;
|
||||
|
||||
import com.baeldung.netty.http2.Http2Util;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
|
||||
public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final SslContext sslCtx;
|
||||
private final int maxContentLength;
|
||||
private Http2SettingsHandler settingsHandler;
|
||||
private Http2ClientResponseHandler responseHandler;
|
||||
private String host;
|
||||
private int port;
|
||||
|
||||
public Http2ClientInitializer(SslContext sslCtx, int maxContentLength, String host, int port) {
|
||||
this.sslCtx = sslCtx;
|
||||
this.maxContentLength = maxContentLength;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
|
||||
settingsHandler = new Http2SettingsHandler(ch.newPromise());
|
||||
responseHandler = new Http2ClientResponseHandler();
|
||||
|
||||
if (sslCtx != null) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(sslCtx.newHandler(ch.alloc(), host, port));
|
||||
pipeline.addLast(Http2Util.getClientAPNHandler(maxContentLength, settingsHandler, responseHandler));
|
||||
}
|
||||
}
|
||||
|
||||
public Http2SettingsHandler getSettingsHandler() {
|
||||
return settingsHandler;
|
||||
}
|
||||
|
||||
public Http2ClientResponseHandler getResponseHandler() {
|
||||
return responseHandler;
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package com.baeldung.netty.http2.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http2.HttpConversionUtil;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class Http2ClientResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Http2ClientResponseHandler.class);
|
||||
private final Map<Integer, MapValues> streamidMap;
|
||||
|
||||
public Http2ClientResponseHandler() {
|
||||
streamidMap = new HashMap<Integer, MapValues>();
|
||||
}
|
||||
|
||||
public MapValues put(int streamId, ChannelFuture writeFuture, ChannelPromise promise) {
|
||||
return streamidMap.put(streamId, new MapValues(writeFuture, promise));
|
||||
}
|
||||
|
||||
public String awaitResponses(long timeout, TimeUnit unit) {
|
||||
|
||||
Iterator<Entry<Integer, MapValues>> itr = streamidMap.entrySet()
|
||||
.iterator();
|
||||
|
||||
String response = null;
|
||||
|
||||
while (itr.hasNext()) {
|
||||
Entry<Integer, MapValues> entry = itr.next();
|
||||
ChannelFuture writeFuture = entry.getValue()
|
||||
.getWriteFuture();
|
||||
|
||||
if (!writeFuture.awaitUninterruptibly(timeout, unit)) {
|
||||
throw new IllegalStateException("Timed out waiting to write for stream id " + entry.getKey());
|
||||
}
|
||||
if (!writeFuture.isSuccess()) {
|
||||
throw new RuntimeException(writeFuture.cause());
|
||||
}
|
||||
ChannelPromise promise = entry.getValue()
|
||||
.getPromise();
|
||||
|
||||
if (!promise.awaitUninterruptibly(timeout, unit)) {
|
||||
throw new IllegalStateException("Timed out waiting for response on stream id " + entry.getKey());
|
||||
}
|
||||
if (!promise.isSuccess()) {
|
||||
throw new RuntimeException(promise.cause());
|
||||
}
|
||||
logger.info("---Stream id: " + entry.getKey() + " received---");
|
||||
response = entry.getValue().getResponse();
|
||||
|
||||
itr.remove();
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
||||
Integer streamId = msg.headers()
|
||||
.getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
|
||||
if (streamId == null) {
|
||||
logger.error("HttpResponseHandler unexpected message received: " + msg);
|
||||
return;
|
||||
}
|
||||
|
||||
MapValues value = streamidMap.get(streamId);
|
||||
|
||||
if (value == null) {
|
||||
logger.error("Message received for unknown stream id " + streamId);
|
||||
ctx.close();
|
||||
} else {
|
||||
ByteBuf content = msg.content();
|
||||
if (content.isReadable()) {
|
||||
int contentLength = content.readableBytes();
|
||||
byte[] arr = new byte[contentLength];
|
||||
content.readBytes(arr);
|
||||
String response = new String(arr, 0, contentLength, CharsetUtil.UTF_8);
|
||||
logger.info("Response from Server: "+ (response));
|
||||
value.setResponse(response);
|
||||
}
|
||||
|
||||
value.getPromise()
|
||||
.setSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapValues {
|
||||
ChannelFuture writeFuture;
|
||||
ChannelPromise promise;
|
||||
String response;
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(String response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public MapValues(ChannelFuture writeFuture2, ChannelPromise promise2) {
|
||||
this.writeFuture = writeFuture2;
|
||||
this.promise = promise2;
|
||||
}
|
||||
|
||||
public ChannelFuture getWriteFuture() {
|
||||
return writeFuture;
|
||||
}
|
||||
|
||||
public ChannelPromise getPromise() {
|
||||
return promise;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.baeldung.netty.http2.client;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http2.Http2Settings;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Http2SettingsHandler extends SimpleChannelInboundHandler<Http2Settings> {
|
||||
private final ChannelPromise promise;
|
||||
|
||||
public Http2SettingsHandler(ChannelPromise promise) {
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
public void awaitSettings(long timeout, TimeUnit unit) throws Exception {
|
||||
if (!promise.awaitUninterruptibly(timeout, unit)) {
|
||||
throw new IllegalStateException("Timed out waiting for settings");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Http2Settings msg) throws Exception {
|
||||
promise.setSuccess();
|
||||
|
||||
ctx.pipeline()
|
||||
.remove(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.baeldung.netty.http2.server;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baeldung.netty.http2.Http2Util;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
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.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
|
||||
public final class Http2Server {
|
||||
|
||||
private static final int PORT = 8443;
|
||||
private static final Logger logger = LoggerFactory.getLogger(Http2Server.class);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SslContext sslCtx = Http2Util.createSSLContext(true);
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.option(ChannelOption.SO_BACKLOG, 1024);
|
||||
b.group(group)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
if (sslCtx != null) {
|
||||
ch.pipeline()
|
||||
.addLast(sslCtx.newHandler(ch.alloc()), Http2Util.getServerAPNHandler());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Channel ch = b.bind(PORT)
|
||||
.sync()
|
||||
.channel();
|
||||
|
||||
logger.info("HTTP/2 Server is listening on https://127.0.0.1:" + PORT + '/');
|
||||
|
||||
ch.closeFuture()
|
||||
.sync();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.baeldung.netty.http2.server;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
|
||||
import io.netty.handler.codec.http2.Http2Headers;
|
||||
import io.netty.handler.codec.http2.Http2HeadersFrame;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
@Sharable
|
||||
public class Http2ServerResponseHandler extends ChannelDuplexHandler {
|
||||
|
||||
static final ByteBuf RESPONSE_BYTES = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8));
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof Http2HeadersFrame) {
|
||||
Http2HeadersFrame msgHeader = (Http2HeadersFrame) msg;
|
||||
if (msgHeader.isEndStream()) {
|
||||
ByteBuf content = ctx.alloc()
|
||||
.buffer();
|
||||
content.writeBytes(RESPONSE_BYTES.duplicate());
|
||||
|
||||
Http2Headers headers = new DefaultHttp2Headers().status(HttpResponseStatus.OK.codeAsText());
|
||||
ctx.write(new DefaultHttp2HeadersFrame(headers).stream(msgHeader.stream()));
|
||||
ctx.write(new DefaultHttp2DataFrame(content, true).stream(msgHeader.stream()));
|
||||
}
|
||||
|
||||
} else {
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user