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/server_socket.h"
21 #include "net/socket/stream_socket.h"
22 #include "net/socket/tcp_server_socket.h"
26 HttpServer::HttpServer(scoped_ptr
<ServerSocket
> server_socket
,
27 HttpServer::Delegate
* delegate
)
28 : server_socket_(server_socket
.Pass()),
31 weak_ptr_factory_(this) {
32 DCHECK(server_socket_
);
36 HttpServer::~HttpServer() {
37 STLDeleteContainerPairSecondPointers(
38 id_to_connection_
.begin(), id_to_connection_
.end());
41 void HttpServer::AcceptWebSocket(
43 const HttpServerRequestInfo
& request
) {
44 HttpConnection
* connection
= FindConnection(connection_id
);
45 if (connection
== NULL
)
47 DCHECK(connection
->web_socket());
48 connection
->web_socket()->Accept(request
);
51 void HttpServer::SendOverWebSocket(int connection_id
,
52 const std::string
& data
) {
53 HttpConnection
* connection
= FindConnection(connection_id
);
54 if (connection
== NULL
)
56 DCHECK(connection
->web_socket());
57 connection
->web_socket()->Send(data
);
60 void HttpServer::SendRaw(int connection_id
, const std::string
& data
) {
61 HttpConnection
* connection
= FindConnection(connection_id
);
62 if (connection
== NULL
)
65 bool writing_in_progress
= !connection
->write_buf()->IsEmpty();
66 if (connection
->write_buf()->Append(data
) && !writing_in_progress
)
67 DoWriteLoop(connection
);
70 void HttpServer::SendResponse(int connection_id
,
71 const HttpServerResponseInfo
& response
) {
72 SendRaw(connection_id
, response
.Serialize());
75 void HttpServer::Send(int connection_id
,
76 HttpStatusCode status_code
,
77 const std::string
& data
,
78 const std::string
& content_type
) {
79 HttpServerResponseInfo
response(status_code
);
80 response
.SetContentHeaders(data
.size(), content_type
);
81 SendResponse(connection_id
, response
);
82 SendRaw(connection_id
, data
);
85 void HttpServer::Send200(int connection_id
,
86 const std::string
& data
,
87 const std::string
& content_type
) {
88 Send(connection_id
, HTTP_OK
, data
, content_type
);
91 void HttpServer::Send404(int connection_id
) {
92 SendResponse(connection_id
, HttpServerResponseInfo::CreateFor404());
95 void HttpServer::Send500(int connection_id
, const std::string
& message
) {
96 SendResponse(connection_id
, HttpServerResponseInfo::CreateFor500(message
));
99 void HttpServer::Close(int connection_id
) {
100 HttpConnection
* connection
= FindConnection(connection_id
);
101 if (connection
== NULL
)
104 id_to_connection_
.erase(connection_id
);
105 delegate_
->OnClose(connection_id
);
107 // The call stack might have callbacks which still have the pointer of
108 // connection. Instead of referencing connection with ID all the time,
109 // destroys the connection in next run loop to make sure any pending
110 // callbacks in the call stack return.
111 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE
, connection
);
114 int HttpServer::GetLocalAddress(IPEndPoint
* address
) {
115 return server_socket_
->GetLocalAddress(address
);
118 void HttpServer::SetReceiveBufferSize(int connection_id
, int32 size
) {
119 HttpConnection
* connection
= FindConnection(connection_id
);
121 connection
->read_buf()->set_max_buffer_size(size
);
124 void HttpServer::SetSendBufferSize(int connection_id
, int32 size
) {
125 HttpConnection
* connection
= FindConnection(connection_id
);
127 connection
->write_buf()->set_max_buffer_size(size
);
130 void HttpServer::DoAcceptLoop() {
133 rv
= server_socket_
->Accept(&accepted_socket_
,
134 base::Bind(&HttpServer::OnAcceptCompleted
,
135 weak_ptr_factory_
.GetWeakPtr()));
136 if (rv
== ERR_IO_PENDING
)
138 rv
= HandleAcceptResult(rv
);
142 void HttpServer::OnAcceptCompleted(int rv
) {
143 if (HandleAcceptResult(rv
) == OK
)
147 int HttpServer::HandleAcceptResult(int rv
) {
149 LOG(ERROR
) << "Accept error: rv=" << rv
;
153 HttpConnection
* connection
=
154 new HttpConnection(++last_id_
, accepted_socket_
.Pass());
155 id_to_connection_
[connection
->id()] = connection
;
156 DoReadLoop(connection
);
160 void HttpServer::DoReadLoop(HttpConnection
* connection
) {
163 HttpConnection::ReadIOBuffer
* read_buf
= connection
->read_buf();
164 // Increases read buffer size if necessary.
165 if (read_buf
->RemainingCapacity() == 0 && !read_buf
->IncreaseCapacity()) {
166 Close(connection
->id());
170 rv
= connection
->socket()->Read(
172 read_buf
->RemainingCapacity(),
173 base::Bind(&HttpServer::OnReadCompleted
,
174 weak_ptr_factory_
.GetWeakPtr(), connection
->id()));
175 if (rv
== ERR_IO_PENDING
)
177 rv
= HandleReadResult(connection
, rv
);
181 void HttpServer::OnReadCompleted(int connection_id
, int rv
) {
182 HttpConnection
* connection
= FindConnection(connection_id
);
183 if (!connection
) // It might be closed right before by write error.
186 if (HandleReadResult(connection
, rv
) == OK
)
187 DoReadLoop(connection
);
190 int HttpServer::HandleReadResult(HttpConnection
* connection
, int rv
) {
192 Close(connection
->id());
193 return rv
== 0 ? ERR_CONNECTION_CLOSED
: rv
;
196 HttpConnection::ReadIOBuffer
* read_buf
= connection
->read_buf();
197 read_buf
->DidRead(rv
);
199 // Handles http requests or websocket messages.
200 while (read_buf
->GetSize() > 0) {
201 if (connection
->web_socket()) {
203 WebSocket::ParseResult result
= connection
->web_socket()->Read(&message
);
204 if (result
== WebSocket::FRAME_INCOMPLETE
)
207 if (result
== WebSocket::FRAME_CLOSE
||
208 result
== WebSocket::FRAME_ERROR
) {
209 Close(connection
->id());
210 return ERR_CONNECTION_CLOSED
;
212 delegate_
->OnWebSocketMessage(connection
->id(), message
);
213 if (HasClosedConnection(connection
))
214 return ERR_CONNECTION_CLOSED
;
218 HttpServerRequestInfo request
;
220 if (!ParseHeaders(read_buf
->StartOfBuffer(), read_buf
->GetSize(),
225 // Sets peer address if exists.
226 connection
->socket()->GetPeerAddress(&request
.peer
);
228 if (request
.HasHeaderValue("connection", "upgrade")) {
229 scoped_ptr
<WebSocket
> websocket(
230 WebSocket::CreateWebSocket(this, connection
, request
, &pos
));
231 if (!websocket
) // Not enough data was received.
233 connection
->SetWebSocket(websocket
.Pass());
234 read_buf
->DidConsume(pos
);
235 delegate_
->OnWebSocketRequest(connection
->id(), request
);
236 if (HasClosedConnection(connection
))
237 return ERR_CONNECTION_CLOSED
;
241 const char kContentLength
[] = "content-length";
242 if (request
.headers
.count(kContentLength
) > 0) {
243 size_t content_length
= 0;
244 const size_t kMaxBodySize
= 100 << 20;
245 if (!base::StringToSizeT(request
.GetHeaderValue(kContentLength
),
247 content_length
> kMaxBodySize
) {
248 SendResponse(connection
->id(),
249 HttpServerResponseInfo::CreateFor500(
250 "request content-length too big or unknown: " +
251 request
.GetHeaderValue(kContentLength
)));
252 Close(connection
->id());
253 return ERR_CONNECTION_CLOSED
;
256 if (read_buf
->GetSize() - pos
< content_length
)
257 break; // Not enough data was received yet.
258 request
.data
.assign(read_buf
->StartOfBuffer() + pos
, content_length
);
259 pos
+= content_length
;
262 read_buf
->DidConsume(pos
);
263 delegate_
->OnHttpRequest(connection
->id(), request
);
264 if (HasClosedConnection(connection
))
265 return ERR_CONNECTION_CLOSED
;
271 void HttpServer::DoWriteLoop(HttpConnection
* connection
) {
273 HttpConnection::QueuedWriteIOBuffer
* write_buf
= connection
->write_buf();
274 while (rv
== OK
&& write_buf
->GetSizeToWrite() > 0) {
275 rv
= connection
->socket()->Write(
277 write_buf
->GetSizeToWrite(),
278 base::Bind(&HttpServer::OnWriteCompleted
,
279 weak_ptr_factory_
.GetWeakPtr(), connection
->id()));
280 if (rv
== ERR_IO_PENDING
|| rv
== OK
)
282 rv
= HandleWriteResult(connection
, rv
);
286 void HttpServer::OnWriteCompleted(int connection_id
, int rv
) {
287 HttpConnection
* connection
= FindConnection(connection_id
);
288 if (!connection
) // It might be closed right before by read error.
291 if (HandleWriteResult(connection
, rv
) == OK
)
292 DoWriteLoop(connection
);
295 int HttpServer::HandleWriteResult(HttpConnection
* connection
, int rv
) {
297 Close(connection
->id());
301 connection
->write_buf()->DidConsume(rv
);
308 // HTTP Request Parser
309 // This HTTP request parser uses a simple state machine to quickly parse
310 // through the headers. The parser is not 100% complete, as it is designed
311 // for use in this simple test driver.
314 // - does not handle whitespace on first HTTP line correctly. Expects
315 // a single space between the method/url and url/protocol.
317 // Input character types.
318 enum header_parse_inputs
{
328 enum header_parse_states
{
329 ST_METHOD
, // Receiving the method
330 ST_URL
, // Receiving the URL
331 ST_PROTO
, // Receiving the protocol
332 ST_HEADER
, // Starting a Request Header
333 ST_NAME
, // Receiving a request header name
334 ST_SEPARATOR
, // Receiving the separator between header name and value
335 ST_VALUE
, // Receiving a request header value
336 ST_DONE
, // Parsing is complete and successful
337 ST_ERR
, // Parsing encountered invalid syntax.
341 // State transition table
342 int parser_state
[MAX_STATES
][MAX_INPUTS
] = {
343 /* METHOD */ { ST_URL
, ST_ERR
, ST_ERR
, ST_ERR
, ST_METHOD
},
344 /* URL */ { ST_PROTO
, ST_ERR
, ST_ERR
, ST_URL
, ST_URL
},
345 /* PROTOCOL */ { ST_ERR
, ST_HEADER
, ST_NAME
, ST_ERR
, ST_PROTO
},
346 /* HEADER */ { ST_ERR
, ST_ERR
, ST_NAME
, ST_ERR
, ST_ERR
},
347 /* NAME */ { ST_SEPARATOR
, ST_DONE
, ST_ERR
, ST_VALUE
, ST_NAME
},
348 /* SEPARATOR */ { ST_SEPARATOR
, ST_ERR
, ST_ERR
, ST_VALUE
, ST_ERR
},
349 /* VALUE */ { ST_VALUE
, ST_HEADER
, ST_NAME
, ST_VALUE
, ST_VALUE
},
350 /* DONE */ { ST_DONE
, ST_DONE
, ST_DONE
, ST_DONE
, ST_DONE
},
351 /* ERR */ { ST_ERR
, ST_ERR
, ST_ERR
, ST_ERR
, ST_ERR
}
354 // Convert an input character to the parser's input token.
355 int charToInput(char ch
) {
367 return INPUT_DEFAULT
;
372 bool HttpServer::ParseHeaders(const char* data
,
374 HttpServerRequestInfo
* info
,
377 int state
= ST_METHOD
;
379 std::string header_name
;
380 std::string header_value
;
381 while (pos
< data_len
) {
382 char ch
= data
[pos
++];
383 int input
= charToInput(ch
);
384 int next_state
= parser_state
[state
][input
];
386 bool transition
= (next_state
!= state
);
387 HttpServerRequestInfo::HeadersMap::iterator it
;
389 // Do any actions based on state transitions.
392 info
->method
= buffer
;
400 // TODO(mbelshe): Deal better with parsing protocol.
401 DCHECK(buffer
== "HTTP/1.1");
405 header_name
= base::StringToLowerASCII(buffer
);
409 base::TrimWhitespaceASCII(buffer
, base::TRIM_LEADING
, &header_value
);
410 it
= info
->headers
.find(header_name
);
411 // See last paragraph ("Multiple message-header fields...")
412 // of www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
413 if (it
== info
->headers
.end()) {
414 info
->headers
[header_name
] = header_value
;
416 it
->second
.append(",");
417 it
->second
.append(header_value
);
426 // Do any actions based on current state
433 buffer
.append(&ch
, 1);
436 DCHECK(input
== INPUT_LF
);
443 // No more characters, but we haven't finished parsing yet.
447 HttpConnection
* HttpServer::FindConnection(int connection_id
) {
448 IdToConnectionMap::iterator it
= id_to_connection_
.find(connection_id
);
449 if (it
== id_to_connection_
.end())
454 // This is called after any delegate callbacks are called to check if Close()
455 // has been called during callback processing. Using the pointer of connection,
456 // |connection| is safe here because Close() deletes the connection in next run
458 bool HttpServer::HasClosedConnection(HttpConnection
* connection
) {
459 return FindConnection(connection
->id()) != connection
;