View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.example.http.snoop;
17  
18  import org.jboss.netty.buffer.ChannelBuffer;
19  import org.jboss.netty.buffer.ChannelBuffers;
20  import org.jboss.netty.channel.ChannelFuture;
21  import org.jboss.netty.channel.ChannelFutureListener;
22  import org.jboss.netty.channel.ChannelHandlerContext;
23  import org.jboss.netty.channel.ExceptionEvent;
24  import org.jboss.netty.channel.MessageEvent;
25  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
26  import org.jboss.netty.handler.codec.http.Cookie;
27  import org.jboss.netty.handler.codec.http.CookieDecoder;
28  import org.jboss.netty.handler.codec.http.CookieEncoder;
29  import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
30  import org.jboss.netty.handler.codec.http.HttpChunk;
31  import org.jboss.netty.handler.codec.http.HttpChunkTrailer;
32  import org.jboss.netty.handler.codec.http.HttpHeaders;
33  import org.jboss.netty.handler.codec.http.HttpRequest;
34  import org.jboss.netty.handler.codec.http.HttpResponse;
35  import org.jboss.netty.handler.codec.http.QueryStringDecoder;
36  import org.jboss.netty.util.CharsetUtil;
37  
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  import java.util.Set;
42  
43  import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
44  import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
45  import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
46  import static org.jboss.netty.handler.codec.http.HttpVersion.*;
47  
48  public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
49  
50      private HttpRequest request;
51      private boolean readingChunks;
52      /** Buffer that stores the response content */
53      private final StringBuilder buf = new StringBuilder();
54  
55      @Override
56      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
57          if (!readingChunks) {
58              HttpRequest request = this.request = (HttpRequest) e.getMessage();
59  
60              if (is100ContinueExpected(request)) {
61                  send100Continue(e);
62              }
63  
64              buf.setLength(0);
65              buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
66              buf.append("===================================\r\n");
67  
68              buf.append("VERSION: " + request.getProtocolVersion() + "\r\n");
69              buf.append("HOSTNAME: " + getHost(request, "unknown") + "\r\n");
70              buf.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n");
71  
72              for (Map.Entry<String, String> h: request.headers()) {
73                  buf.append("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n");
74              }
75              buf.append("\r\n");
76  
77              QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
78              Map<String, List<String>> params = queryStringDecoder.getParameters();
79              if (!params.isEmpty()) {
80                  for (Entry<String, List<String>> p: params.entrySet()) {
81                      String key = p.getKey();
82                      List<String> vals = p.getValue();
83                      for (String val : vals) {
84                          buf.append("PARAM: " + key + " = " + val + "\r\n");
85                      }
86                  }
87                  buf.append("\r\n");
88              }
89  
90              if (request.isChunked()) {
91                  readingChunks = true;
92              } else {
93                  ChannelBuffer content = request.getContent();
94                  if (content.readable()) {
95                      buf.append("CONTENT: " + content.toString(CharsetUtil.UTF_8) + "\r\n");
96                  }
97                  writeResponse(e);
98              }
99          } else {
100             HttpChunk chunk = (HttpChunk) e.getMessage();
101             if (chunk.isLast()) {
102                 readingChunks = false;
103                 buf.append("END OF CONTENT\r\n");
104 
105                 HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
106                 if (!trailer.trailingHeaders().names().isEmpty()) {
107                     buf.append("\r\n");
108                     for (String name: trailer.trailingHeaders().names()) {
109                         for (String value: trailer.trailingHeaders().getAll(name)) {
110                             buf.append("TRAILING HEADER: " + name + " = " + value + "\r\n");
111                         }
112                     }
113                     buf.append("\r\n");
114                 }
115 
116                 writeResponse(e);
117             } else {
118                 buf.append("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) + "\r\n");
119             }
120         }
121     }
122 
123     private void writeResponse(MessageEvent e) {
124         // Decide whether to close the connection or not.
125         boolean keepAlive = isKeepAlive(request);
126 
127         // Build the response object.
128         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
129         response.setContent(ChannelBuffers.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
130         response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
131 
132         if (keepAlive) {
133             // Add 'Content-Length' header only for a keep-alive connection.
134             response.headers().set(CONTENT_LENGTH, response.getContent().readableBytes());
135             // Add keep alive header as per:
136             // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
137             response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
138         }
139 
140         // Encode the cookie.
141         String cookieString = request.headers().get(COOKIE);
142         if (cookieString != null) {
143             CookieDecoder cookieDecoder = new CookieDecoder();
144             Set<Cookie> cookies = cookieDecoder.decode(cookieString);
145             if (!cookies.isEmpty()) {
146                 // Reset the cookies if necessary.
147                 CookieEncoder cookieEncoder = new CookieEncoder(true);
148                 for (Cookie cookie : cookies) {
149                     cookieEncoder.addCookie(cookie);
150                     response.headers().add(SET_COOKIE, cookieEncoder.encode());
151                 }
152             }
153         } else {
154             // Browser sent no cookie.  Add some.
155             CookieEncoder cookieEncoder = new CookieEncoder(true);
156             cookieEncoder.addCookie("key1", "value1");
157             response.headers().add(SET_COOKIE, cookieEncoder.encode());
158             cookieEncoder.addCookie("key2", "value2");
159             response.headers().add(SET_COOKIE, cookieEncoder.encode());
160         }
161 
162         // Write the response.
163         ChannelFuture future = e.getChannel().write(response);
164 
165         // Close the non-keep-alive connection after the write operation is done.
166         if (!keepAlive) {
167             future.addListener(ChannelFutureListener.CLOSE);
168         }
169     }
170 
171     private static void send100Continue(MessageEvent e) {
172         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
173         e.getChannel().write(response);
174     }
175 
176     @Override
177     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
178         e.getCause().printStackTrace();
179         e.getChannel().close();
180     }
181 }