OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/server/http_server.h" | 5 #include "net/server/http_server.h" |
6 | 6 |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
13 #include "base/sys_byteorder.h" | 13 #include "base/sys_byteorder.h" |
14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
15 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
16 #include "net/server/http_connection.h" | 16 #include "net/server/http_connection.h" |
17 #include "net/server/http_server_request_info.h" | 17 #include "net/server/http_server_request_info.h" |
18 #include "net/server/http_server_response_info.h" | 18 #include "net/server/http_server_response_info.h" |
19 #include "net/server/web_socket.h" | 19 #include "net/server/web_socket.h" |
20 #include "net/socket/tcp_listen_socket.h" | 20 #include "net/socket/server_socket.h" |
21 #include "net/socket/stream_socket.h" | |
22 #include "net/socket/tcp_server_socket.h" | |
21 | 23 |
22 namespace net { | 24 namespace net { |
23 | 25 |
24 HttpServer::HttpServer(const StreamListenSocketFactory& factory, | 26 namespace { |
27 | |
28 // A helper function to delete connection in next run loop. | |
29 void DeleteConnection(HttpConnection* connection) { | |
30 delete connection; | |
31 } | |
32 | |
33 } // namespace | |
34 | |
35 HttpServer::HttpServer(scoped_ptr<ServerSocket> server_socket, | |
25 HttpServer::Delegate* delegate) | 36 HttpServer::Delegate* delegate) |
26 : delegate_(delegate), | 37 : server_socket_(server_socket.Pass()), |
27 server_(factory.CreateAndListen(this)) { | 38 delegate_(delegate), |
39 last_id_(0) { | |
40 DCHECK(server_socket_); | |
41 DoAcceptLoop(); | |
42 } | |
43 | |
44 HttpServer::~HttpServer() { | |
45 STLDeleteContainerPairSecondPointers( | |
46 id_to_connection_.begin(), id_to_connection_.end()); | |
28 } | 47 } |
29 | 48 |
30 void HttpServer::AcceptWebSocket( | 49 void HttpServer::AcceptWebSocket( |
31 int connection_id, | 50 int connection_id, |
32 const HttpServerRequestInfo& request) { | 51 const HttpServerRequestInfo& request) { |
33 HttpConnection* connection = FindConnection(connection_id); | 52 HttpConnection* connection = FindConnection(connection_id); |
34 if (connection == NULL) | 53 if (connection == NULL) |
35 return; | 54 return; |
36 | 55 DCHECK(connection->web_socket()); |
37 DCHECK(connection->web_socket_.get()); | 56 connection->web_socket()->Accept(request); |
38 connection->web_socket_->Accept(request); | |
39 } | 57 } |
40 | 58 |
41 void HttpServer::SendOverWebSocket(int connection_id, | 59 void HttpServer::SendOverWebSocket(int connection_id, |
42 const std::string& data) { | 60 const std::string& data) { |
43 HttpConnection* connection = FindConnection(connection_id); | 61 HttpConnection* connection = FindConnection(connection_id); |
44 if (connection == NULL) | 62 if (connection == NULL) |
45 return; | 63 return; |
46 DCHECK(connection->web_socket_.get()); | 64 DCHECK(connection->web_socket()); |
47 connection->web_socket_->Send(data); | 65 connection->web_socket()->Send(data); |
48 } | 66 } |
49 | 67 |
50 void HttpServer::SendRaw(int connection_id, const std::string& data) { | 68 void HttpServer::SendRaw(int connection_id, const std::string& data) { |
51 HttpConnection* connection = FindConnection(connection_id); | 69 HttpConnection* connection = FindConnection(connection_id); |
52 if (connection == NULL) | 70 if (connection == NULL) |
53 return; | 71 return; |
54 connection->Send(data); | 72 |
73 bool writing_in_progress = !connection->write_buf()->IsEmpty(); | |
74 if (connection->write_buf()->Append(data) && !writing_in_progress) | |
75 DoWriteLoop(connection); | |
55 } | 76 } |
56 | 77 |
57 void HttpServer::SendResponse(int connection_id, | 78 void HttpServer::SendResponse(int connection_id, |
58 const HttpServerResponseInfo& response) { | 79 const HttpServerResponseInfo& response) { |
mmenke
2014/07/24 20:50:08
Maybe rename this to SendResponseHeaders? Could o
byungchul
2014/08/05 07:06:19
It's not just for headers. HttpServerResponseInfo
| |
59 HttpConnection* connection = FindConnection(connection_id); | 80 SendRaw(connection_id, response.Serialize()); |
60 if (connection == NULL) | |
61 return; | |
62 connection->Send(response); | |
63 } | 81 } |
64 | 82 |
65 void HttpServer::Send(int connection_id, | 83 void HttpServer::Send(int connection_id, |
66 HttpStatusCode status_code, | 84 HttpStatusCode status_code, |
67 const std::string& data, | 85 const std::string& data, |
68 const std::string& content_type) { | 86 const std::string& content_type) { |
69 HttpServerResponseInfo response(status_code); | 87 HttpServerResponseInfo response(status_code); |
70 response.SetBody(data, content_type); | 88 response.SetContentHeaders(data.size(), content_type); |
71 SendResponse(connection_id, response); | 89 SendResponse(connection_id, response); |
90 SendRaw(connection_id, data); | |
72 } | 91 } |
73 | 92 |
74 void HttpServer::Send200(int connection_id, | 93 void HttpServer::Send200(int connection_id, |
75 const std::string& data, | 94 const std::string& data, |
76 const std::string& content_type) { | 95 const std::string& content_type) { |
77 Send(connection_id, HTTP_OK, data, content_type); | 96 Send(connection_id, HTTP_OK, data, content_type); |
78 } | 97 } |
79 | 98 |
80 void HttpServer::Send404(int connection_id) { | 99 void HttpServer::Send404(int connection_id) { |
81 SendResponse(connection_id, HttpServerResponseInfo::CreateFor404()); | 100 SendResponse(connection_id, HttpServerResponseInfo::CreateFor404()); |
82 } | 101 } |
83 | 102 |
84 void HttpServer::Send500(int connection_id, const std::string& message) { | 103 void HttpServer::Send500(int connection_id, const std::string& message) { |
85 SendResponse(connection_id, HttpServerResponseInfo::CreateFor500(message)); | 104 SendResponse(connection_id, HttpServerResponseInfo::CreateFor500(message)); |
86 } | 105 } |
87 | 106 |
88 void HttpServer::Close(int connection_id) { | 107 void HttpServer::Close(int connection_id) { |
89 HttpConnection* connection = FindConnection(connection_id); | 108 HttpConnection* connection = FindConnection(connection_id); |
90 if (connection == NULL) | 109 if (connection == NULL) |
91 return; | 110 return; |
92 | 111 |
93 // Initiating close from server-side does not lead to the DidClose call. | 112 id_to_connection_.erase(connection->id()); |
94 // Do it manually here. | 113 delegate_->OnClose(connection_id); |
95 DidClose(connection->socket_.get()); | 114 |
115 // The call stack might have callbacks which still have the pointer of | |
116 // connection. Instead of referencing connection with ID all the time, | |
117 // destroys the connection in next run loop to make sure any pending | |
118 // callbacks in the call stack return. | |
mmenke
2014/07/24 20:50:09
What callbacks? SocketLibEvent can handle being d
byungchul
2014/08/05 07:06:19
This is a server. So, it is a common pattern to do
| |
119 base::MessageLoopProxy::current()->PostTask( | |
120 FROM_HERE, | |
121 base::Bind(&DeleteConnection, base::Unretained(connection))); | |
mmenke
2014/07/24 20:50:06
DeleteHelper, maybe?
Both leak if the task is nev
byungchul
2014/08/05 07:06:19
Don't know how to use DeleteHelper correctly. Inst
| |
96 } | 122 } |
97 | 123 |
98 int HttpServer::GetLocalAddress(IPEndPoint* address) { | 124 int HttpServer::GetLocalAddress(IPEndPoint* address) { |
99 if (!server_) | 125 return server_socket_->GetLocalAddress(address); |
100 return ERR_SOCKET_NOT_CONNECTED; | |
101 return server_->GetLocalAddress(address); | |
102 } | 126 } |
103 | 127 |
104 void HttpServer::DidAccept(StreamListenSocket* server, | 128 void HttpServer::SetReceiveBufferSize(int connection_id, int32 size) { |
105 scoped_ptr<StreamListenSocket> socket) { | 129 HttpConnection* connection = FindConnection(connection_id); |
106 HttpConnection* connection = new HttpConnection(this, socket.Pass()); | 130 if (connection == NULL) |
107 id_to_connection_[connection->id()] = connection; | 131 return; |
mmenke
2014/07/24 20:50:06
Is there a real need to let this be called for non
byungchul
2014/08/05 07:06:19
Done.
| |
108 // TODO(szym): Fix socket access. Make HttpConnection the Delegate. | 132 connection->read_buf()->set_capacity_limit(size); |
109 socket_to_connection_[connection->socket_.get()] = connection; | |
110 } | 133 } |
111 | 134 |
112 void HttpServer::DidRead(StreamListenSocket* socket, | 135 void HttpServer::SetSendBufferSize(int connection_id, int32 size) { |
113 const char* data, | 136 HttpConnection* connection = FindConnection(connection_id); |
114 int len) { | |
115 HttpConnection* connection = FindConnection(socket); | |
116 DCHECK(connection != NULL); | |
117 if (connection == NULL) | 137 if (connection == NULL) |
118 return; | 138 return; |
139 connection->write_buf()->set_total_size_limit(size); | |
140 } | |
119 | 141 |
120 connection->recv_data_.append(data, len); | 142 void HttpServer::DoAcceptLoop() { |
121 while (connection->recv_data_.length()) { | 143 int rv; |
122 if (connection->web_socket_.get()) { | 144 do { |
145 rv = server_socket_->Accept(&accepted_socket_, | |
146 base::Bind(&HttpServer::OnAcceptCompleted, | |
147 base::Unretained(this))); | |
148 if (rv == ERR_IO_PENDING) | |
149 return; | |
150 rv = DidAccept(rv); | |
151 } while (rv == OK); | |
152 } | |
153 | |
154 void HttpServer::OnAcceptCompleted(int rv) { | |
155 if (DidAccept(rv) == OK) | |
156 DoAcceptLoop(); | |
157 } | |
158 | |
159 int HttpServer::DidAccept(int rv) { | |
160 if (rv < 0) { | |
161 LOG(ERROR) << "Accept error: rv=" << rv; | |
162 return rv; | |
163 } | |
164 | |
165 HttpConnection* connection = | |
166 new HttpConnection(++last_id_, accepted_socket_.Pass()); | |
167 id_to_connection_[connection->id()] = connection; | |
168 DoReadLoop(connection); | |
mmenke
2014/07/24 20:50:09
Random comment, feel free to ignore: Hrm...we nor
byungchul
2014/08/05 07:06:19
Even onClose() is not safe to destroy http server.
| |
169 return OK; | |
170 } | |
171 | |
172 void HttpServer::DoReadLoop(HttpConnection* connection) { | |
173 int rv; | |
174 do { | |
175 HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); | |
176 // Increases read buffer size if necessary. | |
177 if (read_buf->GetUnusedCapacity() == 0 && !read_buf->IncreaseCapacity()) { | |
178 Close(connection->id()); | |
179 return; | |
180 } | |
181 | |
182 rv = connection->socket()->Read( | |
183 read_buf->GetUnusedIOBuffer(), | |
184 read_buf->GetUnusedCapacity(), | |
185 base::Bind(&HttpServer::OnReadCompleted, | |
186 base::Unretained(this), connection->id())); | |
187 if (rv == ERR_IO_PENDING) | |
188 return; | |
189 rv = DidRead(connection, rv); | |
190 } while (rv == OK); | |
191 } | |
192 | |
193 void HttpServer::OnReadCompleted(int connection_id, int rv) { | |
194 HttpConnection* connection = FindConnection(connection_id); | |
195 if (!connection) // It might be closed right before by write error. | |
mmenke
2014/07/24 20:50:06
What's to prevent the HttpServer from being destro
byungchul
2014/08/05 07:06:19
connection doesn't have the reference for http ser
| |
196 return; | |
197 | |
198 if (DidRead(connection, rv) == OK) | |
199 DoReadLoop(connection); | |
200 } | |
201 | |
202 int HttpServer::DidRead(HttpConnection* connection, int rv) { | |
mmenke
2014/07/24 20:50:07
optional: Suggest renaming this to HandleReadResu
byungchul
2014/08/05 07:06:19
Done.
| |
203 if (rv <= 0) { | |
204 Close(connection->id()); | |
205 return rv == 0 ? ERR_CONNECTION_CLOSED : rv; | |
206 } | |
207 | |
208 HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); | |
209 read_buf->DidRead(rv); | |
210 | |
211 // Handles http requests or websocket messages. | |
212 while (read_buf->GetUnconsumedSize() > 0) { | |
213 if (connection->web_socket()) { | |
123 std::string message; | 214 std::string message; |
124 WebSocket::ParseResult result = connection->web_socket_->Read(&message); | 215 WebSocket::ParseResult result = connection->web_socket()->Read(&message); |
125 if (result == WebSocket::FRAME_INCOMPLETE) | 216 if (result == WebSocket::FRAME_INCOMPLETE) |
126 break; | 217 break; |
127 | 218 |
128 if (result == WebSocket::FRAME_CLOSE || | 219 if (result == WebSocket::FRAME_CLOSE || |
129 result == WebSocket::FRAME_ERROR) { | 220 result == WebSocket::FRAME_ERROR) { |
130 Close(connection->id()); | 221 Close(connection->id()); |
131 break; | 222 break; |
132 } | 223 } |
133 delegate_->OnWebSocketMessage(connection->id(), message); | 224 delegate_->OnWebSocketMessage(connection->id(), message); |
134 continue; | 225 continue; |
135 } | 226 } |
136 | 227 |
137 HttpServerRequestInfo request; | 228 HttpServerRequestInfo request; |
138 size_t pos = 0; | 229 size_t pos = 0; |
139 if (!ParseHeaders(connection, &request, &pos)) | 230 if (!ParseHeaders(read_buf->data(), read_buf->GetUnconsumedSize(), |
231 &request, &pos)) | |
140 break; | 232 break; |
141 | 233 |
142 // Sets peer address if exists. | 234 // Sets peer address if exists. |
143 socket->GetPeerAddress(&request.peer); | 235 connection->socket()->GetPeerAddress(&request.peer); |
144 | 236 |
145 if (request.HasHeaderValue("connection", "upgrade")) { | 237 if (request.HasHeaderValue("connection", "upgrade")) { |
146 connection->web_socket_.reset(WebSocket::CreateWebSocket(connection, | 238 scoped_ptr<WebSocket> websocket( |
147 request, | 239 WebSocket::CreateWebSocket(this, connection, request, &pos)); |
148 &pos)); | 240 if (!websocket) // Not enough data was received. |
149 | |
150 if (!connection->web_socket_.get()) // Not enough data was received. | |
151 break; | 241 break; |
242 connection->SetWebSocket(websocket.Pass()); | |
243 read_buf->DidConsume(pos); | |
152 delegate_->OnWebSocketRequest(connection->id(), request); | 244 delegate_->OnWebSocketRequest(connection->id(), request); |
153 connection->Shift(pos); | |
154 continue; | 245 continue; |
155 } | 246 } |
156 | 247 |
157 const char kContentLength[] = "content-length"; | 248 const char kContentLength[] = "content-length"; |
158 if (request.headers.count(kContentLength)) { | 249 if (request.headers.count(kContentLength) > 0) { |
159 size_t content_length = 0; | 250 size_t content_length = 0; |
160 const size_t kMaxBodySize = 100 << 20; | 251 const size_t kMaxBodySize = 100 << 20; |
161 if (!base::StringToSizeT(request.GetHeaderValue(kContentLength), | 252 if (!base::StringToSizeT(request.GetHeaderValue(kContentLength), |
162 &content_length) || | 253 &content_length) || |
163 content_length > kMaxBodySize) { | 254 content_length > kMaxBodySize) { |
164 connection->Send(HttpServerResponseInfo::CreateFor500( | 255 SendResponse(connection->id(), |
165 "request content-length too big or unknown: " + | 256 HttpServerResponseInfo::CreateFor500( |
166 request.GetHeaderValue(kContentLength))); | 257 "request content-length too big or unknown: " + |
167 DidClose(socket); | 258 request.GetHeaderValue(kContentLength))); |
259 Close(connection->id()); | |
168 break; | 260 break; |
169 } | 261 } |
170 | 262 |
171 if (connection->recv_data_.length() - pos < content_length) | 263 if (read_buf->GetUnconsumedSize() - pos < content_length) |
172 break; // Not enough data was received yet. | 264 break; // Not enough data was received yet. |
173 request.data = connection->recv_data_.substr(pos, content_length); | 265 request.data.assign(read_buf->data() + pos, content_length); |
174 pos += content_length; | 266 pos += content_length; |
175 } | 267 } |
176 | 268 |
269 read_buf->DidConsume(pos); | |
177 delegate_->OnHttpRequest(connection->id(), request); | 270 delegate_->OnHttpRequest(connection->id(), request); |
178 connection->Shift(pos); | 271 } |
272 | |
273 return OK; | |
274 } | |
275 | |
276 void HttpServer::DoWriteLoop(HttpConnection* connection) { | |
277 int rv = OK; | |
278 HttpConnection::PendingWriteIOBuffer* write_buf = connection->write_buf(); | |
279 while (rv == OK && write_buf->GetSizeToWrite() > 0) { | |
280 rv = connection->socket()->Write( | |
281 write_buf, | |
282 write_buf->GetSizeToWrite(), | |
283 base::Bind(&HttpServer::OnWriteCompleted, | |
284 base::Unretained(this), connection->id())); | |
285 if (rv == ERR_IO_PENDING || rv == OK) | |
286 return; | |
287 rv = DidWrite(connection, rv); | |
179 } | 288 } |
180 } | 289 } |
181 | 290 |
182 void HttpServer::DidClose(StreamListenSocket* socket) { | 291 void HttpServer::OnWriteCompleted(int connection_id, int rv) { |
183 HttpConnection* connection = FindConnection(socket); | 292 HttpConnection* connection = FindConnection(connection_id); |
184 DCHECK(connection != NULL); | 293 if (!connection) // It might be closed right before by read error. |
185 id_to_connection_.erase(connection->id()); | 294 return; |
186 socket_to_connection_.erase(connection->socket_.get()); | 295 |
187 delete connection; | 296 if (DidWrite(connection, rv) == OK) |
297 DoWriteLoop(connection); | |
188 } | 298 } |
189 | 299 |
190 HttpServer::~HttpServer() { | 300 int HttpServer::DidWrite(HttpConnection* connection, int rv) { |
191 STLDeleteContainerPairSecondPointers( | 301 if (rv < 0) { |
192 id_to_connection_.begin(), id_to_connection_.end()); | 302 Close(connection->id()); |
303 return rv; | |
304 } | |
305 | |
306 connection->write_buf()->DidConsume(rv); | |
307 return OK; | |
193 } | 308 } |
194 | 309 |
310 namespace { | |
311 | |
195 // | 312 // |
196 // HTTP Request Parser | 313 // HTTP Request Parser |
197 // This HTTP request parser uses a simple state machine to quickly parse | 314 // This HTTP request parser uses a simple state machine to quickly parse |
198 // through the headers. The parser is not 100% complete, as it is designed | 315 // through the headers. The parser is not 100% complete, as it is designed |
199 // for use in this simple test driver. | 316 // for use in this simple test driver. |
200 // | 317 // |
201 // Known issues: | 318 // Known issues: |
202 // - does not handle whitespace on first HTTP line correctly. Expects | 319 // - does not handle whitespace on first HTTP line correctly. Expects |
203 // a single space between the method/url and url/protocol. | 320 // a single space between the method/url and url/protocol. |
204 | 321 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
248 case '\r': | 365 case '\r': |
249 return INPUT_CR; | 366 return INPUT_CR; |
250 case '\n': | 367 case '\n': |
251 return INPUT_LF; | 368 return INPUT_LF; |
252 case ':': | 369 case ':': |
253 return INPUT_COLON; | 370 return INPUT_COLON; |
254 } | 371 } |
255 return INPUT_DEFAULT; | 372 return INPUT_DEFAULT; |
256 } | 373 } |
257 | 374 |
258 bool HttpServer::ParseHeaders(HttpConnection* connection, | 375 } // namespace |
376 | |
377 bool HttpServer::ParseHeaders(const char* data, | |
378 size_t data_len, | |
259 HttpServerRequestInfo* info, | 379 HttpServerRequestInfo* info, |
260 size_t* ppos) { | 380 size_t* ppos) { |
261 size_t& pos = *ppos; | 381 size_t& pos = *ppos; |
262 size_t data_len = connection->recv_data_.length(); | |
263 int state = ST_METHOD; | 382 int state = ST_METHOD; |
264 std::string buffer; | 383 std::string buffer; |
265 std::string header_name; | 384 std::string header_name; |
266 std::string header_value; | 385 std::string header_value; |
267 while (pos < data_len) { | 386 while (pos < data_len) { |
268 char ch = connection->recv_data_[pos++]; | 387 char ch = data[pos++]; |
269 int input = charToInput(ch); | 388 int input = charToInput(ch); |
270 int next_state = parser_state[state][input]; | 389 int next_state = parser_state[state][input]; |
271 | 390 |
272 bool transition = (next_state != state); | 391 bool transition = (next_state != state); |
273 HttpServerRequestInfo::HeadersMap::iterator it; | 392 HttpServerRequestInfo::HeadersMap::iterator it; |
274 if (transition) { | 393 if (transition) { |
275 // Do any actions based on state transitions. | 394 // Do any actions based on state transitions. |
276 switch (state) { | 395 switch (state) { |
277 case ST_METHOD: | 396 case ST_METHOD: |
278 info->method = buffer; | 397 info->method = buffer; |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
330 return false; | 449 return false; |
331 } | 450 } |
332 | 451 |
333 HttpConnection* HttpServer::FindConnection(int connection_id) { | 452 HttpConnection* HttpServer::FindConnection(int connection_id) { |
334 IdToConnectionMap::iterator it = id_to_connection_.find(connection_id); | 453 IdToConnectionMap::iterator it = id_to_connection_.find(connection_id); |
335 if (it == id_to_connection_.end()) | 454 if (it == id_to_connection_.end()) |
336 return NULL; | 455 return NULL; |
337 return it->second; | 456 return it->second; |
338 } | 457 } |
339 | 458 |
340 HttpConnection* HttpServer::FindConnection(StreamListenSocket* socket) { | |
341 SocketToConnectionMap::iterator it = socket_to_connection_.find(socket); | |
342 if (it == socket_to_connection_.end()) | |
343 return NULL; | |
344 return it->second; | |
345 } | |
346 | |
347 } // namespace net | 459 } // namespace net |
OLD | NEW |