View Javadoc

1   /*
2    * Copyright 2009 Red Hat, Inc.
3    *
4    * Red Hat licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the
6    * 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 static org.jboss.netty.handler.codec.http.HttpHeaders.*;
19  import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
20  import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
21  import static org.jboss.netty.handler.codec.http.HttpVersion.*;
22  
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Map.Entry;
26  import java.util.Set;
27  
28  import org.jboss.netty.buffer.ChannelBuffer;
29  import org.jboss.netty.buffer.ChannelBuffers;
30  import org.jboss.netty.channel.ChannelFuture;
31  import org.jboss.netty.channel.ChannelFutureListener;
32  import org.jboss.netty.channel.ChannelHandlerContext;
33  import org.jboss.netty.channel.ExceptionEvent;
34  import org.jboss.netty.channel.MessageEvent;
35  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
36  import org.jboss.netty.handler.codec.http.Cookie;
37  import org.jboss.netty.handler.codec.http.CookieDecoder;
38  import org.jboss.netty.handler.codec.http.CookieEncoder;
39  import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
40  import org.jboss.netty.handler.codec.http.HttpChunk;
41  import org.jboss.netty.handler.codec.http.HttpChunkTrailer;
42  import org.jboss.netty.handler.codec.http.HttpRequest;
43  import org.jboss.netty.handler.codec.http.HttpResponse;
44  import org.jboss.netty.handler.codec.http.QueryStringDecoder;
45  import org.jboss.netty.util.CharsetUtil;
46  
47  /**
48   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
49   * @author Andy Taylor (andy.taylor@jboss.org)
50   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
51   *
52   * @version $Rev: 2368 $, $Date: 2010-10-18 17:19:03 +0900 (Mon, 18 Oct 2010) $
53   */
54  public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
55  
56      private HttpRequest request;
57      private boolean readingChunks;
58      /** Buffer that stores the response content */
59      private final StringBuilder buf = new StringBuilder();
60  
61      @Override
62      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
63          if (!readingChunks) {
64              HttpRequest request = this.request = (HttpRequest) e.getMessage();
65  
66              if (is100ContinueExpected(request)) {
67                  send100Continue(e);
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: " + request.getProtocolVersion() + "\r\n");
75              buf.append("HOSTNAME: " + getHost(request, "unknown") + "\r\n");
76              buf.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n");
77  
78              for (Map.Entry<String, String> h: request.getHeaders()) {
79                  buf.append("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n");
80              }
81              buf.append("\r\n");
82  
83              QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
84              Map<String, List<String>> params = queryStringDecoder.getParameters();
85              if (!params.isEmpty()) {
86                  for (Entry<String, List<String>> p: params.entrySet()) {
87                      String key = p.getKey();
88                      List<String> vals = p.getValue();
89                      for (String val : vals) {
90                          buf.append("PARAM: " + key + " = " + val + "\r\n");
91                      }
92                  }
93                  buf.append("\r\n");
94              }
95  
96              if (request.isChunked()) {
97                  readingChunks = true;
98              } else {
99                  ChannelBuffer content = request.getContent();
100                 if (content.readable()) {
101                     buf.append("CONTENT: " + content.toString(CharsetUtil.UTF_8) + "\r\n");
102                 }
103                 writeResponse(e);
104             }
105         } else {
106             HttpChunk chunk = (HttpChunk) e.getMessage();
107             if (chunk.isLast()) {
108                 readingChunks = false;
109                 buf.append("END OF CONTENT\r\n");
110 
111                 HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
112                 if (!trailer.getHeaderNames().isEmpty()) {
113                     buf.append("\r\n");
114                     for (String name: trailer.getHeaderNames()) {
115                         for (String value: trailer.getHeaders(name)) {
116                             buf.append("TRAILING HEADER: " + name + " = " + value + "\r\n");
117                         }
118                     }
119                     buf.append("\r\n");
120                 }
121 
122                 writeResponse(e);
123             } else {
124                 buf.append("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) + "\r\n");
125             }
126         }
127     }
128 
129     private void writeResponse(MessageEvent e) {
130         // Decide whether to close the connection or not.
131         boolean keepAlive = isKeepAlive(request);
132 
133         // Build the response object.
134         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
135         response.setContent(ChannelBuffers.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
136         response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
137 
138         if (keepAlive) {
139             // Add 'Content-Length' header only for a keep-alive connection.
140             response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
141         }
142 
143         // Encode the cookie.
144         String cookieString = request.getHeader(COOKIE);
145         if (cookieString != null) {
146             CookieDecoder cookieDecoder = new CookieDecoder();
147             Set<Cookie> cookies = cookieDecoder.decode(cookieString);
148             if(!cookies.isEmpty()) {
149                 // Reset the cookies if necessary.
150                 CookieEncoder cookieEncoder = new CookieEncoder(true);
151                 for (Cookie cookie : cookies) {
152                     cookieEncoder.addCookie(cookie);
153                 }
154                 response.addHeader(SET_COOKIE, cookieEncoder.encode());
155             }
156         }
157 
158         // Write the response.
159         ChannelFuture future = e.getChannel().write(response);
160 
161         // Close the non-keep-alive connection after the write operation is done.
162         if (!keepAlive) {
163             future.addListener(ChannelFutureListener.CLOSE);
164         }
165     }
166 
167     private void send100Continue(MessageEvent e) {
168         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
169         e.getChannel().write(response);
170     }
171 
172     @Override
173     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
174             throws Exception {
175         e.getCause().printStackTrace();
176         e.getChannel().close();
177     }
178 }