| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/http/bidirectional_stream.h" | 5 #include "net/http/bidirectional_stream.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/location.h" | 11 #include "base/location.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "base/threading/thread_task_runner_handle.h" | 15 #include "base/threading/thread_task_runner_handle.h" |
| 16 #include "base/timer/timer.h" | 16 #include "base/timer/timer.h" |
| 17 #include "base/values.h" | 17 #include "base/values.h" |
| 18 #include "net/base/load_flags.h" | 18 #include "net/base/load_flags.h" |
| 19 #include "net/base/net_errors.h" | 19 #include "net/base/net_errors.h" |
| 20 #include "net/http/bidirectional_stream_request_info.h" | 20 #include "net/http/bidirectional_stream_request_info.h" |
| 21 #include "net/http/http_log_util.h" | 21 #include "net/http/http_log_util.h" |
| 22 #include "net/http/http_network_session.h" | 22 #include "net/http/http_network_session.h" |
| 23 #include "net/http/http_response_headers.h" | 23 #include "net/http/http_response_headers.h" |
| 24 #include "net/http/http_stream.h" | 24 #include "net/http/http_stream.h" |
| 25 #include "net/log/net_log_capture_mode.h" | 25 #include "net/log/net_log_capture_mode.h" |
| 26 #include "net/log/net_log_event_type.h" |
| 27 #include "net/log/net_log_source_type.h" |
| 26 #include "net/spdy/spdy_header_block.h" | 28 #include "net/spdy/spdy_header_block.h" |
| 27 #include "net/spdy/spdy_http_utils.h" | 29 #include "net/spdy/spdy_http_utils.h" |
| 28 #include "net/ssl/ssl_cert_request_info.h" | 30 #include "net/ssl/ssl_cert_request_info.h" |
| 29 #include "net/ssl/ssl_config.h" | 31 #include "net/ssl/ssl_config.h" |
| 30 #include "url/gurl.h" | 32 #include "url/gurl.h" |
| 31 | 33 |
| 32 namespace net { | 34 namespace net { |
| 33 | 35 |
| 34 namespace { | 36 namespace { |
| 35 | 37 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 base::WrapUnique(new base::Timer(false, false))) {} | 75 base::WrapUnique(new base::Timer(false, false))) {} |
| 74 | 76 |
| 75 BidirectionalStream::BidirectionalStream( | 77 BidirectionalStream::BidirectionalStream( |
| 76 std::unique_ptr<BidirectionalStreamRequestInfo> request_info, | 78 std::unique_ptr<BidirectionalStreamRequestInfo> request_info, |
| 77 HttpNetworkSession* session, | 79 HttpNetworkSession* session, |
| 78 bool send_request_headers_automatically, | 80 bool send_request_headers_automatically, |
| 79 Delegate* delegate, | 81 Delegate* delegate, |
| 80 std::unique_ptr<base::Timer> timer) | 82 std::unique_ptr<base::Timer> timer) |
| 81 : request_info_(std::move(request_info)), | 83 : request_info_(std::move(request_info)), |
| 82 net_log_(BoundNetLog::Make(session->net_log(), | 84 net_log_(BoundNetLog::Make(session->net_log(), |
| 83 NetLog::SOURCE_BIDIRECTIONAL_STREAM)), | 85 NetLogSourceType::BIDIRECTIONAL_STREAM)), |
| 84 session_(session), | 86 session_(session), |
| 85 send_request_headers_automatically_(send_request_headers_automatically), | 87 send_request_headers_automatically_(send_request_headers_automatically), |
| 86 request_headers_sent_(false), | 88 request_headers_sent_(false), |
| 87 delegate_(delegate), | 89 delegate_(delegate), |
| 88 timer_(std::move(timer)), | 90 timer_(std::move(timer)), |
| 89 weak_factory_(this) { | 91 weak_factory_(this) { |
| 90 DCHECK(delegate_); | 92 DCHECK(delegate_); |
| 91 DCHECK(request_info_); | 93 DCHECK(request_info_); |
| 92 | 94 |
| 93 if (net_log_.IsCapturing()) { | 95 if (net_log_.IsCapturing()) { |
| 94 net_log_.BeginEvent( | 96 net_log_.BeginEvent( |
| 95 NetLog::TYPE_BIDIRECTIONAL_STREAM_ALIVE, | 97 NetLogEventType::BIDIRECTIONAL_STREAM_ALIVE, |
| 96 base::Bind(&NetLogCallback, &request_info_->url, &request_info_->method, | 98 base::Bind(&NetLogCallback, &request_info_->url, &request_info_->method, |
| 97 base::Unretained(&request_info_->extra_headers))); | 99 base::Unretained(&request_info_->extra_headers))); |
| 98 } | 100 } |
| 99 | 101 |
| 100 SSLConfig server_ssl_config; | 102 SSLConfig server_ssl_config; |
| 101 session->ssl_config_service()->GetSSLConfig(&server_ssl_config); | 103 session->ssl_config_service()->GetSSLConfig(&server_ssl_config); |
| 102 session->GetAlpnProtos(&server_ssl_config.alpn_protos); | 104 session->GetAlpnProtos(&server_ssl_config.alpn_protos); |
| 103 session->GetNpnProtos(&server_ssl_config.npn_protos); | 105 session->GetNpnProtos(&server_ssl_config.npn_protos); |
| 104 | 106 |
| 105 if (!request_info_->url.SchemeIs(url::kHttpsScheme)) { | 107 if (!request_info_->url.SchemeIs(url::kHttpsScheme)) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 121 // Check that this call cannot fail to set a non-NULL |stream_request_|. | 123 // Check that this call cannot fail to set a non-NULL |stream_request_|. |
| 122 DCHECK(stream_request_); | 124 DCHECK(stream_request_); |
| 123 // Check that HttpStreamFactory does not invoke OnBidirectionalStreamImplReady | 125 // Check that HttpStreamFactory does not invoke OnBidirectionalStreamImplReady |
| 124 // synchronously. | 126 // synchronously. |
| 125 DCHECK(!stream_impl_); | 127 DCHECK(!stream_impl_); |
| 126 } | 128 } |
| 127 | 129 |
| 128 BidirectionalStream::~BidirectionalStream() { | 130 BidirectionalStream::~BidirectionalStream() { |
| 129 UpdateHistograms(); | 131 UpdateHistograms(); |
| 130 if (net_log_.IsCapturing()) { | 132 if (net_log_.IsCapturing()) { |
| 131 net_log_.EndEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_ALIVE); | 133 net_log_.EndEvent(NetLogEventType::BIDIRECTIONAL_STREAM_ALIVE); |
| 132 } | 134 } |
| 133 } | 135 } |
| 134 | 136 |
| 135 void BidirectionalStream::SendRequestHeaders() { | 137 void BidirectionalStream::SendRequestHeaders() { |
| 136 DCHECK(stream_impl_); | 138 DCHECK(stream_impl_); |
| 137 DCHECK(!request_headers_sent_); | 139 DCHECK(!request_headers_sent_); |
| 138 DCHECK(!send_request_headers_automatically_); | 140 DCHECK(!send_request_headers_automatically_); |
| 139 | 141 |
| 140 stream_impl_->SendRequestHeaders(); | 142 stream_impl_->SendRequestHeaders(); |
| 141 } | 143 } |
| 142 | 144 |
| 143 int BidirectionalStream::ReadData(IOBuffer* buf, int buf_len) { | 145 int BidirectionalStream::ReadData(IOBuffer* buf, int buf_len) { |
| 144 DCHECK(stream_impl_); | 146 DCHECK(stream_impl_); |
| 145 | 147 |
| 146 int rv = stream_impl_->ReadData(buf, buf_len); | 148 int rv = stream_impl_->ReadData(buf, buf_len); |
| 147 if (rv > 0) { | 149 if (rv > 0) { |
| 148 read_end_time_ = base::TimeTicks::Now(); | 150 read_end_time_ = base::TimeTicks::Now(); |
| 149 net_log_.AddByteTransferEvent( | 151 net_log_.AddByteTransferEvent( |
| 150 NetLog::TYPE_BIDIRECTIONAL_STREAM_BYTES_RECEIVED, rv, buf->data()); | 152 NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_RECEIVED, rv, buf->data()); |
| 151 } else if (rv == ERR_IO_PENDING) { | 153 } else if (rv == ERR_IO_PENDING) { |
| 152 read_buffer_ = buf; | 154 read_buffer_ = buf; |
| 153 // Bytes will be logged in OnDataRead(). | 155 // Bytes will be logged in OnDataRead(). |
| 154 } | 156 } |
| 155 if (net_log_.IsCapturing()) { | 157 if (net_log_.IsCapturing()) { |
| 156 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_READ_DATA, | 158 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_READ_DATA, |
| 157 NetLog::IntCallback("rv", rv)); | 159 NetLog::IntCallback("rv", rv)); |
| 158 } | 160 } |
| 159 return rv; | 161 return rv; |
| 160 } | 162 } |
| 161 | 163 |
| 162 void BidirectionalStream::SendData(const scoped_refptr<IOBuffer>& data, | 164 void BidirectionalStream::SendData(const scoped_refptr<IOBuffer>& data, |
| 163 int length, | 165 int length, |
| 164 bool end_stream) { | 166 bool end_stream) { |
| 165 DCHECK(stream_impl_); | 167 DCHECK(stream_impl_); |
| 166 DCHECK(write_buffer_list_.empty()); | 168 DCHECK(write_buffer_list_.empty()); |
| 167 DCHECK(write_buffer_len_list_.empty()); | 169 DCHECK(write_buffer_len_list_.empty()); |
| 168 | 170 |
| 169 if (net_log_.IsCapturing()) { | 171 if (net_log_.IsCapturing()) { |
| 170 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_SEND_DATA); | 172 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_SEND_DATA); |
| 171 } | 173 } |
| 172 stream_impl_->SendData(data, length, end_stream); | 174 stream_impl_->SendData(data, length, end_stream); |
| 173 write_buffer_list_.push_back(data); | 175 write_buffer_list_.push_back(data); |
| 174 write_buffer_len_list_.push_back(length); | 176 write_buffer_len_list_.push_back(length); |
| 175 } | 177 } |
| 176 | 178 |
| 177 void BidirectionalStream::SendvData( | 179 void BidirectionalStream::SendvData( |
| 178 const std::vector<scoped_refptr<IOBuffer>>& buffers, | 180 const std::vector<scoped_refptr<IOBuffer>>& buffers, |
| 179 const std::vector<int>& lengths, | 181 const std::vector<int>& lengths, |
| 180 bool end_stream) { | 182 bool end_stream) { |
| 181 DCHECK(stream_impl_); | 183 DCHECK(stream_impl_); |
| 182 DCHECK_EQ(buffers.size(), lengths.size()); | 184 DCHECK_EQ(buffers.size(), lengths.size()); |
| 183 DCHECK(write_buffer_list_.empty()); | 185 DCHECK(write_buffer_list_.empty()); |
| 184 DCHECK(write_buffer_len_list_.empty()); | 186 DCHECK(write_buffer_len_list_.empty()); |
| 185 | 187 |
| 186 if (net_log_.IsCapturing()) { | 188 if (net_log_.IsCapturing()) { |
| 187 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_SENDV_DATA, | 189 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_SENDV_DATA, |
| 188 NetLog::IntCallback("num_buffers", buffers.size())); | 190 NetLog::IntCallback("num_buffers", buffers.size())); |
| 189 } | 191 } |
| 190 stream_impl_->SendvData(buffers, lengths, end_stream); | 192 stream_impl_->SendvData(buffers, lengths, end_stream); |
| 191 for (size_t i = 0; i < buffers.size(); ++i) { | 193 for (size_t i = 0; i < buffers.size(); ++i) { |
| 192 write_buffer_list_.push_back(buffers[i]); | 194 write_buffer_list_.push_back(buffers[i]); |
| 193 write_buffer_len_list_.push_back(lengths[i]); | 195 write_buffer_len_list_.push_back(lengths[i]); |
| 194 } | 196 } |
| 195 } | 197 } |
| 196 | 198 |
| 197 NextProto BidirectionalStream::GetProtocol() const { | 199 NextProto BidirectionalStream::GetProtocol() const { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 212 if (!stream_impl_) | 214 if (!stream_impl_) |
| 213 return 0; | 215 return 0; |
| 214 | 216 |
| 215 return stream_impl_->GetTotalSentBytes(); | 217 return stream_impl_->GetTotalSentBytes(); |
| 216 } | 218 } |
| 217 | 219 |
| 218 void BidirectionalStream::OnStreamReady(bool request_headers_sent) { | 220 void BidirectionalStream::OnStreamReady(bool request_headers_sent) { |
| 219 request_headers_sent_ = request_headers_sent; | 221 request_headers_sent_ = request_headers_sent; |
| 220 if (net_log_.IsCapturing()) { | 222 if (net_log_.IsCapturing()) { |
| 221 net_log_.AddEvent( | 223 net_log_.AddEvent( |
| 222 NetLog::TYPE_BIDIRECTIONAL_STREAM_READY, | 224 NetLogEventType::BIDIRECTIONAL_STREAM_READY, |
| 223 NetLog::BoolCallback("request_headers_sent", request_headers_sent)); | 225 NetLog::BoolCallback("request_headers_sent", request_headers_sent)); |
| 224 } | 226 } |
| 225 send_start_time_ = base::TimeTicks::Now(); | 227 send_start_time_ = base::TimeTicks::Now(); |
| 226 send_end_time_ = send_start_time_; | 228 send_end_time_ = send_start_time_; |
| 227 delegate_->OnStreamReady(request_headers_sent); | 229 delegate_->OnStreamReady(request_headers_sent); |
| 228 } | 230 } |
| 229 | 231 |
| 230 void BidirectionalStream::OnHeadersReceived( | 232 void BidirectionalStream::OnHeadersReceived( |
| 231 const SpdyHeaderBlock& response_headers) { | 233 const SpdyHeaderBlock& response_headers) { |
| 232 HttpResponseInfo response_info; | 234 HttpResponseInfo response_info; |
| 233 if (!SpdyHeadersToHttpResponse(response_headers, &response_info)) { | 235 if (!SpdyHeadersToHttpResponse(response_headers, &response_info)) { |
| 234 DLOG(WARNING) << "Invalid headers"; | 236 DLOG(WARNING) << "Invalid headers"; |
| 235 NotifyFailed(ERR_FAILED); | 237 NotifyFailed(ERR_FAILED); |
| 236 return; | 238 return; |
| 237 } | 239 } |
| 238 if (net_log_.IsCapturing()) { | 240 if (net_log_.IsCapturing()) { |
| 239 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_RECV_HEADERS, | 241 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_RECV_HEADERS, |
| 240 base::Bind(&NetLogHeadersCallback, &response_headers)); | 242 base::Bind(&NetLogHeadersCallback, &response_headers)); |
| 241 } | 243 } |
| 242 read_start_time_ = base::TimeTicks::Now(); | 244 read_start_time_ = base::TimeTicks::Now(); |
| 243 read_end_time_ = read_start_time_; | 245 read_end_time_ = read_start_time_; |
| 244 session_->http_stream_factory()->ProcessAlternativeServices( | 246 session_->http_stream_factory()->ProcessAlternativeServices( |
| 245 session_, response_info.headers.get(), | 247 session_, response_info.headers.get(), |
| 246 url::SchemeHostPort(request_info_->url)); | 248 url::SchemeHostPort(request_info_->url)); |
| 247 delegate_->OnHeadersReceived(response_headers); | 249 delegate_->OnHeadersReceived(response_headers); |
| 248 } | 250 } |
| 249 | 251 |
| 250 void BidirectionalStream::OnDataRead(int bytes_read) { | 252 void BidirectionalStream::OnDataRead(int bytes_read) { |
| 251 DCHECK(read_buffer_); | 253 DCHECK(read_buffer_); |
| 252 | 254 |
| 253 if (net_log_.IsCapturing()) { | 255 if (net_log_.IsCapturing()) { |
| 254 net_log_.AddByteTransferEvent( | 256 net_log_.AddByteTransferEvent( |
| 255 NetLog::TYPE_BIDIRECTIONAL_STREAM_BYTES_RECEIVED, bytes_read, | 257 NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_RECEIVED, bytes_read, |
| 256 read_buffer_->data()); | 258 read_buffer_->data()); |
| 257 } | 259 } |
| 258 read_end_time_ = base::TimeTicks::Now(); | 260 read_end_time_ = base::TimeTicks::Now(); |
| 259 read_buffer_ = nullptr; | 261 read_buffer_ = nullptr; |
| 260 delegate_->OnDataRead(bytes_read); | 262 delegate_->OnDataRead(bytes_read); |
| 261 } | 263 } |
| 262 | 264 |
| 263 void BidirectionalStream::OnDataSent() { | 265 void BidirectionalStream::OnDataSent() { |
| 264 DCHECK(!write_buffer_list_.empty()); | 266 DCHECK(!write_buffer_list_.empty()); |
| 265 DCHECK_EQ(write_buffer_list_.size(), write_buffer_len_list_.size()); | 267 DCHECK_EQ(write_buffer_list_.size(), write_buffer_len_list_.size()); |
| 266 | 268 |
| 267 if (net_log_.IsCapturing()) { | 269 if (net_log_.IsCapturing()) { |
| 268 if (write_buffer_list_.size() > 1) { | 270 if (write_buffer_list_.size() > 1) { |
| 269 net_log_.BeginEvent( | 271 net_log_.BeginEvent( |
| 270 NetLog::TYPE_BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED, | 272 NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED, |
| 271 NetLog::IntCallback("num_buffers_coalesced", | 273 NetLog::IntCallback("num_buffers_coalesced", |
| 272 write_buffer_list_.size())); | 274 write_buffer_list_.size())); |
| 273 } | 275 } |
| 274 for (size_t i = 0; i < write_buffer_list_.size(); ++i) { | 276 for (size_t i = 0; i < write_buffer_list_.size(); ++i) { |
| 275 net_log_.AddByteTransferEvent( | 277 net_log_.AddByteTransferEvent( |
| 276 NetLog::TYPE_BIDIRECTIONAL_STREAM_BYTES_SENT, | 278 NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT, |
| 277 write_buffer_len_list_[i], write_buffer_list_[i]->data()); | 279 write_buffer_len_list_[i], write_buffer_list_[i]->data()); |
| 278 } | 280 } |
| 279 if (write_buffer_list_.size() > 1) { | 281 if (write_buffer_list_.size() > 1) { |
| 280 net_log_.EndEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED); | 282 net_log_.EndEvent( |
| 283 NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED); |
| 281 } | 284 } |
| 282 } | 285 } |
| 283 send_end_time_ = base::TimeTicks::Now(); | 286 send_end_time_ = base::TimeTicks::Now(); |
| 284 write_buffer_list_.clear(); | 287 write_buffer_list_.clear(); |
| 285 write_buffer_len_list_.clear(); | 288 write_buffer_len_list_.clear(); |
| 286 delegate_->OnDataSent(); | 289 delegate_->OnDataSent(); |
| 287 } | 290 } |
| 288 | 291 |
| 289 void BidirectionalStream::OnTrailersReceived(const SpdyHeaderBlock& trailers) { | 292 void BidirectionalStream::OnTrailersReceived(const SpdyHeaderBlock& trailers) { |
| 290 if (net_log_.IsCapturing()) { | 293 if (net_log_.IsCapturing()) { |
| 291 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_RECV_TRAILERS, | 294 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_RECV_TRAILERS, |
| 292 base::Bind(&NetLogHeadersCallback, &trailers)); | 295 base::Bind(&NetLogHeadersCallback, &trailers)); |
| 293 } | 296 } |
| 294 read_end_time_ = base::TimeTicks::Now(); | 297 read_end_time_ = base::TimeTicks::Now(); |
| 295 delegate_->OnTrailersReceived(trailers); | 298 delegate_->OnTrailersReceived(trailers); |
| 296 } | 299 } |
| 297 | 300 |
| 298 void BidirectionalStream::OnFailed(int status) { | 301 void BidirectionalStream::OnFailed(int status) { |
| 299 if (net_log_.IsCapturing()) { | 302 if (net_log_.IsCapturing()) { |
| 300 net_log_.AddEvent(NetLog::TYPE_BIDIRECTIONAL_STREAM_FAILED, | 303 net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_FAILED, |
| 301 NetLog::IntCallback("net_error", status)); | 304 NetLog::IntCallback("net_error", status)); |
| 302 } | 305 } |
| 303 NotifyFailed(status); | 306 NotifyFailed(status); |
| 304 } | 307 } |
| 305 | 308 |
| 306 void BidirectionalStream::OnStreamReady(const SSLConfig& used_ssl_config, | 309 void BidirectionalStream::OnStreamReady(const SSLConfig& used_ssl_config, |
| 307 const ProxyInfo& used_proxy_info, | 310 const ProxyInfo& used_proxy_info, |
| 308 HttpStream* stream) { | 311 HttpStream* stream) { |
| 309 NOTREACHED(); | 312 NOTREACHED(); |
| 310 } | 313 } |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 413 UMA_HISTOGRAM_TIMES("Net.BidirectionalStream.TimeToSendEnd.QUIC", | 416 UMA_HISTOGRAM_TIMES("Net.BidirectionalStream.TimeToSendEnd.QUIC", |
| 414 send_end_time_ - start_time_); | 417 send_end_time_ - start_time_); |
| 415 UMA_HISTOGRAM_COUNTS("Net.BidirectionalStream.ReceivedBytes.QUIC", | 418 UMA_HISTOGRAM_COUNTS("Net.BidirectionalStream.ReceivedBytes.QUIC", |
| 416 stream_impl_->GetTotalReceivedBytes()); | 419 stream_impl_->GetTotalReceivedBytes()); |
| 417 UMA_HISTOGRAM_COUNTS("Net.BidirectionalStream.SentBytes.QUIC", | 420 UMA_HISTOGRAM_COUNTS("Net.BidirectionalStream.SentBytes.QUIC", |
| 418 stream_impl_->GetTotalSentBytes()); | 421 stream_impl_->GetTotalSentBytes()); |
| 419 } | 422 } |
| 420 } | 423 } |
| 421 | 424 |
| 422 } // namespace net | 425 } // namespace net |
| OLD | NEW |