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