diff options
author | simonjam@chromium.org <simonjam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-19 20:14:29 +0000 |
---|---|---|
committer | simonjam@chromium.org <simonjam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-19 20:14:29 +0000 |
commit | 5a60c8bb002e4573dd0809f4a78d56b4c9720add (patch) | |
tree | 4be91c33213b0e7ab66dfed7ced978e8a16b3cdc /net/http/http_pipelined_connection_impl.cc | |
parent | 8053408224c37ade07452972e01de6126d074d27 (diff) | |
download | chromium_src-5a60c8bb002e4573dd0809f4a78d56b4c9720add.zip chromium_src-5a60c8bb002e4573dd0809f4a78d56b4c9720add.tar.gz chromium_src-5a60c8bb002e4573dd0809f4a78d56b4c9720add.tar.bz2 |
Basic HTTP pipelining support.
This must be enabled in about:flags. It is naive and assumes all servers correctly implement pipelining. Proxies are not supported.
Immediate future work:
- Integration tests.
- Additional NetLog logging.
- Refactor HttpPipelinedConnectionImpl.
Long term:
- Detect broken transparent proxies.
- Detect and/or mitigate broken servers.
- Buffer HttpPipelinedStream.
- Optimize number of pipelines and their depth.
- Support proxies.
BUG=8991
TEST=net_unittests
Review URL: http://codereview.chromium.org/7289006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106364 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http/http_pipelined_connection_impl.cc')
-rw-r--r-- | net/http/http_pipelined_connection_impl.cc | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/net/http/http_pipelined_connection_impl.cc b/net/http/http_pipelined_connection_impl.cc new file mode 100644 index 0000000..33f2ef6 --- /dev/null +++ b/net/http/http_pipelined_connection_impl.cc @@ -0,0 +1,612 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_pipelined_connection_impl.h" + +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "net/base/io_buffer.h" +#include "net/http/http_pipelined_stream.h" +#include "net/http/http_request_info.h" +#include "net/http/http_stream_parser.h" +#include "net/socket/client_socket_handle.h" + +namespace net { + +HttpPipelinedConnectionImpl::HttpPipelinedConnectionImpl( + ClientSocketHandle* connection, + HttpPipelinedConnection::Delegate* delegate, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + const BoundNetLog& net_log, + bool was_npn_negotiated) + : delegate_(delegate), + connection_(connection), + used_ssl_config_(used_ssl_config), + used_proxy_info_(used_proxy_info), + net_log_(net_log), + was_npn_negotiated_(was_npn_negotiated), + read_buf_(new GrowableIOBuffer()), + next_pipeline_id_(1), + active_(false), + usable_(true), + completed_one_request_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + send_next_state_(SEND_STATE_NONE), + ALLOW_THIS_IN_INITIALIZER_LIST(send_io_callback_( + this, &HttpPipelinedConnectionImpl::OnSendIOCallback)), + send_user_callback_(NULL), + read_next_state_(READ_STATE_NONE), + ALLOW_THIS_IN_INITIALIZER_LIST(read_io_callback_( + this, &HttpPipelinedConnectionImpl::OnReadIOCallback)), + read_user_callback_(NULL) { + CHECK(connection_.get()); +} + +HttpPipelinedConnectionImpl::~HttpPipelinedConnectionImpl() { + CHECK_EQ(depth(), 0); + CHECK(stream_info_map_.empty()); + CHECK(deferred_request_queue_.empty()); + CHECK(request_order_.empty()); + CHECK_EQ(send_next_state_, SEND_STATE_NONE); + CHECK_EQ(read_next_state_, READ_STATE_NONE); + CHECK(!send_user_callback_); + CHECK(!read_user_callback_); + if (!usable_) { + connection_->socket()->Disconnect(); + } + connection_->Reset(); +} + +HttpPipelinedStream* HttpPipelinedConnectionImpl::CreateNewStream() { + int pipeline_id = next_pipeline_id_++; + CHECK(pipeline_id); + HttpPipelinedStream* stream = new HttpPipelinedStream(this, pipeline_id); + stream_info_map_.insert(std::make_pair(pipeline_id, StreamInfo())); + return stream; +} + +void HttpPipelinedConnectionImpl::InitializeParser( + int pipeline_id, + const HttpRequestInfo* request, + const BoundNetLog& net_log) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(!stream_info_map_[pipeline_id].parser.get()); + stream_info_map_[pipeline_id].state = STREAM_BOUND; + stream_info_map_[pipeline_id].parser.reset(new HttpStreamParser( + connection_.get(), request, read_buf_.get(), net_log)); + + // In case our first stream doesn't SendRequest() immediately, we should still + // allow others to use this pipeline. + if (pipeline_id == 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::ActivatePipeline)); + } +} + +void HttpPipelinedConnectionImpl::ActivatePipeline() { + if (!active_) { + active_ = true; + delegate_->OnPipelineHasCapacity(this); + } +} + +void HttpPipelinedConnectionImpl::OnStreamDeleted(int pipeline_id) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + Close(pipeline_id, false); + + if (stream_info_map_[pipeline_id].state != STREAM_CREATED && + stream_info_map_[pipeline_id].state != STREAM_UNUSED) { + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_CLOSED); + CHECK(stream_info_map_[pipeline_id].parser.get()); + stream_info_map_[pipeline_id].parser.reset(); + } + CHECK(!stream_info_map_[pipeline_id].parser.get()); + CHECK(!stream_info_map_[pipeline_id].read_headers_callback); + stream_info_map_.erase(pipeline_id); + + delegate_->OnPipelineHasCapacity(this); +} + +int HttpPipelinedConnectionImpl::SendRequest(int pipeline_id, + const std::string& request_line, + const HttpRequestHeaders& headers, + UploadDataStream* request_body, + HttpResponseInfo* response, + OldCompletionCallback* callback) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_BOUND); + if (!usable_) { + return ERR_PIPELINE_EVICTION; + } + + DeferredSendRequest deferred_request; + deferred_request.pipeline_id = pipeline_id; + deferred_request.request_line = request_line; + deferred_request.headers = headers; + deferred_request.request_body = request_body; + deferred_request.response = response; + deferred_request.callback = callback; + deferred_request_queue_.push(deferred_request); + + int rv; + if (send_next_state_ == SEND_STATE_NONE) { + send_next_state_ = SEND_STATE_NEXT_REQUEST; + rv = DoSendRequestLoop(OK); + } else { + rv = ERR_IO_PENDING; + } + ActivatePipeline(); + return rv; +} + +int HttpPipelinedConnectionImpl::DoSendRequestLoop(int result) { + int rv = result; + do { + SendRequestState state = send_next_state_; + send_next_state_ = SEND_STATE_NONE; + switch (state) { + case SEND_STATE_NEXT_REQUEST: + rv = DoSendNextRequest(rv); + break; + case SEND_STATE_COMPLETE: + rv = DoSendComplete(rv); + break; + case SEND_STATE_UNUSABLE: + rv = DoEvictPendingSendRequests(rv); + break; + default: + NOTREACHED() << "bad send state: " << state; + rv = ERR_FAILED; + break; + } + } while (rv != ERR_IO_PENDING && send_next_state_ != SEND_STATE_NONE); + return rv; +} + +void HttpPipelinedConnectionImpl::OnSendIOCallback(int result) { + CHECK(send_user_callback_); + DoSendRequestLoop(result); +} + +int HttpPipelinedConnectionImpl::DoSendNextRequest(int result) { + CHECK(!deferred_request_queue_.empty()); + const DeferredSendRequest& deferred_request = deferred_request_queue_.front(); + CHECK(ContainsKey(stream_info_map_, deferred_request.pipeline_id)); + if (stream_info_map_[deferred_request.pipeline_id].state == STREAM_CLOSED) { + deferred_request_queue_.pop(); + if (deferred_request_queue_.empty()) { + send_next_state_ = SEND_STATE_NONE; + } else { + send_next_state_ = SEND_STATE_NEXT_REQUEST; + } + return OK; + } + CHECK(stream_info_map_[deferred_request.pipeline_id].parser.get()); + int rv = stream_info_map_[deferred_request.pipeline_id].parser->SendRequest( + deferred_request.request_line, + deferred_request.headers, + deferred_request.request_body, + deferred_request.response, + &send_io_callback_); + // |result| == ERR_IO_PENDING means this function was *not* called on the same + // stack as SendRequest(). That means we returned ERR_IO_PENDING to + // SendRequest() earlier and will need to invoke its callback. + if (result == ERR_IO_PENDING || rv == ERR_IO_PENDING) { + send_user_callback_ = deferred_request.callback; + } + stream_info_map_[deferred_request.pipeline_id].state = STREAM_SENDING; + send_next_state_ = SEND_STATE_COMPLETE; + return rv; +} + +int HttpPipelinedConnectionImpl::DoSendComplete(int result) { + CHECK(!deferred_request_queue_.empty()); + const DeferredSendRequest& deferred_request = deferred_request_queue_.front(); + CHECK_EQ(stream_info_map_[deferred_request.pipeline_id].state, + STREAM_SENDING); + request_order_.push(deferred_request.pipeline_id); + stream_info_map_[deferred_request.pipeline_id].state = STREAM_SENT; + deferred_request_queue_.pop(); + if (result == ERR_SOCKET_NOT_CONNECTED && completed_one_request_) { + result = ERR_PIPELINE_EVICTION; + } + if (result < OK) { + send_next_state_ = SEND_STATE_UNUSABLE; + usable_ = false; + } + if (send_user_callback_) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::FireUserCallback, + send_user_callback_, + result)); + send_user_callback_ = NULL; + } + if (result < OK) { + return result; + } + if (deferred_request_queue_.empty()) { + send_next_state_ = SEND_STATE_NONE; + return OK; + } + send_next_state_ = SEND_STATE_NEXT_REQUEST; + return OK; +} + +int HttpPipelinedConnectionImpl::DoEvictPendingSendRequests(int result) { + send_next_state_ = SEND_STATE_NONE; + while (!deferred_request_queue_.empty()) { + const DeferredSendRequest& evicted_send = deferred_request_queue_.front(); + if (stream_info_map_[evicted_send.pipeline_id].state != STREAM_CLOSED) { + evicted_send.callback->Run(ERR_PIPELINE_EVICTION); + } + deferred_request_queue_.pop(); + } + return result; +} + +int HttpPipelinedConnectionImpl::ReadResponseHeaders( + int pipeline_id, + OldCompletionCallback* callback) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_SENT); + CHECK(!stream_info_map_[pipeline_id].read_headers_callback); + if (!usable_) { + return ERR_PIPELINE_EVICTION; + } + stream_info_map_[pipeline_id].state = STREAM_READ_PENDING; + stream_info_map_[pipeline_id].read_headers_callback = callback; + if (read_next_state_ == READ_STATE_NONE) { + read_next_state_ = READ_STATE_NEXT_HEADERS; + return DoReadHeadersLoop(OK); + } else { + return ERR_IO_PENDING; + } +} + +int HttpPipelinedConnectionImpl::DoReadHeadersLoop(int result) { + int rv = result; + do { + ReadHeadersState state = read_next_state_; + read_next_state_ = READ_STATE_NONE; + switch (state) { + case READ_STATE_NEXT_HEADERS: + rv = DoReadNextHeaders(rv); + break; + case READ_STATE_COMPLETE: + rv = DoReadHeadersComplete(rv); + break; + case READ_STATE_WAITING_FOR_CLOSE: + rv = DoReadWaitingForClose(rv); + return rv; + case READ_STATE_STREAM_CLOSED: + rv = DoReadStreamClosed(); + break; + case READ_STATE_UNUSABLE: + rv = DoEvictPendingReadHeaders(rv); + break; + case READ_STATE_NONE: + break; + default: + NOTREACHED() << "bad read state"; + rv = ERR_FAILED; + break; + } + } while (rv != ERR_IO_PENDING && read_next_state_ != READ_STATE_NONE); + return rv; +} + +void HttpPipelinedConnectionImpl::OnReadIOCallback(int result) { + CHECK(read_user_callback_); + DoReadHeadersLoop(result); +} + +int HttpPipelinedConnectionImpl::DoReadNextHeaders(int result) { + CHECK(!request_order_.empty()); + int pipeline_id = request_order_.front(); + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + if (stream_info_map_[pipeline_id].state == STREAM_CLOSED) { + // Since nobody will read whatever data is on the pipeline associated with + // this closed request, we must shut down the rest of the pipeline. + read_next_state_ = READ_STATE_UNUSABLE; + return OK; + } + if (stream_info_map_[pipeline_id].read_headers_callback == NULL) { + return ERR_IO_PENDING; + } + CHECK(stream_info_map_[pipeline_id].parser.get()); + + if (result == ERR_IO_PENDING) { + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_ACTIVE); + } else { + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_READ_PENDING); + stream_info_map_[pipeline_id].state = STREAM_ACTIVE; + } + + int rv = stream_info_map_[pipeline_id].parser->ReadResponseHeaders( + &read_io_callback_); + if (rv == ERR_IO_PENDING) { + read_next_state_ = READ_STATE_COMPLETE; + read_user_callback_ = stream_info_map_[pipeline_id].read_headers_callback; + } else if (rv < OK) { + read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; + if (rv == ERR_SOCKET_NOT_CONNECTED && completed_one_request_) + rv = ERR_PIPELINE_EVICTION; + } else { + CHECK_LE(OK, rv); + read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; + } + + // |result| == ERR_IO_PENDING means this function was *not* called on the same + // stack as ReadResponseHeaders(). That means we returned ERR_IO_PENDING to + // ReadResponseHeaders() earlier and now need to invoke its callback. + if (rv != ERR_IO_PENDING && result == ERR_IO_PENDING) { + read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; + read_user_callback_ = stream_info_map_[pipeline_id].read_headers_callback; + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::FireUserCallback, + read_user_callback_, + rv)); + } + return rv; +} + +int HttpPipelinedConnectionImpl::DoReadHeadersComplete(int result) { + read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; + if (read_user_callback_) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::FireUserCallback, + read_user_callback_, + result)); + read_user_callback_ = NULL; + } + return result; +} + +int HttpPipelinedConnectionImpl::DoReadWaitingForClose(int result) { + read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; + return result; +} + +int HttpPipelinedConnectionImpl::DoReadStreamClosed() { + CHECK(!request_order_.empty()); + int pipeline_id = request_order_.front(); + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_CLOSED); + CHECK(stream_info_map_[pipeline_id].read_headers_callback); + stream_info_map_[pipeline_id].read_headers_callback = NULL; + request_order_.pop(); + if (!usable_) { + read_next_state_ = READ_STATE_UNUSABLE; + return OK; + } else { + completed_one_request_ = true; + if (!request_order_.empty()) { + int next_pipeline_id = request_order_.front(); + CHECK(ContainsKey(stream_info_map_, next_pipeline_id)); + if (stream_info_map_[next_pipeline_id].read_headers_callback) { + stream_info_map_[next_pipeline_id].state = STREAM_ACTIVE; + read_next_state_ = READ_STATE_NEXT_HEADERS; + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::DoReadHeadersLoop, + ERR_IO_PENDING)); + return ERR_IO_PENDING; // Wait for the task to fire. + } + } + read_next_state_ = READ_STATE_NONE; + return OK; + } +} + +int HttpPipelinedConnectionImpl::DoEvictPendingReadHeaders(int result) { + while (!request_order_.empty()) { + int evicted_id = request_order_.front(); + request_order_.pop(); + if (!ContainsKey(stream_info_map_, evicted_id) || + (stream_info_map_[evicted_id].read_headers_callback == NULL)) { + continue; + } + if (stream_info_map_[evicted_id].state != STREAM_CLOSED) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpPipelinedConnectionImpl::FireUserCallback, + stream_info_map_[evicted_id].read_headers_callback, + ERR_PIPELINE_EVICTION)); + } + stream_info_map_[evicted_id].read_headers_callback = NULL; + } + read_next_state_ = READ_STATE_NONE; + return result; +} + +void HttpPipelinedConnectionImpl::Close(int pipeline_id, + bool not_reusable) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + switch (stream_info_map_[pipeline_id].state) { + case STREAM_CREATED: + stream_info_map_[pipeline_id].state = STREAM_UNUSED; + break; + + case STREAM_BOUND: + stream_info_map_[pipeline_id].state = STREAM_CLOSED; + break; + + case STREAM_SENDING: + usable_ = false; + stream_info_map_[pipeline_id].state = STREAM_CLOSED; + send_user_callback_ = NULL; + send_next_state_ = SEND_STATE_UNUSABLE; + DoSendRequestLoop(OK); + break; + + case STREAM_SENT: + case STREAM_READ_PENDING: + usable_ = false; + stream_info_map_[pipeline_id].state = STREAM_CLOSED; + stream_info_map_[pipeline_id].read_headers_callback = NULL; + if (read_next_state_ == READ_STATE_NONE) { + read_next_state_ = READ_STATE_UNUSABLE; + DoReadHeadersLoop(OK); + } + break; + + case STREAM_ACTIVE: + stream_info_map_[pipeline_id].state = STREAM_CLOSED; + if (not_reusable) { + usable_ = false; + } + read_next_state_ = READ_STATE_STREAM_CLOSED; + read_user_callback_ = NULL; + DoReadHeadersLoop(OK); + break; + + case STREAM_CLOSED: + case STREAM_UNUSED: + // TODO(simonjam): Why is Close() sometimes called twice? + break; + + default: + NOTREACHED(); + break; + } +} + +int HttpPipelinedConnectionImpl::ReadResponseBody( + int pipeline_id, + IOBuffer* buf, + int buf_len, + OldCompletionCallback* callback) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(!request_order_.empty()); + CHECK_EQ(pipeline_id, request_order_.front()); + CHECK(stream_info_map_[pipeline_id].parser.get()); + return stream_info_map_[pipeline_id].parser->ReadResponseBody( + buf, buf_len, callback); +} + +uint64 HttpPipelinedConnectionImpl::GetUploadProgress(int pipeline_id) const { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); + return stream_info_map_.find(pipeline_id)->second.parser->GetUploadProgress(); +} + +HttpResponseInfo* HttpPipelinedConnectionImpl::GetResponseInfo( + int pipeline_id) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); + return stream_info_map_.find(pipeline_id)->second.parser->GetResponseInfo(); +} + +bool HttpPipelinedConnectionImpl::IsResponseBodyComplete( + int pipeline_id) const { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); + return stream_info_map_.find(pipeline_id)->second.parser-> + IsResponseBodyComplete(); +} + +bool HttpPipelinedConnectionImpl::CanFindEndOfResponse(int pipeline_id) const { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); + return stream_info_map_.find(pipeline_id)->second.parser-> + CanFindEndOfResponse(); +} + +bool HttpPipelinedConnectionImpl::IsMoreDataBuffered(int pipeline_id) const { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + return read_buf_->offset() != 0; +} + +bool HttpPipelinedConnectionImpl::IsConnectionReused(int pipeline_id) const { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + if (pipeline_id > 1) { + return true; + } + ClientSocketHandle::SocketReuseType reuse_type = connection_->reuse_type(); + return connection_->is_reused() || + reuse_type == ClientSocketHandle::UNUSED_IDLE; +} + +void HttpPipelinedConnectionImpl::SetConnectionReused(int pipeline_id) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + connection_->set_is_reused(true); +} + +void HttpPipelinedConnectionImpl::GetSSLInfo(int pipeline_id, + SSLInfo* ssl_info) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_[pipeline_id].parser.get()); + return stream_info_map_[pipeline_id].parser->GetSSLInfo(ssl_info); +} + +void HttpPipelinedConnectionImpl::GetSSLCertRequestInfo( + int pipeline_id, + SSLCertRequestInfo* cert_request_info) { + CHECK(ContainsKey(stream_info_map_, pipeline_id)); + CHECK(stream_info_map_[pipeline_id].parser.get()); + return stream_info_map_[pipeline_id].parser->GetSSLCertRequestInfo( + cert_request_info); +} + +void HttpPipelinedConnectionImpl::FireUserCallback( + OldCompletionCallback* callback, + int result) { + CHECK(callback); + callback->Run(result); +} + +int HttpPipelinedConnectionImpl::depth() const { + return stream_info_map_.size(); +} + +bool HttpPipelinedConnectionImpl::usable() const { + return usable_; +} + +bool HttpPipelinedConnectionImpl::active() const { + return active_; +} + +const SSLConfig& HttpPipelinedConnectionImpl::used_ssl_config() const { + return used_ssl_config_; +} + +const ProxyInfo& HttpPipelinedConnectionImpl::used_proxy_info() const { + return used_proxy_info_; +} + +const NetLog::Source& HttpPipelinedConnectionImpl::source() const { + return net_log_.source(); +} + +bool HttpPipelinedConnectionImpl::was_npn_negotiated() const { + return was_npn_negotiated_; +} + +HttpPipelinedConnectionImpl::DeferredSendRequest::DeferredSendRequest() { +} + +HttpPipelinedConnectionImpl::DeferredSendRequest::~DeferredSendRequest() { +} + +HttpPipelinedConnectionImpl::StreamInfo::StreamInfo() + : read_headers_callback(NULL), + state(STREAM_CREATED) { +} + +HttpPipelinedConnectionImpl::StreamInfo::~StreamInfo() { +} + +} // namespace net |