ebfc4513e0
Please note that the build will fail at the moment due to various checkstyle violations which should be fixed soon
174 lines
6.6 KiB
Java
174 lines
6.6 KiB
Java
/*
|
|
* Copyright 2011 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.example.http.snoop;
|
|
|
|
import static io.netty.handler.codec.http.HttpHeaders.*;
|
|
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
|
|
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
|
|
import io.netty.buffer.ChannelBuffer;
|
|
import io.netty.buffer.ChannelBuffers;
|
|
import io.netty.channel.ChannelFuture;
|
|
import io.netty.channel.ChannelFutureListener;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ExceptionEvent;
|
|
import io.netty.channel.MessageEvent;
|
|
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
import io.netty.handler.codec.http.Cookie;
|
|
import io.netty.handler.codec.http.CookieDecoder;
|
|
import io.netty.handler.codec.http.CookieEncoder;
|
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
|
import io.netty.handler.codec.http.HttpChunk;
|
|
import io.netty.handler.codec.http.HttpChunkTrailer;
|
|
import io.netty.handler.codec.http.HttpRequest;
|
|
import io.netty.handler.codec.http.HttpResponse;
|
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
|
import io.netty.util.CharsetUtil;
|
|
|
|
/**
|
|
*/
|
|
public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
|
|
|
|
private HttpRequest request;
|
|
private boolean readingChunks;
|
|
/** Buffer that stores the response content */
|
|
private final StringBuilder buf = new StringBuilder();
|
|
|
|
@Override
|
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
|
if (!readingChunks) {
|
|
HttpRequest request = this.request = (HttpRequest) e.getMessage();
|
|
|
|
if (is100ContinueExpected(request)) {
|
|
send100Continue(e);
|
|
}
|
|
|
|
buf.setLength(0);
|
|
buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
|
|
buf.append("===================================\r\n");
|
|
|
|
buf.append("VERSION: " + request.getProtocolVersion() + "\r\n");
|
|
buf.append("HOSTNAME: " + getHost(request, "unknown") + "\r\n");
|
|
buf.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n");
|
|
|
|
for (Map.Entry<String, String> h: request.getHeaders()) {
|
|
buf.append("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n");
|
|
}
|
|
buf.append("\r\n");
|
|
|
|
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
|
|
Map<String, List<String>> params = queryStringDecoder.getParameters();
|
|
if (!params.isEmpty()) {
|
|
for (Entry<String, List<String>> p: params.entrySet()) {
|
|
String key = p.getKey();
|
|
List<String> vals = p.getValue();
|
|
for (String val : vals) {
|
|
buf.append("PARAM: " + key + " = " + val + "\r\n");
|
|
}
|
|
}
|
|
buf.append("\r\n");
|
|
}
|
|
|
|
if (request.isChunked()) {
|
|
readingChunks = true;
|
|
} else {
|
|
ChannelBuffer content = request.getContent();
|
|
if (content.readable()) {
|
|
buf.append("CONTENT: " + content.toString(CharsetUtil.UTF_8) + "\r\n");
|
|
}
|
|
writeResponse(e);
|
|
}
|
|
} else {
|
|
HttpChunk chunk = (HttpChunk) e.getMessage();
|
|
if (chunk.isLast()) {
|
|
readingChunks = false;
|
|
buf.append("END OF CONTENT\r\n");
|
|
|
|
HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
|
|
if (!trailer.getHeaderNames().isEmpty()) {
|
|
buf.append("\r\n");
|
|
for (String name: trailer.getHeaderNames()) {
|
|
for (String value: trailer.getHeaders(name)) {
|
|
buf.append("TRAILING HEADER: " + name + " = " + value + "\r\n");
|
|
}
|
|
}
|
|
buf.append("\r\n");
|
|
}
|
|
|
|
writeResponse(e);
|
|
} else {
|
|
buf.append("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) + "\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void writeResponse(MessageEvent e) {
|
|
// Decide whether to close the connection or not.
|
|
boolean keepAlive = isKeepAlive(request);
|
|
|
|
// Build the response object.
|
|
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
|
response.setContent(ChannelBuffers.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
|
|
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
|
|
|
if (keepAlive) {
|
|
// Add 'Content-Length' header only for a keep-alive connection.
|
|
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
|
|
}
|
|
|
|
// Encode the cookie.
|
|
String cookieString = request.getHeader(COOKIE);
|
|
if (cookieString != null) {
|
|
CookieDecoder cookieDecoder = new CookieDecoder();
|
|
Set<Cookie> cookies = cookieDecoder.decode(cookieString);
|
|
if (!cookies.isEmpty()) {
|
|
// Reset the cookies if necessary.
|
|
CookieEncoder cookieEncoder = new CookieEncoder(true);
|
|
for (Cookie cookie : cookies) {
|
|
cookieEncoder.addCookie(cookie);
|
|
}
|
|
response.addHeader(SET_COOKIE, cookieEncoder.encode());
|
|
}
|
|
}
|
|
|
|
// Write the response.
|
|
ChannelFuture future = e.getChannel().write(response);
|
|
|
|
// Close the non-keep-alive connection after the write operation is done.
|
|
if (!keepAlive) {
|
|
future.addListener(ChannelFutureListener.CLOSE);
|
|
}
|
|
}
|
|
|
|
private void send100Continue(MessageEvent e) {
|
|
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
|
|
e.getChannel().write(response);
|
|
}
|
|
|
|
@Override
|
|
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
|
|
throws Exception {
|
|
e.getCause().printStackTrace();
|
|
e.getChannel().close();
|
|
}
|
|
}
|