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.
5 #include "net/server/http_server.h"
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"
24 HttpServer::HttpServer(const StreamListenSocketFactory
& factory
,
25 HttpServer::Delegate
* delegate
)
26 : delegate_(delegate
),
27 server_(factory
.CreateAndListen(this)) {
30 void HttpServer::AcceptWebSocket(
32 const HttpServerRequestInfo
& request
) {
33 HttpConnection
* connection
= FindConnection(connection_id
);
34 if (connection
== NULL
)
37 DCHECK(connection
->web_socket_
.get());
38 connection
->web_socket_
->Accept(request
);
41 void HttpServer::SendOverWebSocket(int connection_id
,
42 const std::string
& data
) {
43 HttpConnection
* connection
= FindConnection(connection_id
);
44 if (connection
== NULL
)
46 DCHECK(connection
->web_socket_
.get());
47 connection
->web_socket_
->Send(data
);
50 void HttpServer::SendRaw(int connection_id
, const std::string
& data
) {
51 HttpConnection
* connection
= FindConnection(connection_id
);
52 if (connection
== NULL
)
54 connection
->Send(data
);
57 void HttpServer::SendResponse(int connection_id
,
58 const HttpServerResponseInfo
& response
) {
59 HttpConnection
* connection
= FindConnection(connection_id
);
60 if (connection
== NULL
)
62 connection
->Send(response
);
65 void HttpServer::Send(int connection_id
,
66 HttpStatusCode status_code
,
67 const std::string
& data
,
68 const std::string
& content_type
) {
69 HttpServerResponseInfo
response(status_code
);
70 response
.SetBody(data
, content_type
);
71 SendResponse(connection_id
, response
);
74 void HttpServer::Send200(int connection_id
,
75 const std::string
& data
,
76 const std::string
& content_type
) {
77 Send(connection_id
, HTTP_OK
, data
, content_type
);
80 void HttpServer::Send404(int connection_id
) {
81 SendResponse(connection_id
, HttpServerResponseInfo::CreateFor404());
84 void HttpServer::Send500(int connection_id
, const std::string
& message
) {
85 SendResponse(connection_id
, HttpServerResponseInfo::CreateFor500(message
));
88 void HttpServer::Close(int connection_id
) {
89 HttpConnection
* connection
= FindConnection(connection_id
);
90 if (connection
== NULL
)
93 // Initiating close from server-side does not lead to the DidClose call.
94 // Do it manually here.
95 DidClose(connection
->socket_
.get());
98 int HttpServer::GetLocalAddress(IPEndPoint
* address
) {
100 return ERR_SOCKET_NOT_CONNECTED
;
101 return server_
->GetLocalAddress(address
);
104 void HttpServer::DidAccept(StreamListenSocket
* server
,
105 scoped_ptr
<StreamListenSocket
> socket
) {
106 HttpConnection
* connection
= new HttpConnection(this, socket
.Pass());
107 id_to_connection_
[connection
->id()] = connection
;
108 // TODO(szym): Fix socket access. Make HttpConnection the Delegate.
109 socket_to_connection_
[connection
->socket_
.get()] = connection
;
112 void HttpServer::DidRead(StreamListenSocket
* socket
,
115 HttpConnection
* connection
= FindConnection(socket
);
116 DCHECK(connection
!= NULL
);
117 if (connection
== NULL
)
120 connection
->recv_data_
.append(data
, len
);
121 while (connection
->recv_data_
.length()) {
122 if (connection
->web_socket_
.get()) {
124 WebSocket::ParseResult result
= connection
->web_socket_
->Read(&message
);
125 if (result
== WebSocket::FRAME_INCOMPLETE
)
128 if (result
== WebSocket::FRAME_CLOSE
||
129 result
== WebSocket::FRAME_ERROR
) {
130 Close(connection
->id());
133 delegate_
->OnWebSocketMessage(connection
->id(), message
);
137 HttpServerRequestInfo request
;
139 if (!ParseHeaders(connection
, &request
, &pos
))
142 // Sets peer address if exists.
143 socket
->GetPeerAddress(&request
.peer
);
145 std::string connection_header
= request
.GetHeaderValue("connection");
146 if (connection_header
== "Upgrade") {
147 connection
->web_socket_
.reset(WebSocket::CreateWebSocket(connection
,
151 if (!connection
->web_socket_
.get()) // Not enough data was received.
153 delegate_
->OnWebSocketRequest(connection
->id(), request
);
154 connection
->Shift(pos
);
158 const char kContentLength
[] = "content-length";
159 if (request
.headers
.count(kContentLength
)) {
160 size_t content_length
= 0;
161 const size_t kMaxBodySize
= 100 << 20;
162 if (!base::StringToSizeT(request
.GetHeaderValue(kContentLength
),
164 content_length
> kMaxBodySize
) {
165 connection
->Send(HttpServerResponseInfo::CreateFor500(
166 "request content-length too big or unknown: " +
167 request
.GetHeaderValue(kContentLength
)));
172 if (connection
->recv_data_
.length() - pos
< content_length
)
173 break; // Not enough data was received yet.
174 request
.data
= connection
->recv_data_
.substr(pos
, content_length
);
175 pos
+= content_length
;
178 delegate_
->OnHttpRequest(connection
->id(), request
);
179 connection
->Shift(pos
);
183 void HttpServer::DidClose(StreamListenSocket
* socket
) {
184 HttpConnection
* connection
= FindConnection(socket
);
185 DCHECK(connection
!= NULL
);
186 id_to_connection_
.erase(connection
->id());
187 socket_to_connection_
.erase(connection
->socket_
.get());
191 HttpServer::~HttpServer() {
192 STLDeleteContainerPairSecondPointers(
193 id_to_connection_
.begin(), id_to_connection_
.end());
197 // HTTP Request Parser
198 // This HTTP request parser uses a simple state machine to quickly parse
199 // through the headers. The parser is not 100% complete, as it is designed
200 // for use in this simple test driver.
203 // - does not handle whitespace on first HTTP line correctly. Expects
204 // a single space between the method/url and url/protocol.
206 // Input character types.
207 enum header_parse_inputs
{
217 enum header_parse_states
{
218 ST_METHOD
, // Receiving the method
219 ST_URL
, // Receiving the URL
220 ST_PROTO
, // Receiving the protocol
221 ST_HEADER
, // Starting a Request Header
222 ST_NAME
, // Receiving a request header name
223 ST_SEPARATOR
, // Receiving the separator between header name and value
224 ST_VALUE
, // Receiving a request header value
225 ST_DONE
, // Parsing is complete and successful
226 ST_ERR
, // Parsing encountered invalid syntax.
230 // State transition table
231 int parser_state
[MAX_STATES
][MAX_INPUTS
] = {
232 /* METHOD */ { ST_URL
, ST_ERR
, ST_ERR
, ST_ERR
, ST_METHOD
},
233 /* URL */ { ST_PROTO
, ST_ERR
, ST_ERR
, ST_URL
, ST_URL
},
234 /* PROTOCOL */ { ST_ERR
, ST_HEADER
, ST_NAME
, ST_ERR
, ST_PROTO
},
235 /* HEADER */ { ST_ERR
, ST_ERR
, ST_NAME
, ST_ERR
, ST_ERR
},
236 /* NAME */ { ST_SEPARATOR
, ST_DONE
, ST_ERR
, ST_VALUE
, ST_NAME
},
237 /* SEPARATOR */ { ST_SEPARATOR
, ST_ERR
, ST_ERR
, ST_VALUE
, ST_ERR
},
238 /* VALUE */ { ST_VALUE
, ST_HEADER
, ST_NAME
, ST_VALUE
, ST_VALUE
},
239 /* DONE */ { ST_DONE
, ST_DONE
, ST_DONE
, ST_DONE
, ST_DONE
},
240 /* ERR */ { ST_ERR
, ST_ERR
, ST_ERR
, ST_ERR
, ST_ERR
}
243 // Convert an input character to the parser's input token.
244 int charToInput(char ch
) {
255 return INPUT_DEFAULT
;
258 bool HttpServer::ParseHeaders(HttpConnection
* connection
,
259 HttpServerRequestInfo
* info
,
262 size_t data_len
= connection
->recv_data_
.length();
263 int state
= ST_METHOD
;
265 std::string header_name
;
266 std::string header_value
;
267 while (pos
< data_len
) {
268 char ch
= connection
->recv_data_
[pos
++];
269 int input
= charToInput(ch
);
270 int next_state
= parser_state
[state
][input
];
272 bool transition
= (next_state
!= state
);
274 // Do any actions based on state transitions.
277 info
->method
= buffer
;
285 // TODO(mbelshe): Deal better with parsing protocol.
286 DCHECK(buffer
== "HTTP/1.1");
290 header_name
= StringToLowerASCII(buffer
);
294 base::TrimWhitespaceASCII(buffer
, base::TRIM_LEADING
, &header_value
);
295 // TODO(mbelshe): Deal better with duplicate headers
296 DCHECK(info
->headers
.find(header_name
) == info
->headers
.end());
297 info
->headers
[header_name
] = header_value
;
305 // Do any actions based on current state
312 buffer
.append(&ch
, 1);
315 DCHECK(input
== INPUT_LF
);
322 // No more characters, but we haven't finished parsing yet.
326 HttpConnection
* HttpServer::FindConnection(int connection_id
) {
327 IdToConnectionMap::iterator it
= id_to_connection_
.find(connection_id
);
328 if (it
== id_to_connection_
.end())
333 HttpConnection
* HttpServer::FindConnection(StreamListenSocket
* socket
) {
334 SocketToConnectionMap::iterator it
= socket_to_connection_
.find(socket
);
335 if (it
== socket_to_connection_
.end())