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
3 // found in the LICENSE file.
5 #include "net/http/http_pipelined_host_impl.h"
7 #include "base/stl_util.h"
8 #include "base/values.h"
9 #include "net/http/http_pipelined_connection_impl.h"
10 #include "net/http/http_pipelined_stream.h"
14 // TODO(simonjam): Run experiments to see what value minimizes evictions without
15 // costing too much performance. Until then, this is just a bad guess.
16 static const int kNumKnownSuccessesThreshold
= 3;
18 HttpPipelinedHostImpl::HttpPipelinedHostImpl(
19 HttpPipelinedHost::Delegate
* delegate
,
20 const HttpPipelinedHost::Key
& key
,
21 HttpPipelinedConnection::Factory
* factory
,
22 HttpPipelinedHostCapability capability
)
23 : delegate_(delegate
),
26 capability_(capability
) {
28 factory_
.reset(new HttpPipelinedConnectionImpl::Factory());
32 HttpPipelinedHostImpl::~HttpPipelinedHostImpl() {
33 CHECK(pipelines_
.empty());
36 HttpPipelinedStream
* HttpPipelinedHostImpl::CreateStreamOnNewPipeline(
37 ClientSocketHandle
* connection
,
38 const SSLConfig
& used_ssl_config
,
39 const ProxyInfo
& used_proxy_info
,
40 const BoundNetLog
& net_log
,
41 bool was_npn_negotiated
,
42 NextProto protocol_negotiated
) {
43 if (capability_
== PIPELINE_INCAPABLE
) {
46 HttpPipelinedConnection
* pipeline
= factory_
->CreateNewPipeline(
47 connection
, this, key_
.origin(), used_ssl_config
, used_proxy_info
,
48 net_log
, was_npn_negotiated
, protocol_negotiated
);
50 pipelines_
.insert(std::make_pair(pipeline
, info
));
51 return pipeline
->CreateNewStream();
54 HttpPipelinedStream
* HttpPipelinedHostImpl::CreateStreamOnExistingPipeline() {
55 HttpPipelinedConnection
* available_pipeline
= NULL
;
56 for (PipelineInfoMap::iterator it
= pipelines_
.begin();
57 it
!= pipelines_
.end(); ++it
) {
58 if (CanPipelineAcceptRequests(it
->first
) &&
59 (!available_pipeline
||
60 it
->first
->depth() < available_pipeline
->depth())) {
61 available_pipeline
= it
->first
;
64 if (!available_pipeline
) {
67 return available_pipeline
->CreateNewStream();
70 bool HttpPipelinedHostImpl::IsExistingPipelineAvailable() const {
71 for (PipelineInfoMap::const_iterator it
= pipelines_
.begin();
72 it
!= pipelines_
.end(); ++it
) {
73 if (CanPipelineAcceptRequests(it
->first
)) {
80 const HttpPipelinedHost::Key
& HttpPipelinedHostImpl::GetKey() const {
84 void HttpPipelinedHostImpl::OnPipelineEmpty(HttpPipelinedConnection
* pipeline
) {
85 CHECK(ContainsKey(pipelines_
, pipeline
));
86 pipelines_
.erase(pipeline
);
88 if (pipelines_
.empty()) {
89 delegate_
->OnHostIdle(this);
90 // WARNING: We'll probably be deleted here.
94 void HttpPipelinedHostImpl::OnPipelineHasCapacity(
95 HttpPipelinedConnection
* pipeline
) {
96 CHECK(ContainsKey(pipelines_
, pipeline
));
97 if (CanPipelineAcceptRequests(pipeline
)) {
98 delegate_
->OnHostHasAdditionalCapacity(this);
100 if (!pipeline
->depth()) {
101 OnPipelineEmpty(pipeline
);
102 // WARNING: We might be deleted here.
106 void HttpPipelinedHostImpl::OnPipelineFeedback(
107 HttpPipelinedConnection
* pipeline
,
108 HttpPipelinedConnection::Feedback feedback
) {
109 CHECK(ContainsKey(pipelines_
, pipeline
));
111 case HttpPipelinedConnection::OK
:
112 ++pipelines_
[pipeline
].num_successes
;
113 if (capability_
== PIPELINE_UNKNOWN
) {
114 capability_
= PIPELINE_PROBABLY_CAPABLE
;
115 NotifyAllPipelinesHaveCapacity();
116 } else if (capability_
== PIPELINE_PROBABLY_CAPABLE
&&
117 pipelines_
[pipeline
].num_successes
>=
118 kNumKnownSuccessesThreshold
) {
119 capability_
= PIPELINE_CAPABLE
;
120 delegate_
->OnHostDeterminedCapability(this, PIPELINE_CAPABLE
);
124 case HttpPipelinedConnection::PIPELINE_SOCKET_ERROR
:
125 // Socket errors on the initial request - when no other requests are
126 // pipelined - can't be due to pipelining.
127 if (pipelines_
[pipeline
].num_successes
> 0 || pipeline
->depth() > 1) {
128 // TODO(simonjam): This may be needlessly harsh. For example, pogo.com
129 // only returns a socket error once after the root document, but is
130 // otherwise able to pipeline just fine. Consider being more persistent
131 // and only give up on pipelining if we get a couple of failures.
132 capability_
= PIPELINE_INCAPABLE
;
133 delegate_
->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE
);
137 case HttpPipelinedConnection::OLD_HTTP_VERSION
:
138 case HttpPipelinedConnection::AUTHENTICATION_REQUIRED
:
139 capability_
= PIPELINE_INCAPABLE
;
140 delegate_
->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE
);
143 case HttpPipelinedConnection::MUST_CLOSE_CONNECTION
:
148 int HttpPipelinedHostImpl::GetPipelineCapacity() const {
150 switch (capability_
) {
151 case PIPELINE_CAPABLE
:
152 case PIPELINE_PROBABLY_CAPABLE
:
153 capacity
= max_pipeline_depth();
156 case PIPELINE_INCAPABLE
:
159 case PIPELINE_UNKNOWN
:
164 CHECK(false) << "Unkown pipeline capability: " << capability_
;
169 bool HttpPipelinedHostImpl::CanPipelineAcceptRequests(
170 HttpPipelinedConnection
* pipeline
) const {
171 return capability_
!= PIPELINE_INCAPABLE
&&
172 pipeline
->usable() &&
173 pipeline
->active() &&
174 pipeline
->depth() < GetPipelineCapacity();
177 void HttpPipelinedHostImpl::NotifyAllPipelinesHaveCapacity() {
178 // Calling OnPipelineHasCapacity() can have side effects that include
179 // deleting and removing entries from |pipelines_|.
180 PipelineInfoMap pipelines_to_notify
= pipelines_
;
181 for (PipelineInfoMap::iterator it
= pipelines_to_notify
.begin();
182 it
!= pipelines_to_notify
.end(); ++it
) {
183 if (pipelines_
.find(it
->first
) != pipelines_
.end()) {
184 OnPipelineHasCapacity(it
->first
);
189 base::Value
* HttpPipelinedHostImpl::PipelineInfoToValue() const {
190 base::ListValue
* list_value
= new base::ListValue();
191 for (PipelineInfoMap::const_iterator it
= pipelines_
.begin();
192 it
!= pipelines_
.end(); ++it
) {
193 base::DictionaryValue
* pipeline_dict
= new base::DictionaryValue
;
194 pipeline_dict
->SetString("host", key_
.origin().ToString());
195 pipeline_dict
->SetBoolean("forced", false);
196 pipeline_dict
->SetInteger("depth", it
->first
->depth());
197 pipeline_dict
->SetInteger("capacity", GetPipelineCapacity());
198 pipeline_dict
->SetBoolean("usable", it
->first
->usable());
199 pipeline_dict
->SetBoolean("active", it
->first
->active());
200 pipeline_dict
->SetInteger("source_id", it
->first
->net_log().source().id
);
201 list_value
->Append(pipeline_dict
);
206 HttpPipelinedHostImpl::PipelineInfo::PipelineInfo()