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