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. 4 5 #include "net/http/http_pipelined_host_impl.h" 6 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" 11 12 namespace net { 13 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; 17 18 HttpPipelinedHostImpl::HttpPipelinedHostImpl( 19 HttpPipelinedHost::Delegate* delegate, 20 const HttpPipelinedHost::Key& key, 21 HttpPipelinedConnection::Factory* factory, 22 HttpPipelinedHostCapability capability) 23 : delegate_(delegate), 24 key_(key), 25 factory_(factory), 26 capability_(capability) { 27 if (!factory) { 28 factory_.reset(new HttpPipelinedConnectionImpl::Factory()); 29 } 30 } 31 32 HttpPipelinedHostImpl::~HttpPipelinedHostImpl() { 33 CHECK(pipelines_.empty()); 34 } 35 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) { 44 return NULL; 45 } 46 HttpPipelinedConnection* pipeline = factory_->CreateNewPipeline( 47 connection, this, key_.origin(), used_ssl_config, used_proxy_info, 48 net_log, was_npn_negotiated, protocol_negotiated); 49 PipelineInfo info; 50 pipelines_.insert(std::make_pair(pipeline, info)); 51 return pipeline->CreateNewStream(); 52 } 53 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; 62 } 63 } 64 if (!available_pipeline) { 65 return NULL; 66 } 67 return available_pipeline->CreateNewStream(); 68 } 69 70 bool HttpPipelinedHostImpl::IsExistingPipelineAvailable() const { 71 for (PipelineInfoMap::const_iterator it = pipelines_.begin(); 72 it != pipelines_.end(); ++it) { 73 if (CanPipelineAcceptRequests(it->first)) { 74 return true; 75 } 76 } 77 return false; 78 } 79 80 const HttpPipelinedHost::Key& HttpPipelinedHostImpl::GetKey() const { 81 return key_; 82 } 83 84 void HttpPipelinedHostImpl::OnPipelineEmpty(HttpPipelinedConnection* pipeline) { 85 CHECK(ContainsKey(pipelines_, pipeline)); 86 pipelines_.erase(pipeline); 87 delete pipeline; 88 if (pipelines_.empty()) { 89 delegate_->OnHostIdle(this); 90 // WARNING: We'll probably be deleted here. 91 } 92 } 93 94 void HttpPipelinedHostImpl::OnPipelineHasCapacity( 95 HttpPipelinedConnection* pipeline) { 96 CHECK(ContainsKey(pipelines_, pipeline)); 97 if (CanPipelineAcceptRequests(pipeline)) { 98 delegate_->OnHostHasAdditionalCapacity(this); 99 } 100 if (!pipeline->depth()) { 101 OnPipelineEmpty(pipeline); 102 // WARNING: We might be deleted here. 103 } 104 } 105 106 void HttpPipelinedHostImpl::OnPipelineFeedback( 107 HttpPipelinedConnection* pipeline, 108 HttpPipelinedConnection::Feedback feedback) { 109 CHECK(ContainsKey(pipelines_, pipeline)); 110 switch (feedback) { 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); 121 } 122 break; 123 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); 134 } 135 break; 136 137 case HttpPipelinedConnection::OLD_HTTP_VERSION: 138 case HttpPipelinedConnection::AUTHENTICATION_REQUIRED: 139 capability_ = PIPELINE_INCAPABLE; 140 delegate_->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE); 141 break; 142 143 case HttpPipelinedConnection::MUST_CLOSE_CONNECTION: 144 break; 145 } 146 } 147 148 int HttpPipelinedHostImpl::GetPipelineCapacity() const { 149 int capacity = 0; 150 switch (capability_) { 151 case PIPELINE_CAPABLE: 152 case PIPELINE_PROBABLY_CAPABLE: 153 capacity = max_pipeline_depth(); 154 break; 155 156 case PIPELINE_INCAPABLE: 157 CHECK(false); 158 159 case PIPELINE_UNKNOWN: 160 capacity = 1; 161 break; 162 163 default: 164 CHECK(false) << "Unkown pipeline capability: " << capability_; 165 } 166 return capacity; 167 } 168 169 bool HttpPipelinedHostImpl::CanPipelineAcceptRequests( 170 HttpPipelinedConnection* pipeline) const { 171 return capability_ != PIPELINE_INCAPABLE && 172 pipeline->usable() && 173 pipeline->active() && 174 pipeline->depth() < GetPipelineCapacity(); 175 } 176 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); 185 } 186 } 187 } 188 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); 202 } 203 return list_value; 204 } 205 206 HttpPipelinedHostImpl::PipelineInfo::PipelineInfo() 207 : num_successes(0) { 208 } 209 210 } // namespace net 211