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::Listen() {
28  ListenSocket::Listen();
29}
30
31void 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
44HttpListenSocket* 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.
70enum 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.
80enum 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
94int 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.
107int 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
121HttpServerRequestInfo* 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
188void HttpListenSocket::DidAccept(ListenSocket* server,
189                                 ListenSocket* connection) {
190  connection->AddRef();
191}
192
193void 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
206void HttpListenSocket::DidClose(ListenSocket* sock) {
207  sock->Release();
208}
209
210// Convert the numeric status code to a string.
211// e.g.  200 -> "200 OK"
212std::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
222void 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