1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.example.http.snoop;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.channel.ChannelFutureListeners;
20 import io.netty5.channel.ChannelHandlerContext;
21 import io.netty5.channel.SimpleChannelInboundHandler;
22 import io.netty5.handler.codec.DecoderResult;
23 import io.netty5.handler.codec.http.DefaultFullHttpResponse;
24 import io.netty5.handler.codec.http.FullHttpResponse;
25 import io.netty5.handler.codec.http.HttpContent;
26 import io.netty5.handler.codec.http.HttpHeaderNames;
27 import io.netty5.handler.codec.http.HttpHeaderValues;
28 import io.netty5.handler.codec.http.HttpHeaders;
29 import io.netty5.handler.codec.http.HttpObject;
30 import io.netty5.handler.codec.http.HttpRequest;
31 import io.netty5.handler.codec.http.HttpUtil;
32 import io.netty5.handler.codec.http.LastHttpContent;
33 import io.netty5.handler.codec.http.QueryStringDecoder;
34 import io.netty5.handler.codec.http.cookie.Cookie;
35 import io.netty5.handler.codec.http.cookie.ServerCookieDecoder;
36 import io.netty5.handler.codec.http.cookie.ServerCookieEncoder;
37 import io.netty5.util.CharsetUtil;
38
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Map.Entry;
42 import java.util.Set;
43
44 import static io.netty5.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
45 import static io.netty5.handler.codec.http.HttpResponseStatus.CONTINUE;
46 import static io.netty5.handler.codec.http.HttpResponseStatus.OK;
47 import static io.netty5.handler.codec.http.HttpVersion.HTTP_1_1;
48 import static java.nio.charset.StandardCharsets.UTF_8;
49
50 public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object> {
51
52 private HttpRequest request;
53
54 private final StringBuilder buf = new StringBuilder();
55
56 @Override
57 public void channelReadComplete(ChannelHandlerContext ctx) {
58 ctx.flush();
59 }
60
61 @Override
62 protected void messageReceived(ChannelHandlerContext ctx, Object msg) {
63 if (msg instanceof HttpRequest) {
64 HttpRequest request = this.request = (HttpRequest) msg;
65
66 if (HttpUtil.is100ContinueExpected(request)) {
67 send100Continue(ctx);
68 }
69
70 buf.setLength(0);
71 buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
72 buf.append("===================================\r\n");
73
74 buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
75 buf.append("HOSTNAME: ").append(request.headers().get(HttpHeaderNames.HOST, "unknown")).append("\r\n");
76 buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");
77
78 HttpHeaders headers = request.headers();
79 if (!headers.isEmpty()) {
80 for (Map.Entry<String, String> h: headers) {
81 CharSequence key = h.getKey();
82 CharSequence value = h.getValue();
83 buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
84 }
85 buf.append("\r\n");
86 }
87
88 QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
89 Map<String, List<String>> params = queryStringDecoder.parameters();
90 if (!params.isEmpty()) {
91 for (Entry<String, List<String>> p: params.entrySet()) {
92 String key = p.getKey();
93 List<String> vals = p.getValue();
94 for (String val : vals) {
95 buf.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n");
96 }
97 }
98 buf.append("\r\n");
99 }
100
101 appendDecoderResult(buf, request);
102 }
103
104 if (msg instanceof HttpContent) {
105 HttpContent<?> httpContent = (HttpContent<?>) msg;
106
107 Buffer content = httpContent.payload();
108 if (content.readableBytes() > 0) {
109 buf.append("CONTENT: ");
110 buf.append(content.toString(CharsetUtil.UTF_8));
111 buf.append("\r\n");
112 appendDecoderResult(buf, request);
113 }
114
115 if (msg instanceof LastHttpContent) {
116 buf.append("END OF CONTENT\r\n");
117
118 LastHttpContent<?> trailer = (LastHttpContent<?>) msg;
119 if (!trailer.trailingHeaders().isEmpty()) {
120 buf.append("\r\n");
121 for (CharSequence name: trailer.trailingHeaders().names()) {
122 for (CharSequence value: trailer.trailingHeaders().getAll(name)) {
123 buf.append("TRAILING HEADER: ");
124 buf.append(name).append(" = ").append(value).append("\r\n");
125 }
126 }
127 buf.append("\r\n");
128 }
129
130 if (!writeResponse(trailer, ctx)) {
131
132 ctx.writeAndFlush(ctx.bufferAllocator().allocate(0)).addListener(ctx, ChannelFutureListeners.CLOSE);
133 }
134 }
135 }
136 }
137
138 private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
139 DecoderResult result = o.decoderResult();
140 if (result.isSuccess()) {
141 return;
142 }
143
144 buf.append(".. WITH DECODER FAILURE: ");
145 buf.append(result.cause());
146 buf.append("\r\n");
147 }
148
149 private boolean writeResponse(HttpObject currentObj, ChannelHandlerContext ctx) {
150
151 boolean keepAlive = HttpUtil.isKeepAlive(request);
152
153 FullHttpResponse response = new DefaultFullHttpResponse(
154 HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
155 ctx.bufferAllocator().copyOf(buf.toString(), UTF_8));
156
157 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
158
159 if (keepAlive) {
160
161 response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.payload().readableBytes());
162
163
164 response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
165 }
166
167
168 String cookieString = request.headers().get(HttpHeaderNames.COOKIE);
169 if (cookieString != null) {
170 Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString);
171 if (!cookies.isEmpty()) {
172
173 for (Cookie cookie: cookies) {
174 response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
175 }
176 }
177 } else {
178
179 response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("key1", "value1"));
180 response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("key2", "value2"));
181 }
182
183
184 ctx.write(response);
185
186 return keepAlive;
187 }
188
189 private static void send100Continue(ChannelHandlerContext ctx) {
190 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE,
191 ctx.bufferAllocator().allocate(0));
192 ctx.write(response);
193 }
194
195 @Override
196 public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
197 cause.printStackTrace();
198 ctx.close();
199 }
200 }