http_server.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
1// Copyright (c) 2012 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/server/http_server.h"
6
7#include "base/compiler_specific.h"
8#include "base/logging.h"
9#include "base/stl_util.h"
10#include "base/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/sys_byteorder.h"
13#include "build/build_config.h"
14#include "net/server/http_connection.h"
15#include "net/server/http_server_request_info.h"
16#include "net/server/web_socket.h"
17#include "net/socket/tcp_listen_socket.h"
18
19namespace net {
20
21HttpServer::HttpServer(const StreamListenSocketFactory& factory,
22                       HttpServer::Delegate* delegate)
23    : delegate_(delegate),
24      server_(factory.CreateAndListen(this)) {
25  DCHECK(server_.get());
26}
27
28void HttpServer::AcceptWebSocket(
29    int connection_id,
30    const HttpServerRequestInfo& request) {
31  HttpConnection* connection = FindConnection(connection_id);
32  if (connection == NULL)
33    return;
34
35  DCHECK(connection->web_socket_.get());
36  connection->web_socket_->Accept(request);
37}
38
39void HttpServer::SendOverWebSocket(int connection_id,
40                                   const std::string& data) {
41  HttpConnection* connection = FindConnection(connection_id);
42  if (connection == NULL)
43    return;
44  DCHECK(connection->web_socket_.get());
45  connection->web_socket_->Send(data);
46}
47
48void HttpServer::Send(int connection_id,
49                      HttpStatusCode status_code,
50                      const std::string& data,
51                      const std::string& content_type) {
52  HttpConnection* connection = FindConnection(connection_id);
53  if (connection == NULL)
54    return;
55  connection->Send(status_code, data, content_type);
56}
57
58void HttpServer::Send200(int connection_id,
59                         const std::string& data,
60                         const std::string& content_type) {
61  Send(connection_id, HTTP_OK, data, content_type);
62}
63
64void HttpServer::Send404(int connection_id) {
65  Send(connection_id, HTTP_NOT_FOUND, std::string(), "text/html");
66}
67
68void HttpServer::Send500(int connection_id, const std::string& message) {
69  Send(connection_id, HTTP_INTERNAL_SERVER_ERROR, message, "text/html");
70}
71
72void HttpServer::Close(int connection_id) {
73  HttpConnection* connection = FindConnection(connection_id);
74  if (connection == NULL)
75    return;
76
77  // Initiating close from server-side does not lead to the DidClose call.
78  // Do it manually here.
79  DidClose(connection->socket_.get());
80}
81
82int HttpServer::GetLocalAddress(IPEndPoint* address) {
83  return server_->GetLocalAddress(address);
84}
85
86void HttpServer::DidAccept(StreamListenSocket* server,
87                           StreamListenSocket* socket) {
88  HttpConnection* connection = new HttpConnection(this, socket);
89  id_to_connection_[connection->id()] = connection;
90  socket_to_connection_[socket] = connection;
91}
92
93void HttpServer::DidRead(StreamListenSocket* socket,
94                         const char* data,
95                         int len) {
96  HttpConnection* connection = FindConnection(socket);
97  DCHECK(connection != NULL);
98  if (connection == NULL)
99    return;
100
101  connection->recv_data_.append(data, len);
102  while (connection->recv_data_.length()) {
103    if (connection->web_socket_.get()) {
104      std::string message;
105      WebSocket::ParseResult result = connection->web_socket_->Read(&message);
106      if (result == WebSocket::FRAME_INCOMPLETE)
107        break;
108
109      if (result == WebSocket::FRAME_CLOSE ||
110          result == WebSocket::FRAME_ERROR) {
111        Close(connection->id());
112        break;
113      }
114      delegate_->OnWebSocketMessage(connection->id(), message);
115      continue;
116    }
117
118    HttpServerRequestInfo request;
119    size_t pos = 0;
120    if (!ParseHeaders(connection, &request, &pos))
121      break;
122
123    std::string connection_header = request.GetHeaderValue("Connection");
124    if (connection_header == "Upgrade") {
125      connection->web_socket_.reset(WebSocket::CreateWebSocket(connection,
126                                                               request,
127                                                               &pos));
128
129      if (!connection->web_socket_.get())  // Not enought data was received.
130        break;
131      delegate_->OnWebSocketRequest(connection->id(), request);
132      connection->Shift(pos);
133      continue;
134    }
135    // Request body is not supported. It is always empty.
136    delegate_->OnHttpRequest(connection->id(), request);
137    connection->Shift(pos);
138  }
139}
140
141void HttpServer::DidClose(StreamListenSocket* socket) {
142  HttpConnection* connection = FindConnection(socket);
143  DCHECK(connection != NULL);
144  id_to_connection_.erase(connection->id());
145  socket_to_connection_.erase(connection->socket_.get());
146  delete connection;
147}
148
149HttpServer::~HttpServer() {
150  STLDeleteContainerPairSecondPointers(
151      id_to_connection_.begin(), id_to_connection_.end());
152  server_ = NULL;
153}
154
155//
156// HTTP Request Parser
157// This HTTP request parser uses a simple state machine to quickly parse
158// through the headers.  The parser is not 100% complete, as it is designed
159// for use in this simple test driver.
160//
161// Known issues:
162//   - does not handle whitespace on first HTTP line correctly.  Expects
163//     a single space between the method/url and url/protocol.
164
165// Input character types.
166enum header_parse_inputs {
167  INPUT_SPACE,
168  INPUT_CR,
169  INPUT_LF,
170  INPUT_COLON,
171  INPUT_DEFAULT,
172  MAX_INPUTS,
173};
174
175// Parser states.
176enum header_parse_states {
177  ST_METHOD,     // Receiving the method
178  ST_URL,        // Receiving the URL
179  ST_PROTO,      // Receiving the protocol
180  ST_HEADER,     // Starting a Request Header
181  ST_NAME,       // Receiving a request header name
182  ST_SEPARATOR,  // Receiving the separator between header name and value
183  ST_VALUE,      // Receiving a request header value
184  ST_DONE,       // Parsing is complete and successful
185  ST_ERR,        // Parsing encountered invalid syntax.
186  MAX_STATES
187};
188
189// State transition table
190int parser_state[MAX_STATES][MAX_INPUTS] = {
191/* METHOD    */ { ST_URL,       ST_ERR,     ST_ERR,   ST_ERR,       ST_METHOD },
192/* URL       */ { ST_PROTO,     ST_ERR,     ST_ERR,   ST_URL,       ST_URL },
193/* PROTOCOL  */ { ST_ERR,       ST_HEADER,  ST_NAME,  ST_ERR,       ST_PROTO },
194/* HEADER    */ { ST_ERR,       ST_ERR,     ST_NAME,  ST_ERR,       ST_ERR },
195/* NAME      */ { ST_SEPARATOR, ST_DONE,    ST_ERR,   ST_SEPARATOR, ST_NAME },
196/* SEPARATOR */ { ST_SEPARATOR, ST_ERR,     ST_ERR,   ST_SEPARATOR, ST_VALUE },
197/* VALUE     */ { ST_VALUE,     ST_HEADER,  ST_NAME,  ST_VALUE,     ST_VALUE },
198/* DONE      */ { ST_DONE,      ST_DONE,    ST_DONE,  ST_DONE,      ST_DONE },
199/* ERR       */ { ST_ERR,       ST_ERR,     ST_ERR,   ST_ERR,       ST_ERR }
200};
201
202// Convert an input character to the parser's input token.
203int charToInput(char ch) {
204  switch(ch) {
205    case ' ':
206      return INPUT_SPACE;
207    case '\r':
208      return INPUT_CR;
209    case '\n':
210      return INPUT_LF;
211    case ':':
212      return INPUT_COLON;
213  }
214  return INPUT_DEFAULT;
215}
216
217bool HttpServer::ParseHeaders(HttpConnection* connection,
218                              HttpServerRequestInfo* info,
219                              size_t* ppos) {
220  size_t& pos = *ppos;
221  size_t data_len = connection->recv_data_.length();
222  int state = ST_METHOD;
223  std::string buffer;
224  std::string header_name;
225  std::string header_value;
226  while (pos < data_len) {
227    char ch = connection->recv_data_[pos++];
228    int input = charToInput(ch);
229    int next_state = parser_state[state][input];
230
231    bool transition = (next_state != state);
232    if (transition) {
233      // Do any actions based on state transitions.
234      switch (state) {
235        case ST_METHOD:
236          info->method = buffer;
237          buffer.clear();
238          break;
239        case ST_URL:
240          info->path = buffer;
241          buffer.clear();
242          break;
243        case ST_PROTO:
244          // TODO(mbelshe): Deal better with parsing protocol.
245          DCHECK(buffer == "HTTP/1.1");
246          buffer.clear();
247          break;
248        case ST_NAME:
249          header_name = buffer;
250          buffer.clear();
251          break;
252        case ST_VALUE:
253          header_value = buffer;
254          // TODO(mbelshe): Deal better with duplicate headers
255          DCHECK(info->headers.find(header_name) == info->headers.end());
256          info->headers[header_name] = header_value;
257          buffer.clear();
258          break;
259        case ST_SEPARATOR:
260          buffer.append(&ch, 1);
261          break;
262      }
263      state = next_state;
264    } else {
265      // Do any actions based on current state
266      switch (state) {
267        case ST_METHOD:
268        case ST_URL:
269        case ST_PROTO:
270        case ST_VALUE:
271        case ST_NAME:
272          buffer.append(&ch, 1);
273          break;
274        case ST_DONE:
275          DCHECK(input == INPUT_LF);
276          return true;
277        case ST_ERR:
278          return false;
279      }
280    }
281  }
282  // No more characters, but we haven't finished parsing yet.
283  return false;
284}
285
286HttpConnection* HttpServer::FindConnection(int connection_id) {
287  IdToConnectionMap::iterator it = id_to_connection_.find(connection_id);
288  if (it == id_to_connection_.end())
289    return NULL;
290  return it->second;
291}
292
293HttpConnection* HttpServer::FindConnection(StreamListenSocket* socket) {
294  SocketToConnectionMap::iterator it = socket_to_connection_.find(socket);
295  if (it == socket_to_connection_.end())
296    return NULL;
297  return it->second;
298}
299
300}  // namespace net
301