Home | History | Annotate | Download | only in fetch
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/tools/fetch/http_listen_socket.h"
      6 
      7 #include <map>
      8 
      9 #include "base/compiler_specific.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop.h"
     12 #include "base/string_number_conversions.h"
     13 #include "net/tools/fetch/http_server_request_info.h"
     14 #include "net/tools/fetch/http_server_response_info.h"
     15 
     16 // must run in the IO thread
     17 HttpListenSocket::HttpListenSocket(SOCKET s,
     18                                    HttpListenSocket::Delegate* delegate)
     19     : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)),
     20       delegate_(delegate) {
     21 }
     22 
     23 // must run in the IO thread
     24 HttpListenSocket::~HttpListenSocket() {
     25 }
     26 
     27 void HttpListenSocket::Listen() {
     28   ListenSocket::Listen();
     29 }
     30 
     31 void HttpListenSocket::Accept() {
     32   SOCKET conn = ListenSocket::Accept(socket_);
     33   DCHECK_NE(conn, ListenSocket::kInvalidSocket);
     34   if (conn == ListenSocket::kInvalidSocket) {
     35     // TODO
     36   } else {
     37     scoped_refptr<HttpListenSocket> sock(
     38         new HttpListenSocket(conn, delegate_));
     39     // it's up to the delegate to AddRef if it wants to keep it around
     40     DidAccept(this, sock);
     41   }
     42 }
     43 
     44 HttpListenSocket* HttpListenSocket::Listen(
     45     const std::string& ip,
     46     int port,
     47     HttpListenSocket::Delegate* delegate) {
     48   SOCKET s = ListenSocket::Listen(ip, port);
     49   if (s == ListenSocket::kInvalidSocket) {
     50     // TODO (ibrar): error handling
     51   } else {
     52     HttpListenSocket *serv = new HttpListenSocket(s, delegate);
     53     serv->Listen();
     54     return serv;
     55   }
     56   return NULL;
     57 }
     58 
     59 //
     60 // HTTP Request Parser
     61 // This HTTP request parser uses a simple state machine to quickly parse
     62 // through the headers.  The parser is not 100% complete, as it is designed
     63 // for use in this simple test driver.
     64 //
     65 // Known issues:
     66 //   - does not handle whitespace on first HTTP line correctly.  Expects
     67 //     a single space between the method/url and url/protocol.
     68 
     69 // Input character types.
     70 enum header_parse_inputs {
     71   INPUT_SPACE,
     72   INPUT_CR,
     73   INPUT_LF,
     74   INPUT_COLON,
     75   INPUT_DEFAULT,
     76   MAX_INPUTS
     77 };
     78 
     79 // Parser states.
     80 enum header_parse_states {
     81   ST_METHOD,     // Receiving the method
     82   ST_URL,        // Receiving the URL
     83   ST_PROTO,      // Receiving the protocol
     84   ST_HEADER,     // Starting a Request Header
     85   ST_NAME,       // Receiving a request header name
     86   ST_SEPARATOR,  // Receiving the separator between header name and value
     87   ST_VALUE,      // Receiving a request header value
     88   ST_DONE,       // Parsing is complete and successful
     89   ST_ERR,        // Parsing encountered invalid syntax.
     90   MAX_STATES
     91 };
     92 
     93 // State transition table
     94 int parser_state[MAX_STATES][MAX_INPUTS] = {
     95 /* METHOD    */ { ST_URL,       ST_ERR,     ST_ERR,  ST_ERR,       ST_METHOD },
     96 /* URL       */ { ST_PROTO,     ST_ERR,     ST_ERR,  ST_URL,       ST_URL },
     97 /* PROTOCOL  */ { ST_ERR,       ST_HEADER,  ST_NAME, ST_ERR,       ST_PROTO },
     98 /* HEADER    */ { ST_ERR,       ST_ERR,     ST_NAME, ST_ERR,       ST_ERR },
     99 /* NAME      */ { ST_SEPARATOR, ST_DONE,    ST_ERR,  ST_SEPARATOR, ST_NAME },
    100 /* SEPARATOR */ { ST_SEPARATOR, ST_ERR,     ST_ERR,  ST_SEPARATOR, ST_VALUE },
    101 /* VALUE     */ { ST_VALUE,     ST_HEADER,  ST_NAME, ST_VALUE,     ST_VALUE },
    102 /* DONE      */ { ST_DONE,      ST_DONE,    ST_DONE, ST_DONE,      ST_DONE },
    103 /* ERR       */ { ST_ERR,       ST_ERR,     ST_ERR,  ST_ERR,       ST_ERR }
    104 };
    105 
    106 // Convert an input character to the parser's input token.
    107 int charToInput(char ch) {
    108   switch(ch) {
    109     case ' ':
    110       return INPUT_SPACE;
    111     case '\r':
    112       return INPUT_CR;
    113     case '\n':
    114       return INPUT_LF;
    115     case ':':
    116       return INPUT_COLON;
    117   }
    118   return INPUT_DEFAULT;
    119 }
    120 
    121 HttpServerRequestInfo* HttpListenSocket::ParseHeaders() {
    122   int pos = 0;
    123   int data_len = recv_data_.length();
    124   int state = ST_METHOD;
    125   HttpServerRequestInfo* info = new HttpServerRequestInfo();
    126   std::string buffer;
    127   std::string header_name;
    128   std::string header_value;
    129   while (pos < data_len) {
    130     char ch = recv_data_[pos++];
    131     int input = charToInput(ch);
    132     int next_state = parser_state[state][input];
    133 
    134     bool transition = (next_state != state);
    135     if (transition) {
    136       // Do any actions based on state transitions.
    137       switch (state) {
    138         case ST_METHOD:
    139           info->method = buffer;
    140           buffer.clear();
    141           break;
    142         case ST_URL:
    143           info->url = GURL(buffer);
    144           buffer.clear();
    145           break;
    146         case ST_PROTO:
    147           // TODO(mbelshe): Deal better with parsing protocol.
    148           DCHECK(buffer == "HTTP/1.1");
    149           buffer.clear();
    150           break;
    151         case ST_NAME:
    152           header_name = buffer;
    153           buffer.clear();
    154           break;
    155         case ST_VALUE:
    156           header_value = buffer;
    157           // TODO(mbelshe): Deal better with duplicate headers
    158           DCHECK(info->headers.find(header_name) == info->headers.end());
    159           info->headers[header_name] = header_value;
    160           buffer.clear();
    161           break;
    162       }
    163       state = next_state;
    164     } else {
    165       // Do any actions based on current state
    166       switch (state) {
    167         case ST_METHOD:
    168         case ST_URL:
    169         case ST_PROTO:
    170         case ST_VALUE:
    171         case ST_NAME:
    172           buffer.append(&ch, 1);
    173           break;
    174         case ST_DONE:
    175           recv_data_ = recv_data_.substr(pos);
    176           return info;
    177         case ST_ERR:
    178           delete info;
    179           return NULL;
    180       }
    181     }
    182   }
    183   // No more characters, but we haven't finished parsing yet.
    184   delete info;
    185   return NULL;
    186 }
    187 
    188 void HttpListenSocket::DidAccept(ListenSocket* server,
    189                                  ListenSocket* connection) {
    190   connection->AddRef();
    191 }
    192 
    193 void HttpListenSocket::DidRead(ListenSocket* connection,
    194                                const char* data,
    195                                int len) {
    196   recv_data_.append(data, len);
    197   while (recv_data_.length()) {
    198     HttpServerRequestInfo* request = ParseHeaders();
    199     if (!request)
    200       break;
    201     delegate_->OnRequest(this, request);
    202     delete request;
    203   }
    204 }
    205 
    206 void HttpListenSocket::DidClose(ListenSocket* sock) {
    207   sock->Release();
    208 }
    209 
    210 // Convert the numeric status code to a string.
    211 // e.g.  200 -> "200 OK"
    212 std::string ServerStatus(int code) {
    213   switch(code) {
    214     case 200:
    215       return std::string("200 OK");
    216     // TODO(mbelshe): handle other codes.
    217   }
    218   NOTREACHED();
    219   return std::string();
    220 }
    221 
    222 void HttpListenSocket::Respond(HttpServerResponseInfo* info,
    223                                std::string& data) {
    224   std::string response;
    225 
    226   // status line
    227   response = info->protocol + " ";
    228   response += ServerStatus(info->status);
    229   response += "\r\n";
    230 
    231   // standard headers
    232   if (info->content_type.length())
    233     response += "Content-type: " + info->content_type + "\r\n";
    234 
    235   if (info->content_length > 0)
    236     response += "Content-length: " + base::IntToString(info->content_length) +
    237         "\r\n";
    238 
    239   if (info->connection_close)
    240     response += "Connection: close\r\n";
    241 
    242   // TODO(mbelshe): support additional headers
    243 
    244   // End of headers
    245   response += "\r\n";
    246 
    247   // Add data
    248   response += data;
    249 
    250   // Write it all out.
    251   this->Send(response, false);
    252 }
    253