Chromium Code Reviews| 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 HttpServer::HttpServer(scoped_ptr<ServerSocket> server_socket, |
| 25 HttpServer::Delegate* delegate) | 27 HttpServer::Delegate* delegate) |
| 26 : delegate_(delegate), | 28 : server_socket_(server_socket.Pass()), |
| 27 server_(factory.CreateAndListen(this)) { | 29 delegate_(delegate), |
| 30 last_id_(0), | |
| 31 weak_ptr_factory_(this) { | |
| 32 DCHECK(server_socket_); | |
| 33 DoAcceptLoop(); | |
| 34 } | |
| 35 | |
| 36 HttpServer::~HttpServer() { | |
| 37 STLDeleteContainerPairSecondPointers( | |
| 38 id_to_connection_.begin(), id_to_connection_.end()); | |
| 28 } | 39 } |
| 29 | 40 |
| 30 void HttpServer::AcceptWebSocket( | 41 void HttpServer::AcceptWebSocket( |
| 31 int connection_id, | 42 int connection_id, |
| 32 const HttpServerRequestInfo& request) { | 43 const HttpServerRequestInfo& request) { |
| 33 HttpConnection* connection = FindConnection(connection_id); | 44 HttpConnection* connection = FindConnection(connection_id); |
| 34 if (connection == NULL) | 45 if (connection == NULL) |
| 35 return; | 46 return; |
| 36 | 47 DCHECK(connection->web_socket()); |
| 37 DCHECK(connection->web_socket_.get()); | 48 connection->web_socket()->Accept(request); |
| 38 connection->web_socket_->Accept(request); | |
| 39 } | 49 } |
| 40 | 50 |
| 41 void HttpServer::SendOverWebSocket(int connection_id, | 51 void HttpServer::SendOverWebSocket(int connection_id, |
| 42 const std::string& data) { | 52 const std::string& data) { |
| 43 HttpConnection* connection = FindConnection(connection_id); | 53 HttpConnection* connection = FindConnection(connection_id); |
| 44 if (connection == NULL) | 54 if (connection == NULL) |
| 45 return; | 55 return; |
| 46 DCHECK(connection->web_socket_.get()); | 56 DCHECK(connection->web_socket()); |
| 47 connection->web_socket_->Send(data); | 57 connection->web_socket()->Send(data); |
| 48 } | 58 } |
| 49 | 59 |
| 50 void HttpServer::SendRaw(int connection_id, const std::string& data) { | 60 void HttpServer::SendRaw(int connection_id, const std::string& data) { |
| 51 HttpConnection* connection = FindConnection(connection_id); | 61 HttpConnection* connection = FindConnection(connection_id); |
| 52 if (connection == NULL) | 62 if (connection == NULL) |
| 53 return; | 63 return; |
| 54 connection->Send(data); | 64 |
| 65 bool writing_in_progress = !connection->write_buf()->IsEmpty(); | |
| 66 if (connection->write_buf()->Append(data) && !writing_in_progress) | |
| 67 DoWriteLoop(connection); | |
| 55 } | 68 } |
| 56 | 69 |
| 57 void HttpServer::SendResponse(int connection_id, | 70 void HttpServer::SendResponse(int connection_id, |
| 58 const HttpServerResponseInfo& response) { | 71 const HttpServerResponseInfo& response) { |
| 59 HttpConnection* connection = FindConnection(connection_id); | 72 SendRaw(connection_id, response.Serialize()); |
| 60 if (connection == NULL) | |
| 61 return; | |
| 62 connection->Send(response); | |
| 63 } | 73 } |
| 64 | 74 |
| 65 void HttpServer::Send(int connection_id, | 75 void HttpServer::Send(int connection_id, |
| 66 HttpStatusCode status_code, | 76 HttpStatusCode status_code, |
| 67 const std::string& data, | 77 const std::string& data, |
| 68 const std::string& content_type) { | 78 const std::string& content_type) { |
| 69 HttpServerResponseInfo response(status_code); | 79 HttpServerResponseInfo response(status_code); |
| 70 response.SetBody(data, content_type); | 80 response.SetContentHeaders(data.size(), content_type); |
| 71 SendResponse(connection_id, response); | 81 SendResponse(connection_id, response); |
| 82 SendRaw(connection_id, data); | |
| 72 } | 83 } |
| 73 | 84 |
| 74 void HttpServer::Send200(int connection_id, | 85 void HttpServer::Send200(int connection_id, |
| 75 const std::string& data, | 86 const std::string& data, |
| 76 const std::string& content_type) { | 87 const std::string& content_type) { |
| 77 Send(connection_id, HTTP_OK, data, content_type); | 88 Send(connection_id, HTTP_OK, data, content_type); |
| 78 } | 89 } |
| 79 | 90 |
| 80 void HttpServer::Send404(int connection_id) { | 91 void HttpServer::Send404(int connection_id) { |
| 81 SendResponse(connection_id, HttpServerResponseInfo::CreateFor404()); | 92 SendResponse(connection_id, HttpServerResponseInfo::CreateFor404()); |
| 82 } | 93 } |
| 83 | 94 |
| 84 void HttpServer::Send500(int connection_id, const std::string& message) { | 95 void HttpServer::Send500(int connection_id, const std::string& message) { |
| 85 SendResponse(connection_id, HttpServerResponseInfo::CreateFor500(message)); | 96 SendResponse(connection_id, HttpServerResponseInfo::CreateFor500(message)); |
| 86 } | 97 } |
| 87 | 98 |
| 88 void HttpServer::Close(int connection_id) { | 99 void HttpServer::Close(int connection_id) { |
| 89 HttpConnection* connection = FindConnection(connection_id); | 100 HttpConnection* connection = FindConnection(connection_id); |
| 90 if (connection == NULL) | 101 if (connection == NULL) |
| 91 return; | 102 return; |
| 92 | 103 |
| 93 // Initiating close from server-side does not lead to the DidClose call. | 104 id_to_connection_.erase(connection_id); |
| 94 // Do it manually here. | 105 delegate_->OnClose(connection_id); |
| 95 DidClose(connection->socket_.get()); | 106 |
| 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); | |
| 96 } | 112 } |
| 97 | 113 |
| 98 int HttpServer::GetLocalAddress(IPEndPoint* address) { | 114 int HttpServer::GetLocalAddress(IPEndPoint* address) { |
| 99 if (!server_) | 115 return server_socket_->GetLocalAddress(address); |
| 100 return ERR_SOCKET_NOT_CONNECTED; | 116 } |
| 101 return server_->GetLocalAddress(address); | 117 |
| 102 } | 118 void HttpServer::SetReceiveBufferSize(int connection_id, int32 size) { |
| 103 | 119 HttpConnection* connection = FindConnection(connection_id); |
| 104 void HttpServer::DidAccept(StreamListenSocket* server, | 120 DCHECK(connection); |
| 105 scoped_ptr<StreamListenSocket> socket) { | 121 connection->read_buf()->set_max_buffer_size(size); |
| 106 HttpConnection* connection = new HttpConnection(this, socket.Pass()); | 122 } |
| 123 | |
| 124 void HttpServer::SetSendBufferSize(int connection_id, int32 size) { | |
| 125 HttpConnection* connection = FindConnection(connection_id); | |
| 126 DCHECK(connection); | |
| 127 connection->write_buf()->set_max_buffer_size(size); | |
| 128 } | |
| 129 | |
| 130 void HttpServer::DoAcceptLoop() { | |
| 131 int rv; | |
| 132 do { | |
| 133 rv = server_socket_->Accept(&accepted_socket_, | |
| 134 base::Bind(&HttpServer::OnAcceptCompleted, | |
| 135 weak_ptr_factory_.GetWeakPtr())); | |
| 136 if (rv == ERR_IO_PENDING) | |
| 137 return; | |
| 138 rv = HandleAcceptResult(rv); | |
| 139 } while (rv == OK); | |
| 140 } | |
| 141 | |
| 142 void HttpServer::OnAcceptCompleted(int rv) { | |
| 143 if (HandleAcceptResult(rv) == OK) | |
| 144 DoAcceptLoop(); | |
| 145 } | |
| 146 | |
| 147 int HttpServer::HandleAcceptResult(int rv) { | |
| 148 if (rv < 0) { | |
| 149 LOG(ERROR) << "Accept error: rv=" << rv; | |
| 150 return rv; | |
| 151 } | |
| 152 | |
| 153 HttpConnection* connection = | |
| 154 new HttpConnection(++last_id_, accepted_socket_.Pass()); | |
| 107 id_to_connection_[connection->id()] = connection; | 155 id_to_connection_[connection->id()] = connection; |
| 108 // TODO(szym): Fix socket access. Make HttpConnection the Delegate. | 156 DoReadLoop(connection); |
| 109 socket_to_connection_[connection->socket_.get()] = connection; | 157 return OK; |
| 110 } | 158 } |
| 111 | 159 |
| 112 void HttpServer::DidRead(StreamListenSocket* socket, | 160 void HttpServer::DoReadLoop(HttpConnection* connection) { |
| 113 const char* data, | 161 int rv; |
| 114 int len) { | 162 do { |
| 115 HttpConnection* connection = FindConnection(socket); | 163 HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); |
| 116 DCHECK(connection != NULL); | 164 // Increases read buffer size if necessary. |
| 117 if (connection == NULL) | 165 if (read_buf->RemainingCapacity() == 0 && !read_buf->IncreaseCapacity()) { |
| 166 Close(connection->id()); | |
| 167 return; | |
| 168 } | |
| 169 | |
| 170 rv = connection->socket()->Read( | |
| 171 read_buf, | |
| 172 read_buf->RemainingCapacity(), | |
| 173 base::Bind(&HttpServer::OnReadCompleted, | |
| 174 weak_ptr_factory_.GetWeakPtr(), connection->id())); | |
| 175 if (rv == ERR_IO_PENDING) | |
| 176 return; | |
| 177 rv = HandleReadResult(connection, rv); | |
| 178 } while (rv == OK); | |
| 179 } | |
| 180 | |
| 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. | |
| 118 return; | 184 return; |
| 119 | 185 |
| 120 connection->recv_data_.append(data, len); | 186 if (HandleReadResult(connection, rv) == OK) |
| 121 while (connection->recv_data_.length()) { | 187 DoReadLoop(connection); |
| 122 if (connection->web_socket_.get()) { | 188 } |
| 189 | |
| 190 int HttpServer::HandleReadResult(HttpConnection* connection, int rv) { | |
| 191 if (rv <= 0) { | |
| 192 Close(connection->id()); | |
| 193 return rv == 0 ? ERR_CONNECTION_CLOSED : rv; | |
| 194 } | |
| 195 | |
| 196 HttpConnection::ReadIOBuffer* read_buf = connection->read_buf(); | |
| 197 read_buf->DidRead(rv); | |
| 198 | |
| 199 // Handles http requests or websocket messages. | |
| 200 while (read_buf->GetSize() > 0) { | |
| 201 if (connection->web_socket()) { | |
| 123 std::string message; | 202 std::string message; |
| 124 WebSocket::ParseResult result = connection->web_socket_->Read(&message); | 203 WebSocket::ParseResult result = connection->web_socket()->Read(&message); |
| 125 if (result == WebSocket::FRAME_INCOMPLETE) | 204 if (result == WebSocket::FRAME_INCOMPLETE) |
| 126 break; | 205 break; |
| 127 | 206 |
| 128 if (result == WebSocket::FRAME_CLOSE || | 207 if (result == WebSocket::FRAME_CLOSE || |
| 129 result == WebSocket::FRAME_ERROR) { | 208 result == WebSocket::FRAME_ERROR) { |
| 130 Close(connection->id()); | 209 Close(connection->id()); |
| 131 break; | 210 return ERR_CONNECTION_CLOSED; |
| 132 } | 211 } |
| 133 delegate_->OnWebSocketMessage(connection->id(), message); | 212 delegate_->OnWebSocketMessage(connection->id(), message); |
| 213 if (IsConnectionClosed(connection)) { | |
| 214 return ERR_CONNECTION_CLOSED; | |
| 215 } | |
|
mmenke
2014/08/15 17:53:02
nit: Don't use braces for ifs with single line bo
byungchul
2014/08/15 18:05:17
Done.
| |
| 134 continue; | 216 continue; |
| 135 } | 217 } |
| 136 | 218 |
| 137 HttpServerRequestInfo request; | 219 HttpServerRequestInfo request; |
| 138 size_t pos = 0; | 220 size_t pos = 0; |
| 139 if (!ParseHeaders(connection, &request, &pos)) | 221 if (!ParseHeaders(read_buf->StartOfBuffer(), read_buf->GetSize(), |
| 222 &request, &pos)) { | |
| 140 break; | 223 break; |
| 224 } | |
| 141 | 225 |
| 142 // Sets peer address if exists. | 226 // Sets peer address if exists. |
| 143 socket->GetPeerAddress(&request.peer); | 227 connection->socket()->GetPeerAddress(&request.peer); |
| 144 | 228 |
| 145 if (request.HasHeaderValue("connection", "upgrade")) { | 229 if (request.HasHeaderValue("connection", "upgrade")) { |
| 146 connection->web_socket_.reset(WebSocket::CreateWebSocket(connection, | 230 scoped_ptr<WebSocket> websocket( |
| 147 request, | 231 WebSocket::CreateWebSocket(this, connection, request, &pos)); |
| 148 &pos)); | 232 if (!websocket) // Not enough data was received. |
| 149 | |
| 150 if (!connection->web_socket_.get()) // Not enough data was received. | |
| 151 break; | 233 break; |
| 234 connection->SetWebSocket(websocket.Pass()); | |
| 235 read_buf->DidConsume(pos); | |
| 152 delegate_->OnWebSocketRequest(connection->id(), request); | 236 delegate_->OnWebSocketRequest(connection->id(), request); |
| 153 connection->Shift(pos); | 237 if (IsConnectionClosed(connection)) { |
| 238 return ERR_CONNECTION_CLOSED; | |
| 239 } | |
| 154 continue; | 240 continue; |
| 155 } | 241 } |
| 156 | 242 |
| 157 const char kContentLength[] = "content-length"; | 243 const char kContentLength[] = "content-length"; |
| 158 if (request.headers.count(kContentLength)) { | 244 if (request.headers.count(kContentLength) > 0) { |
| 159 size_t content_length = 0; | 245 size_t content_length = 0; |
| 160 const size_t kMaxBodySize = 100 << 20; | 246 const size_t kMaxBodySize = 100 << 20; |
| 161 if (!base::StringToSizeT(request.GetHeaderValue(kContentLength), | 247 if (!base::StringToSizeT(request.GetHeaderValue(kContentLength), |
| 162 &content_length) || | 248 &content_length) || |
| 163 content_length > kMaxBodySize) { | 249 content_length > kMaxBodySize) { |
| 164 connection->Send(HttpServerResponseInfo::CreateFor500( | 250 SendResponse(connection->id(), |
| 165 "request content-length too big or unknown: " + | 251 HttpServerResponseInfo::CreateFor500( |
| 166 request.GetHeaderValue(kContentLength))); | 252 "request content-length too big or unknown: " + |
| 167 DidClose(socket); | 253 request.GetHeaderValue(kContentLength))); |
| 168 break; | 254 Close(connection->id()); |
| 169 } | 255 return ERR_CONNECTION_CLOSED; |
| 170 | 256 } |
| 171 if (connection->recv_data_.length() - pos < content_length) | 257 |
| 258 if (read_buf->GetSize() - pos < content_length) | |
| 172 break; // Not enough data was received yet. | 259 break; // Not enough data was received yet. |
| 173 request.data = connection->recv_data_.substr(pos, content_length); | 260 request.data.assign(read_buf->StartOfBuffer() + pos, content_length); |
| 174 pos += content_length; | 261 pos += content_length; |
| 175 } | 262 } |
| 176 | 263 |
| 264 read_buf->DidConsume(pos); | |
| 177 delegate_->OnHttpRequest(connection->id(), request); | 265 delegate_->OnHttpRequest(connection->id(), request); |
| 178 connection->Shift(pos); | 266 if (IsConnectionClosed(connection)) { |
| 179 } | 267 return ERR_CONNECTION_CLOSED; |
| 180 } | 268 } |
| 181 | 269 } |
| 182 void HttpServer::DidClose(StreamListenSocket* socket) { | 270 |
| 183 HttpConnection* connection = FindConnection(socket); | 271 return OK; |
| 184 DCHECK(connection != NULL); | 272 } |
| 185 id_to_connection_.erase(connection->id()); | 273 |
| 186 socket_to_connection_.erase(connection->socket_.get()); | 274 void HttpServer::DoWriteLoop(HttpConnection* connection) { |
| 187 delete connection; | 275 int rv = OK; |
| 188 } | 276 HttpConnection::QueuedWriteIOBuffer* write_buf = connection->write_buf(); |
| 189 | 277 while (rv == OK && write_buf->GetSizeToWrite() > 0) { |
| 190 HttpServer::~HttpServer() { | 278 rv = connection->socket()->Write( |
| 191 STLDeleteContainerPairSecondPointers( | 279 write_buf, |
| 192 id_to_connection_.begin(), id_to_connection_.end()); | 280 write_buf->GetSizeToWrite(), |
| 193 } | 281 base::Bind(&HttpServer::OnWriteCompleted, |
| 282 weak_ptr_factory_.GetWeakPtr(), connection->id())); | |
| 283 if (rv == ERR_IO_PENDING || rv == OK) | |
| 284 return; | |
| 285 rv = HandleWriteResult(connection, rv); | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void HttpServer::OnWriteCompleted(int connection_id, int rv) { | |
| 290 HttpConnection* connection = FindConnection(connection_id); | |
| 291 if (!connection) // It might be closed right before by read error. | |
| 292 return; | |
| 293 | |
| 294 if (HandleWriteResult(connection, rv) == OK) | |
| 295 DoWriteLoop(connection); | |
| 296 } | |
| 297 | |
| 298 int HttpServer::HandleWriteResult(HttpConnection* connection, int rv) { | |
| 299 if (rv < 0) { | |
| 300 Close(connection->id()); | |
| 301 return rv; | |
| 302 } | |
| 303 | |
| 304 connection->write_buf()->DidConsume(rv); | |
| 305 return OK; | |
| 306 } | |
| 307 | |
| 308 namespace { | |
| 194 | 309 |
| 195 // | 310 // |
| 196 // HTTP Request Parser | 311 // HTTP Request Parser |
| 197 // This HTTP request parser uses a simple state machine to quickly parse | 312 // 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 | 313 // through the headers. The parser is not 100% complete, as it is designed |
| 199 // for use in this simple test driver. | 314 // for use in this simple test driver. |
| 200 // | 315 // |
| 201 // Known issues: | 316 // Known issues: |
| 202 // - does not handle whitespace on first HTTP line correctly. Expects | 317 // - does not handle whitespace on first HTTP line correctly. Expects |
| 203 // a single space between the method/url and url/protocol. | 318 // a single space between the method/url and url/protocol. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 case '\r': | 363 case '\r': |
| 249 return INPUT_CR; | 364 return INPUT_CR; |
| 250 case '\n': | 365 case '\n': |
| 251 return INPUT_LF; | 366 return INPUT_LF; |
| 252 case ':': | 367 case ':': |
| 253 return INPUT_COLON; | 368 return INPUT_COLON; |
| 254 } | 369 } |
| 255 return INPUT_DEFAULT; | 370 return INPUT_DEFAULT; |
| 256 } | 371 } |
| 257 | 372 |
| 258 bool HttpServer::ParseHeaders(HttpConnection* connection, | 373 } // namespace |
| 374 | |
| 375 bool HttpServer::ParseHeaders(const char* data, | |
| 376 size_t data_len, | |
| 259 HttpServerRequestInfo* info, | 377 HttpServerRequestInfo* info, |
| 260 size_t* ppos) { | 378 size_t* ppos) { |
| 261 size_t& pos = *ppos; | 379 size_t& pos = *ppos; |
| 262 size_t data_len = connection->recv_data_.length(); | |
| 263 int state = ST_METHOD; | 380 int state = ST_METHOD; |
| 264 std::string buffer; | 381 std::string buffer; |
| 265 std::string header_name; | 382 std::string header_name; |
| 266 std::string header_value; | 383 std::string header_value; |
| 267 while (pos < data_len) { | 384 while (pos < data_len) { |
| 268 char ch = connection->recv_data_[pos++]; | 385 char ch = data[pos++]; |
| 269 int input = charToInput(ch); | 386 int input = charToInput(ch); |
| 270 int next_state = parser_state[state][input]; | 387 int next_state = parser_state[state][input]; |
| 271 | 388 |
| 272 bool transition = (next_state != state); | 389 bool transition = (next_state != state); |
| 273 HttpServerRequestInfo::HeadersMap::iterator it; | 390 HttpServerRequestInfo::HeadersMap::iterator it; |
| 274 if (transition) { | 391 if (transition) { |
| 275 // Do any actions based on state transitions. | 392 // Do any actions based on state transitions. |
| 276 switch (state) { | 393 switch (state) { |
| 277 case ST_METHOD: | 394 case ST_METHOD: |
| 278 info->method = buffer; | 395 info->method = buffer; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 330 return false; | 447 return false; |
| 331 } | 448 } |
| 332 | 449 |
| 333 HttpConnection* HttpServer::FindConnection(int connection_id) { | 450 HttpConnection* HttpServer::FindConnection(int connection_id) { |
| 334 IdToConnectionMap::iterator it = id_to_connection_.find(connection_id); | 451 IdToConnectionMap::iterator it = id_to_connection_.find(connection_id); |
| 335 if (it == id_to_connection_.end()) | 452 if (it == id_to_connection_.end()) |
| 336 return NULL; | 453 return NULL; |
| 337 return it->second; | 454 return it->second; |
| 338 } | 455 } |
| 339 | 456 |
| 340 HttpConnection* HttpServer::FindConnection(StreamListenSocket* socket) { | 457 // This is called after any delegate callbacks are called to check if the |
| 341 SocketToConnectionMap::iterator it = socket_to_connection_.find(socket); | 458 // connection has been closed during callback processing. Using the pointer of |
| 342 if (it == socket_to_connection_.end()) | 459 // connection, |connection| is safe here because Close() above deletes the |
| 343 return NULL; | 460 // connection in next run loop. |
| 344 return it->second; | 461 bool HttpServer::IsConnectionClosed(HttpConnection* connection) { |
| 462 return FindConnection(connection->id()) != connection; | |
| 345 } | 463 } |
| 346 | 464 |
| 347 } // namespace net | 465 } // namespace net |
| OLD | NEW |