http_listen_socket.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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
14HttpListenSocket::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
21HttpListenSocket::~HttpListenSocket() {
22}
23
24void 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
37HttpListenSocket* 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.
61enum 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.
71enum 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
85int 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.
98int 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
112HttpServerRequestInfo* 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
179void HttpListenSocket::DidAccept(ListenSocket* server,
180                                 ListenSocket* connection) {
181  connection->AddRef();
182}
183
184void 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
196void HttpListenSocket::DidClose(ListenSocket* sock) {
197  sock->Release();
198}
199
200// Convert the numeric status code to a string.
201// e.g.  200 -> "200 OK"
202std::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
212void 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