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 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.HttpHeaders;
    43  import org.jboss.netty.handler.codec.http.HttpRequest;
    44  import org.jboss.netty.handler.codec.http.HttpResponse;
    45  import org.jboss.netty.handler.codec.http.QueryStringDecoder;
    46  import org.jboss.netty.util.CharsetUtil;
    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) throws Exception {
    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.getHeaders()) {
    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.getHeaderNames().isEmpty()) {
    107                     buf.append("\r\n");
    108                     for (String name: trailer.getHeaderNames()) {
    109                         for (String value: trailer.getHeaders(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.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
    131
    132         if (keepAlive) {
    133             // Add 'Content-Length' header only for a keep-alive connection.
    134             response.setHeader(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.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    138         }
    139
    140         // Encode the cookie.
    141         String cookieString = request.getHeader(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.addHeader(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.addHeader(SET_COOKIE, cookieEncoder.encode());
    158             cookieEncoder.addCookie("key2", "value2");
    159             response.addHeader(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             throws Exception {
    179         e.getCause().printStackTrace();
    180         e.getChannel().close();
    181     }
    182 }