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