| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |