| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/spdy/spdy_http_stream.h" | 5 #include "net/spdy/spdy_http_stream.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <list> | 8 #include <list> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 namespace net { | 24 namespace net { |
| 25 | 25 |
| 26 SpdyHttpStream::SpdyHttpStream(SpdySession* spdy_session, | 26 SpdyHttpStream::SpdyHttpStream(SpdySession* spdy_session, |
| 27 bool direct) | 27 bool direct) |
| 28 : ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_factory_(this)), | 28 : ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_factory_(this)), |
| 29 stream_(NULL), | 29 stream_(NULL), |
| 30 spdy_session_(spdy_session), | 30 spdy_session_(spdy_session), |
| 31 response_info_(NULL), | 31 response_info_(NULL), |
| 32 download_finished_(false), | 32 download_finished_(false), |
| 33 response_headers_received_(false), | 33 response_headers_received_(false), |
| 34 user_callback_(NULL), | |
| 35 user_buffer_len_(0), | 34 user_buffer_len_(0), |
| 36 buffered_read_callback_pending_(false), | 35 buffered_read_callback_pending_(false), |
| 37 more_read_data_pending_(false), | 36 more_read_data_pending_(false), |
| 38 direct_(direct) { } | 37 direct_(direct) { } |
| 39 | 38 |
| 40 void SpdyHttpStream::InitializeWithExistingStream(SpdyStream* spdy_stream) { | 39 void SpdyHttpStream::InitializeWithExistingStream(SpdyStream* spdy_stream) { |
| 41 stream_ = spdy_stream; | 40 stream_ = spdy_stream; |
| 42 stream_->SetDelegate(this); | 41 stream_->SetDelegate(this); |
| 43 response_headers_received_ = true; | 42 response_headers_received_ = true; |
| 44 } | 43 } |
| 45 | 44 |
| 46 SpdyHttpStream::~SpdyHttpStream() { | 45 SpdyHttpStream::~SpdyHttpStream() { |
| 47 if (stream_) | 46 if (stream_) |
| 48 stream_->DetachDelegate(); | 47 stream_->DetachDelegate(); |
| 49 } | 48 } |
| 50 | 49 |
| 51 int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info, | 50 int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info, |
| 52 const BoundNetLog& stream_net_log, | 51 const BoundNetLog& stream_net_log, |
| 53 OldCompletionCallback* callback) { | 52 const CompletionCallback& callback) { |
| 54 DCHECK(!stream_.get()); | 53 DCHECK(!stream_.get()); |
| 55 if (spdy_session_->IsClosed()) | 54 if (spdy_session_->IsClosed()) |
| 56 return ERR_CONNECTION_CLOSED; | 55 return ERR_CONNECTION_CLOSED; |
| 57 | 56 |
| 58 request_info_ = request_info; | 57 request_info_ = request_info; |
| 59 if (request_info_->method == "GET") { | 58 if (request_info_->method == "GET") { |
| 60 int error = spdy_session_->GetPushStream(request_info_->url, &stream_, | 59 int error = spdy_session_->GetPushStream(request_info_->url, &stream_, |
| 61 stream_net_log); | 60 stream_net_log); |
| 62 if (error != OK) | 61 if (error != OK) |
| 63 return error; | 62 return error; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 75 return response_info_; | 74 return response_info_; |
| 76 } | 75 } |
| 77 | 76 |
| 78 uint64 SpdyHttpStream::GetUploadProgress() const { | 77 uint64 SpdyHttpStream::GetUploadProgress() const { |
| 79 if (!request_body_stream_.get()) | 78 if (!request_body_stream_.get()) |
| 80 return 0; | 79 return 0; |
| 81 | 80 |
| 82 return request_body_stream_->position(); | 81 return request_body_stream_->position(); |
| 83 } | 82 } |
| 84 | 83 |
| 85 int SpdyHttpStream::ReadResponseHeaders(OldCompletionCallback* callback) { | 84 int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) { |
| 86 CHECK(callback); | 85 CHECK(!callback.is_null()); |
| 87 CHECK(!stream_->cancelled()); | 86 CHECK(!stream_->cancelled()); |
| 88 | 87 |
| 89 if (stream_->closed()) | 88 if (stream_->closed()) |
| 90 return stream_->response_status(); | 89 return stream_->response_status(); |
| 91 | 90 |
| 92 // Check if we already have the response headers. If so, return synchronously. | 91 // Check if we already have the response headers. If so, return synchronously. |
| 93 if(stream_->response_received()) { | 92 if(stream_->response_received()) { |
| 94 CHECK(stream_->is_idle()); | 93 CHECK(stream_->is_idle()); |
| 95 return OK; | 94 return OK; |
| 96 } | 95 } |
| 97 | 96 |
| 98 // Still waiting for the response, return IO_PENDING. | 97 // Still waiting for the response, return IO_PENDING. |
| 99 CHECK(!user_callback_); | 98 CHECK(callback_.is_null()); |
| 100 user_callback_ = callback; | 99 callback_ = callback; |
| 101 return ERR_IO_PENDING; | 100 return ERR_IO_PENDING; |
| 102 } | 101 } |
| 103 | 102 |
| 104 int SpdyHttpStream::ReadResponseBody( | 103 int SpdyHttpStream::ReadResponseBody( |
| 105 IOBuffer* buf, int buf_len, OldCompletionCallback* callback) { | 104 IOBuffer* buf, int buf_len, const CompletionCallback& callback) { |
| 106 CHECK(stream_->is_idle()); | 105 CHECK(stream_->is_idle()); |
| 107 CHECK(buf); | 106 CHECK(buf); |
| 108 CHECK(buf_len); | 107 CHECK(buf_len); |
| 109 CHECK(callback); | 108 CHECK(!callback.is_null()); |
| 110 | 109 |
| 111 // If we have data buffered, complete the IO immediately. | 110 // If we have data buffered, complete the IO immediately. |
| 112 if (!response_body_.empty()) { | 111 if (!response_body_.empty()) { |
| 113 int bytes_read = 0; | 112 int bytes_read = 0; |
| 114 while (!response_body_.empty() && buf_len > 0) { | 113 while (!response_body_.empty() && buf_len > 0) { |
| 115 scoped_refptr<IOBufferWithSize> data = response_body_.front(); | 114 scoped_refptr<IOBufferWithSize> data = response_body_.front(); |
| 116 const int bytes_to_copy = std::min(buf_len, data->size()); | 115 const int bytes_to_copy = std::min(buf_len, data->size()); |
| 117 memcpy(&(buf->data()[bytes_read]), data->data(), bytes_to_copy); | 116 memcpy(&(buf->data()[bytes_read]), data->data(), bytes_to_copy); |
| 118 buf_len -= bytes_to_copy; | 117 buf_len -= bytes_to_copy; |
| 119 if (bytes_to_copy == data->size()) { | 118 if (bytes_to_copy == data->size()) { |
| 120 response_body_.pop_front(); | 119 response_body_.pop_front(); |
| 121 } else { | 120 } else { |
| 122 const int bytes_remaining = data->size() - bytes_to_copy; | 121 const int bytes_remaining = data->size() - bytes_to_copy; |
| 123 IOBufferWithSize* new_buffer = new IOBufferWithSize(bytes_remaining); | 122 IOBufferWithSize* new_buffer = new IOBufferWithSize(bytes_remaining); |
| 124 memcpy(new_buffer->data(), &(data->data()[bytes_to_copy]), | 123 memcpy(new_buffer->data(), &(data->data()[bytes_to_copy]), |
| 125 bytes_remaining); | 124 bytes_remaining); |
| 126 response_body_.pop_front(); | 125 response_body_.pop_front(); |
| 127 response_body_.push_front(make_scoped_refptr(new_buffer)); | 126 response_body_.push_front(make_scoped_refptr(new_buffer)); |
| 128 } | 127 } |
| 129 bytes_read += bytes_to_copy; | 128 bytes_read += bytes_to_copy; |
| 130 } | 129 } |
| 131 if (spdy_session_ && spdy_session_->is_flow_control_enabled()) | 130 if (spdy_session_ && spdy_session_->is_flow_control_enabled()) |
| 132 stream_->IncreaseRecvWindowSize(bytes_read); | 131 stream_->IncreaseRecvWindowSize(bytes_read); |
| 133 return bytes_read; | 132 return bytes_read; |
| 134 } else if (stream_->closed()) { | 133 } else if (stream_->closed()) { |
| 135 return stream_->response_status(); | 134 return stream_->response_status(); |
| 136 } | 135 } |
| 137 | 136 |
| 138 CHECK(!user_callback_); | 137 CHECK(callback_.is_null()); |
| 139 CHECK(!user_buffer_); | 138 CHECK(!user_buffer_); |
| 140 CHECK_EQ(0, user_buffer_len_); | 139 CHECK_EQ(0, user_buffer_len_); |
| 141 | 140 |
| 142 user_callback_ = callback; | 141 callback_ = callback; |
| 143 user_buffer_ = buf; | 142 user_buffer_ = buf; |
| 144 user_buffer_len_ = buf_len; | 143 user_buffer_len_ = buf_len; |
| 145 return ERR_IO_PENDING; | 144 return ERR_IO_PENDING; |
| 146 } | 145 } |
| 147 | 146 |
| 148 void SpdyHttpStream::Close(bool not_reusable) { | 147 void SpdyHttpStream::Close(bool not_reusable) { |
| 149 // Note: the not_reusable flag has no meaning for SPDY streams. | 148 // Note: the not_reusable flag has no meaning for SPDY streams. |
| 150 | 149 |
| 151 Cancel(); | 150 Cancel(); |
| 152 } | 151 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 183 } | 182 } |
| 184 | 183 |
| 185 void SpdyHttpStream::set_chunk_callback(ChunkCallback* callback) { | 184 void SpdyHttpStream::set_chunk_callback(ChunkCallback* callback) { |
| 186 if (request_body_stream_ != NULL) | 185 if (request_body_stream_ != NULL) |
| 187 request_body_stream_->set_chunk_callback(callback); | 186 request_body_stream_->set_chunk_callback(callback); |
| 188 } | 187 } |
| 189 | 188 |
| 190 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers, | 189 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers, |
| 191 UploadDataStream* request_body, | 190 UploadDataStream* request_body, |
| 192 HttpResponseInfo* response, | 191 HttpResponseInfo* response, |
| 193 OldCompletionCallback* callback) { | 192 const CompletionCallback& callback) { |
| 194 base::Time request_time = base::Time::Now(); | 193 base::Time request_time = base::Time::Now(); |
| 195 CHECK(stream_.get()); | 194 CHECK(stream_.get()); |
| 196 | 195 |
| 197 stream_->SetDelegate(this); | 196 stream_->SetDelegate(this); |
| 198 | 197 |
| 199 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); | 198 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); |
| 200 CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, | 199 CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, |
| 201 headers.get(), direct_); | 200 headers.get(), direct_); |
| 202 stream_->set_spdy_headers(headers); | 201 stream_->set_spdy_headers(headers); |
| 203 | 202 |
| 204 stream_->SetRequestTime(request_time); | 203 stream_->SetRequestTime(request_time); |
| 205 // This should only get called in the case of a request occurring | 204 // This should only get called in the case of a request occurring |
| 206 // during server push that has already begun but hasn't finished, | 205 // during server push that has already begun but hasn't finished, |
| 207 // so we set the response's request time to be the actual one | 206 // so we set the response's request time to be the actual one |
| 208 if (response_info_) | 207 if (response_info_) |
| 209 response_info_->request_time = request_time; | 208 response_info_->request_time = request_time; |
| 210 | 209 |
| 211 CHECK(!request_body_stream_.get()); | 210 CHECK(!request_body_stream_.get()); |
| 212 if (request_body) { | 211 if (request_body) { |
| 213 if (request_body->size() || request_body->is_chunked()) | 212 if (request_body->size() || request_body->is_chunked()) |
| 214 request_body_stream_.reset(request_body); | 213 request_body_stream_.reset(request_body); |
| 215 else | 214 else |
| 216 delete request_body; | 215 delete request_body; |
| 217 } | 216 } |
| 218 | 217 |
| 219 CHECK(callback); | 218 CHECK(!callback.is_null()); |
| 220 CHECK(!stream_->cancelled()); | 219 CHECK(!stream_->cancelled()); |
| 221 CHECK(response); | 220 CHECK(response); |
| 222 | 221 |
| 223 if (!stream_->pushed() && stream_->closed()) { | 222 if (!stream_->pushed() && stream_->closed()) { |
| 224 if (stream_->response_status() == OK) | 223 if (stream_->response_status() == OK) |
| 225 return ERR_FAILED; | 224 return ERR_FAILED; |
| 226 else | 225 else |
| 227 return stream_->response_status(); | 226 return stream_->response_status(); |
| 228 } | 227 } |
| 229 | 228 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 245 // Put the peer's IP address and port into the response. | 244 // Put the peer's IP address and port into the response. |
| 246 AddressList address; | 245 AddressList address; |
| 247 int result = stream_->GetPeerAddress(&address); | 246 int result = stream_->GetPeerAddress(&address); |
| 248 if (result != OK) | 247 if (result != OK) |
| 249 return result; | 248 return result; |
| 250 response_info_->socket_address = HostPortPair::FromAddrInfo(address.head()); | 249 response_info_->socket_address = HostPortPair::FromAddrInfo(address.head()); |
| 251 | 250 |
| 252 bool has_upload_data = request_body_stream_.get() != NULL; | 251 bool has_upload_data = request_body_stream_.get() != NULL; |
| 253 result = stream_->SendRequest(has_upload_data); | 252 result = stream_->SendRequest(has_upload_data); |
| 254 if (result == ERR_IO_PENDING) { | 253 if (result == ERR_IO_PENDING) { |
| 255 CHECK(!user_callback_); | 254 CHECK(callback_.is_null()); |
| 256 user_callback_ = callback; | 255 callback_ = callback; |
| 257 } | 256 } |
| 258 return result; | 257 return result; |
| 259 } | 258 } |
| 260 | 259 |
| 261 void SpdyHttpStream::Cancel() { | 260 void SpdyHttpStream::Cancel() { |
| 262 if (spdy_session_) | 261 if (spdy_session_) |
| 263 spdy_session_->CancelPendingCreateStreams(&stream_); | 262 spdy_session_->CancelPendingCreateStreams(&stream_); |
| 264 user_callback_ = NULL; | 263 callback_.Reset(); |
| 265 if (stream_) | 264 if (stream_) |
| 266 stream_->Cancel(); | 265 stream_->Cancel(); |
| 267 } | 266 } |
| 268 | 267 |
| 269 bool SpdyHttpStream::OnSendHeadersComplete(int status) { | 268 bool SpdyHttpStream::OnSendHeadersComplete(int status) { |
| 270 if (user_callback_) | 269 if (!callback_.is_null()) |
| 271 DoCallback(status); | 270 DoCallback(status); |
| 272 return request_body_stream_.get() == NULL; | 271 return request_body_stream_.get() == NULL; |
| 273 } | 272 } |
| 274 | 273 |
| 275 int SpdyHttpStream::OnSendBody() { | 274 int SpdyHttpStream::OnSendBody() { |
| 276 CHECK(request_body_stream_.get()); | 275 CHECK(request_body_stream_.get()); |
| 277 | 276 |
| 278 int buf_len = static_cast<int>(request_body_stream_->buf_len()); | 277 int buf_len = static_cast<int>(request_body_stream_->buf_len()); |
| 279 if (!buf_len) | 278 if (!buf_len) |
| 280 return OK; | 279 return OK; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 // will take care of that part. | 331 // will take care of that part. |
| 333 SSLInfo ssl_info; | 332 SSLInfo ssl_info; |
| 334 stream_->GetSSLInfo(&ssl_info, | 333 stream_->GetSSLInfo(&ssl_info, |
| 335 &response_info_->was_npn_negotiated); | 334 &response_info_->was_npn_negotiated); |
| 336 response_info_->request_time = stream_->GetRequestTime(); | 335 response_info_->request_time = stream_->GetRequestTime(); |
| 337 response_info_->vary_data.Init(*request_info_, *response_info_->headers); | 336 response_info_->vary_data.Init(*request_info_, *response_info_->headers); |
| 338 // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control | 337 // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control |
| 339 // frame has been received and processed. Move to framer? | 338 // frame has been received and processed. Move to framer? |
| 340 response_info_->response_time = response_time; | 339 response_info_->response_time = response_time; |
| 341 | 340 |
| 342 if (user_callback_) | 341 if (!callback_.is_null()) |
| 343 DoCallback(status); | 342 DoCallback(status); |
| 343 |
| 344 return status; | 344 return status; |
| 345 } | 345 } |
| 346 | 346 |
| 347 void SpdyHttpStream::OnDataReceived(const char* data, int length) { | 347 void SpdyHttpStream::OnDataReceived(const char* data, int length) { |
| 348 // SpdyStream won't call us with data if the header block didn't contain a | 348 // SpdyStream won't call us with data if the header block didn't contain a |
| 349 // valid set of headers. So we don't expect to not have headers received | 349 // valid set of headers. So we don't expect to not have headers received |
| 350 // here. | 350 // here. |
| 351 DCHECK(response_headers_received_); | 351 DCHECK(response_headers_received_); |
| 352 | 352 |
| 353 // Note that data may be received for a SpdyStream prior to the user calling | 353 // Note that data may be received for a SpdyStream prior to the user calling |
| (...skipping 19 matching lines...) Expand all Loading... |
| 373 // so it is never called. | 373 // so it is never called. |
| 374 NOTREACHED(); | 374 NOTREACHED(); |
| 375 } | 375 } |
| 376 | 376 |
| 377 void SpdyHttpStream::OnClose(int status) { | 377 void SpdyHttpStream::OnClose(int status) { |
| 378 bool invoked_callback = false; | 378 bool invoked_callback = false; |
| 379 if (status == net::OK) { | 379 if (status == net::OK) { |
| 380 // We need to complete any pending buffered read now. | 380 // We need to complete any pending buffered read now. |
| 381 invoked_callback = DoBufferedReadCallback(); | 381 invoked_callback = DoBufferedReadCallback(); |
| 382 } | 382 } |
| 383 if (!invoked_callback && user_callback_) | 383 if (!invoked_callback && !callback_.is_null()) |
| 384 DoCallback(status); | 384 DoCallback(status); |
| 385 } | 385 } |
| 386 | 386 |
| 387 void SpdyHttpStream::ScheduleBufferedReadCallback() { | 387 void SpdyHttpStream::ScheduleBufferedReadCallback() { |
| 388 // If there is already a scheduled DoBufferedReadCallback, don't issue | 388 // If there is already a scheduled DoBufferedReadCallback, don't issue |
| 389 // another one. Mark that we have received more data and return. | 389 // another one. Mark that we have received more data and return. |
| 390 if (buffered_read_callback_pending_) { | 390 if (buffered_read_callback_pending_) { |
| 391 more_read_data_pending_ = true; | 391 more_read_data_pending_ = true; |
| 392 return; | 392 return; |
| 393 } | 393 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 // When more_read_data_pending_ is true, it means that more data has | 429 // When more_read_data_pending_ is true, it means that more data has |
| 430 // arrived since we started waiting. Wait a little longer and continue | 430 // arrived since we started waiting. Wait a little longer and continue |
| 431 // to buffer. | 431 // to buffer. |
| 432 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) { | 432 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) { |
| 433 ScheduleBufferedReadCallback(); | 433 ScheduleBufferedReadCallback(); |
| 434 return false; | 434 return false; |
| 435 } | 435 } |
| 436 | 436 |
| 437 int rv = 0; | 437 int rv = 0; |
| 438 if (user_buffer_) { | 438 if (user_buffer_) { |
| 439 rv = ReadResponseBody(user_buffer_, user_buffer_len_, user_callback_); | 439 rv = ReadResponseBody(user_buffer_, user_buffer_len_, callback_); |
| 440 CHECK_NE(rv, ERR_IO_PENDING); | 440 CHECK_NE(rv, ERR_IO_PENDING); |
| 441 user_buffer_ = NULL; | 441 user_buffer_ = NULL; |
| 442 user_buffer_len_ = 0; | 442 user_buffer_len_ = 0; |
| 443 DoCallback(rv); | 443 DoCallback(rv); |
| 444 return true; | 444 return true; |
| 445 } | 445 } |
| 446 return false; | 446 return false; |
| 447 } | 447 } |
| 448 | 448 |
| 449 void SpdyHttpStream::DoCallback(int rv) { | 449 void SpdyHttpStream::DoCallback(int rv) { |
| 450 CHECK_NE(rv, ERR_IO_PENDING); | 450 CHECK_NE(rv, ERR_IO_PENDING); |
| 451 CHECK(user_callback_); | 451 CHECK(!callback_.is_null()); |
| 452 | 452 |
| 453 // Since Run may result in being called back, clear user_callback_ in advance. | 453 // Since Run may result in being called back, clear user_callback_ in advance. |
| 454 OldCompletionCallback* c = user_callback_; | 454 CompletionCallback c = callback_; |
| 455 user_callback_ = NULL; | 455 callback_.Reset(); |
| 456 c->Run(rv); | 456 c.Run(rv); |
| 457 } | 457 } |
| 458 | 458 |
| 459 void SpdyHttpStream::GetSSLInfo(SSLInfo* ssl_info) { | 459 void SpdyHttpStream::GetSSLInfo(SSLInfo* ssl_info) { |
| 460 DCHECK(stream_); | 460 DCHECK(stream_); |
| 461 bool using_npn; | 461 bool using_npn; |
| 462 stream_->GetSSLInfo(ssl_info, &using_npn); | 462 stream_->GetSSLInfo(ssl_info, &using_npn); |
| 463 } | 463 } |
| 464 | 464 |
| 465 void SpdyHttpStream::GetSSLCertRequestInfo( | 465 void SpdyHttpStream::GetSSLCertRequestInfo( |
| 466 SSLCertRequestInfo* cert_request_info) { | 466 SSLCertRequestInfo* cert_request_info) { |
| 467 DCHECK(stream_); | 467 DCHECK(stream_); |
| 468 stream_->GetSSLCertRequestInfo(cert_request_info); | 468 stream_->GetSSLCertRequestInfo(cert_request_info); |
| 469 } | 469 } |
| 470 | 470 |
| 471 bool SpdyHttpStream::IsSpdyHttpStream() const { | 471 bool SpdyHttpStream::IsSpdyHttpStream() const { |
| 472 return true; | 472 return true; |
| 473 } | 473 } |
| 474 | 474 |
| 475 void SpdyHttpStream::Drain(HttpNetworkSession* session) { | 475 void SpdyHttpStream::Drain(HttpNetworkSession* session) { |
| 476 Close(false); | 476 Close(false); |
| 477 delete this; | 477 delete this; |
| 478 } | 478 } |
| 479 | 479 |
| 480 } // namespace net | 480 } // namespace net |
| OLD | NEW |