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