Index: net/http/http_pipelined_host_impl.cc |
diff --git a/net/http/http_pipelined_host_impl.cc b/net/http/http_pipelined_host_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c778acc61dd3dae622a0619ed68809cf72a564a8 |
--- /dev/null |
+++ b/net/http/http_pipelined_host_impl.cc |
@@ -0,0 +1,183 @@ |
+// 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_host_impl.h" |
+ |
+#include "base/stl_util.h" |
+#include "net/http/http_pipelined_connection_impl.h" |
+#include "net/http/http_pipelined_stream.h" |
+ |
+namespace net { |
+ |
+// TODO(simonjam): Run experiments to see what value minimizes evictions without |
+// costing too much performance. Until then, this is just a bad guess. |
+static const int kNumKnownSuccessesThreshold = 3; |
+ |
+class HttpPipelinedConnectionImplFactory : |
+ public HttpPipelinedConnection::Factory { |
+ public: |
+ HttpPipelinedConnection* CreateNewPipeline( |
+ ClientSocketHandle* connection, |
+ HttpPipelinedConnection::Delegate* delegate, |
+ const SSLConfig& used_ssl_config, |
+ const ProxyInfo& used_proxy_info, |
+ const BoundNetLog& net_log, |
+ bool was_npn_negotiated) OVERRIDE { |
+ return new HttpPipelinedConnectionImpl(connection, delegate, |
+ used_ssl_config, used_proxy_info, |
+ net_log, was_npn_negotiated); |
+ } |
+}; |
+ |
+HttpPipelinedHostImpl::HttpPipelinedHostImpl( |
+ HttpPipelinedHost::Delegate* delegate, |
+ const HostPortPair& origin, |
+ HttpPipelinedConnection::Factory* factory, |
+ Capability capability) |
+ : delegate_(delegate), |
+ origin_(origin), |
+ factory_(factory), |
+ capability_(capability) { |
+ if (!factory) { |
+ factory_.reset(new HttpPipelinedConnectionImplFactory()); |
+ } |
+} |
+ |
+HttpPipelinedHostImpl::~HttpPipelinedHostImpl() { |
+ CHECK(pipelines_.empty()); |
+} |
+ |
+HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnNewPipeline( |
+ ClientSocketHandle* connection, |
+ const SSLConfig& used_ssl_config, |
+ const ProxyInfo& used_proxy_info, |
+ const BoundNetLog& net_log, |
+ bool was_npn_negotiated) { |
+ if (capability_ == INCAPABLE) { |
+ return NULL; |
+ } |
+ HttpPipelinedConnection* pipeline = factory_->CreateNewPipeline( |
+ connection, this, used_ssl_config, used_proxy_info, net_log, |
+ was_npn_negotiated); |
+ PipelineInfo info; |
+ pipelines_.insert(std::make_pair(pipeline, info)); |
+ return pipeline->CreateNewStream(); |
+} |
+ |
+HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnExistingPipeline() { |
+ HttpPipelinedConnection* available_pipeline = NULL; |
+ for (PipelineInfoMap::iterator it = pipelines_.begin(); |
+ it != pipelines_.end(); ++it) { |
+ if (it->first->usable() && |
+ it->first->active() && |
+ it->first->depth() < GetPipelineCapacity() && |
+ (!available_pipeline || |
+ it->first->depth() < available_pipeline->depth())) { |
+ available_pipeline = it->first; |
+ } |
+ } |
+ if (!available_pipeline) { |
+ return NULL; |
+ } |
+ return available_pipeline->CreateNewStream(); |
+} |
+ |
+bool HttpPipelinedHostImpl::IsExistingPipelineAvailable() const { |
+ for (PipelineInfoMap::const_iterator it = pipelines_.begin(); |
+ it != pipelines_.end(); ++it) { |
+ if (it->first->usable() && |
+ it->first->active() && |
+ it->first->depth() < GetPipelineCapacity()) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+const HostPortPair& HttpPipelinedHostImpl::origin() const { |
+ return origin_; |
+} |
+ |
+void HttpPipelinedHostImpl::OnPipelineEmpty(HttpPipelinedConnection* pipeline) { |
+ CHECK(ContainsKey(pipelines_, pipeline)); |
+ pipelines_.erase(pipeline); |
+ delete pipeline; |
+ if (pipelines_.empty()) { |
+ delegate_->OnHostIdle(this); |
+ // WARNING: We'll probably be deleted here. |
+ } |
+} |
+ |
+void HttpPipelinedHostImpl::OnPipelineHasCapacity( |
+ HttpPipelinedConnection* pipeline) { |
+ CHECK(ContainsKey(pipelines_, pipeline)); |
+ if (pipeline->usable() && |
+ capability_ != INCAPABLE && |
+ pipeline->depth() < GetPipelineCapacity()) { |
+ delegate_->OnHostHasAdditionalCapacity(this); |
+ } |
+ if (!pipeline->depth()) { |
+ OnPipelineEmpty(pipeline); |
+ // WARNING: We might be deleted here. |
+ } |
+} |
+ |
+void HttpPipelinedHostImpl::OnPipelineFeedback( |
+ HttpPipelinedConnection* pipeline, |
+ HttpPipelinedConnection::Feedback feedback) { |
+ CHECK(ContainsKey(pipelines_, pipeline)); |
+ switch (feedback) { |
+ case HttpPipelinedConnection::OK: |
+ ++pipelines_[pipeline].num_successes; |
+ if (capability_ == UNKNOWN) { |
+ capability_ = PROBABLY_CAPABLE; |
+ for (PipelineInfoMap::iterator it = pipelines_.begin(); |
+ it != pipelines_.end(); ++it) { |
+ OnPipelineHasCapacity(it->first); |
mmenke
2011/12/01 17:22:48
We may want a unit test for this behavior at some
James Simonsen
2011/12/01 20:36:42
Done.
|
+ } |
+ } else if (capability_ == PROBABLY_CAPABLE && |
+ pipelines_[pipeline].num_successes >= |
+ kNumKnownSuccessesThreshold) { |
mmenke
2011/12/01 17:22:48
Could you indent this 4 more spaces, to make it a
James Simonsen
2011/12/01 20:36:42
Done.
|
+ capability_ = CAPABLE; |
+ delegate_->OnHostDeterminedCapability(this, CAPABLE); |
+ } |
+ break; |
+ |
+ case HttpPipelinedConnection::PIPELINE_SOCKET_ERROR: |
+ case HttpPipelinedConnection::OLD_HTTP_VERSION: |
+ capability_ = INCAPABLE; |
+ delegate_->OnHostDeterminedCapability(this, INCAPABLE); |
+ break; |
+ |
+ case HttpPipelinedConnection::MUST_CLOSE_CONNECTION: |
+ break; |
+ } |
+} |
+ |
+int HttpPipelinedHostImpl::GetPipelineCapacity() const { |
+ int capacity = 0; |
+ switch (capability_) { |
+ case CAPABLE: |
+ case PROBABLY_CAPABLE: |
+ capacity = max_pipeline_depth(); |
+ break; |
+ |
+ case INCAPABLE: |
+ CHECK(false); |
+ |
+ case UNKNOWN: |
+ capacity = 1; |
+ break; |
+ |
+ default: |
+ CHECK(false) << "Unkown pipeline capability: " << capability_; |
+ } |
+ return capacity; |
+} |
+ |
+HttpPipelinedHostImpl::PipelineInfo::PipelineInfo() |
+ : num_successes(0) { |
+} |
+ |
+} // namespace net |