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