Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1661)

Unified Diff: net/socket/websocket_transport_client_socket_pool.cc

Issue 240873003: Create WebSocketTransportClientSocketPool (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/socket/websocket_transport_client_socket_pool.cc
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
new file mode 100644
index 0000000000000000000000000000000000000000..39a2771cb2b9f7d3cc0a8de9527656f03584aa79
--- /dev/null
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -0,0 +1,645 @@
+// Copyright 2014 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/socket/websocket_transport_client_socket_pool.h"
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/websocket_endpoint_lock_manager.h"
+#include "net/socket/websocket_transport_connect_sub_job.h"
+
+namespace net {
+
+namespace {
+
+using base::TimeDelta;
+
+// TODO(ricea): For now, we implement a global timeout for compatability with
+// TransportConnectJob. Since WebSocketTransportConnectJob controls the address
+// selection process more tightly, it could do something smarter here.
+const int kTransportConnectJobTimeoutInSeconds = 240; // 4 minutes.
+
+} // namespace
+
+WebSocketTransportConnectJob::WebSocketTransportConnectJob(
+ const std::string& group_name,
+ RequestPriority priority,
+ const scoped_refptr<TransportSocketParams>& params,
+ TimeDelta timeout_duration,
+ const CompletionCallback& callback,
+ ClientSocketFactory* client_socket_factory,
+ HostResolver* host_resolver,
+ ClientSocketHandle* handle,
+ Delegate* delegate,
+ NetLog* pool_net_log,
+ const BoundNetLog& request_net_log)
+ : ConnectJob(group_name,
+ timeout_duration,
+ priority,
+ delegate,
+ BoundNetLog::Make(pool_net_log, NetLog::SOURCE_CONNECT_JOB)),
+ helper_(params, client_socket_factory, host_resolver, &connect_timing_),
+ race_result_(TransportConnectJobHelper::CONNECTION_LATENCY_UNKNOWN),
+ handle_(handle),
+ callback_(callback),
+ request_net_log_(request_net_log),
+ had_ipv4_(false),
+ had_ipv6_(false) {
+ helper_.SetOnIOComplete(this);
+}
+
+WebSocketTransportConnectJob::~WebSocketTransportConnectJob() {}
+
+LoadState WebSocketTransportConnectJob::GetLoadState() const {
+ LoadState load_state = LOAD_STATE_RESOLVING_HOST;
+ if (ipv6_job_)
+ load_state = ipv6_job_->GetLoadState();
+ // This method should return LOAD_STATE_CONNECTING in preference to
+ // LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET when possible because "waiting for
+ // available socket" implies that nothing is happening.
+ if (ipv4_job_ && load_state != LOAD_STATE_CONNECTING)
+ load_state = ipv4_job_->GetLoadState();
+ return load_state;
+}
+
+int WebSocketTransportConnectJob::DoResolveHost() {
+ return helper_.DoResolveHost(priority(), net_log());
+}
+
+int WebSocketTransportConnectJob::DoResolveHostComplete(int result) {
+ return helper_.DoResolveHostComplete(result, net_log());
+}
+
+int WebSocketTransportConnectJob::DoTransportConnect() {
+ AddressList ipv4_addresses;
+ AddressList ipv6_addresses;
+ int result = ERR_UNEXPECTED;
+ helper_.set_next_state(
+ TransportConnectJobHelper::STATE_TRANSPORT_CONNECT_COMPLETE);
+
+ for (AddressList::const_iterator it = helper_.addresses().begin();
+ it != helper_.addresses().end();
+ ++it) {
+ switch (it->GetFamily()) {
+ case ADDRESS_FAMILY_IPV4:
+ ipv4_addresses.push_back(*it);
+ break;
+
+ case ADDRESS_FAMILY_IPV6:
+ ipv6_addresses.push_back(*it);
+ break;
+
+ default:
+ DVLOG(1) << "Unexpected ADDRESS_FAMILY: " << it->GetFamily();
+ break;
+ }
+ }
+
+ if (!ipv4_addresses.empty()) {
+ had_ipv4_ = true;
+ ipv4_job_.reset(new WebSocketTransportConnectSubJob(
+ ipv4_addresses, this, SUB_JOB_IPV4));
+ }
+
+ if (!ipv6_addresses.empty()) {
+ had_ipv6_ = true;
+ ipv6_job_.reset(new WebSocketTransportConnectSubJob(
+ ipv6_addresses, this, SUB_JOB_IPV6));
+ result = ipv6_job_->Start();
+ switch (result) {
+ case OK:
+ SetSocket(ipv6_job_->PassSocket());
+ race_result_ =
+ had_ipv4_
+ ? TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_RACEABLE
+ : TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_SOLO;
+ return result;
+
+ case ERR_IO_PENDING:
+ if (ipv4_job_) {
+ // This use of base::Unretained is safe because |fallback_timer_| is
+ // owned by this object.
+ fallback_timer_.Start(
+ FROM_HERE,
+ TimeDelta::FromMilliseconds(
+ TransportConnectJobHelper::kIPv6FallbackTimerInMs),
+ base::Bind(&WebSocketTransportConnectJob::StartIPv4JobAsync,
+ base::Unretained(this)));
+ }
+ return result;
+
+ default:
+ ipv6_job_.reset();
+ }
+ }
+
+ DCHECK(!ipv6_job_);
+ if (ipv4_job_) {
+ result = ipv4_job_->Start();
+ if (result == OK) {
+ SetSocket(ipv4_job_->PassSocket());
+ race_result_ =
+ had_ipv6_
+ ? TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_WINS_RACE
+ : TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_NO_RACE;
+ }
+ }
+
+ return result;
+}
+
+int WebSocketTransportConnectJob::DoTransportConnectComplete(int result) {
+ if (result == OK)
+ helper_.HistogramDuration(race_result_);
+ return result;
+}
+
+void WebSocketTransportConnectJob::OnSubJobComplete(
+ int result,
+ WebSocketTransportConnectSubJob* job) {
+ if (result == OK) {
+ switch (job->type()) {
+ case SUB_JOB_IPV4:
+ race_result_ =
+ had_ipv6_
+ ? TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_WINS_RACE
+ : TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_NO_RACE;
+ break;
+
+ case SUB_JOB_IPV6:
+ race_result_ =
+ had_ipv4_
+ ? TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_RACEABLE
+ : TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_SOLO;
+ break;
+ }
+ SetSocket(job->PassSocket());
+
+ // Make sure all connections are cancelled even if this object fails to be
+ // deleted.
+ ipv4_job_.reset();
+ ipv6_job_.reset();
+ } else {
+ switch (job->type()) {
+ case SUB_JOB_IPV4:
+ ipv4_job_.reset();
+ break;
+
+ case SUB_JOB_IPV6:
+ ipv6_job_.reset();
+ if (ipv4_job_ && !ipv4_job_->started()) {
+ fallback_timer_.Stop();
+ result = ipv4_job_->Start();
+ if (result != ERR_IO_PENDING) {
+ OnSubJobComplete(result, ipv4_job_.get());
+ return;
+ }
+ }
+ break;
+ }
+ if (ipv4_job_ || ipv6_job_)
+ return;
+ }
+ helper_.OnIOComplete(this, result);
+}
+
+void WebSocketTransportConnectJob::StartIPv4JobAsync() {
+ DCHECK(ipv4_job_);
+ int result = ipv4_job_->Start();
+ if (result != ERR_IO_PENDING)
+ OnSubJobComplete(result, ipv4_job_.get());
+}
+
+int WebSocketTransportConnectJob::ConnectInternal() {
+ return helper_.DoConnectInternal(this);
+}
+
+WebSocketTransportClientSocketPool::WebSocketTransportClientSocketPool(
+ int max_sockets,
+ int max_sockets_per_group,
+ ClientSocketPoolHistograms* histograms,
+ HostResolver* host_resolver,
+ ClientSocketFactory* client_socket_factory,
+ NetLog* net_log)
+ : TransportClientSocketPool(max_sockets,
+ max_sockets_per_group,
+ histograms,
+ host_resolver,
+ client_socket_factory,
+ net_log),
+ connect_job_delegate_(this),
+ histograms_(histograms),
+ pool_net_log_(net_log),
+ client_socket_factory_(client_socket_factory),
+ host_resolver_(host_resolver),
+ max_sockets_(max_sockets),
+ handed_out_socket_count_(0),
+ flushing_(false),
+ weak_factory_(this) {}
+
+WebSocketTransportClientSocketPool::~WebSocketTransportClientSocketPool() {
+ // Clean up any pending connect jobs.
+ FlushWithError(ERR_ABORTED);
+ DCHECK(pending_connects_.empty());
+ DCHECK_EQ(0, handed_out_socket_count_);
+ DCHECK(stalled_request_queue_.empty());
+ DCHECK(stalled_request_map_.empty());
+}
+
+// static
+void WebSocketTransportClientSocketPool::UnlockEndpoint(
+ ClientSocketHandle* handle) {
+ DCHECK(handle->is_initialized());
+ WebSocketEndpointLockManager::GetInstance()->UnlockSocket(handle->socket());
+}
+
+int WebSocketTransportClientSocketPool::RequestSocket(
+ const std::string& group_name,
+ const void* params,
+ RequestPriority priority,
+ ClientSocketHandle* handle,
+ const CompletionCallback& callback,
+ const BoundNetLog& request_net_log) {
+ DCHECK(params);
+ const scoped_refptr<TransportSocketParams>& casted_params =
+ *static_cast<const scoped_refptr<TransportSocketParams>*>(params);
+
+ NetLogTcpClientSocketPoolRequestedSocket(request_net_log, &casted_params);
+
+ CHECK(!callback.is_null());
+ CHECK(handle);
+
+ request_net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL);
+
+ if (ReachedMaxSocketsLimit() && !casted_params->ignore_limits()) {
+ request_net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
+ // TODO(ricea): Use emplace_back when C++11 becomes allowed.
+ StalledRequest request(
+ casted_params, priority, handle, callback, request_net_log);
+ stalled_request_queue_.push_back(request);
+ StalledRequestQueue::iterator iterator = stalled_request_queue_.end();
+ --iterator;
+ DCHECK_EQ(handle, iterator->handle);
+ // Because StalledRequestQueue is a std::list, its iterators are guaranteed
+ // to remain valid as long as the elements are not removed. As long as
+ // stalled_request_queue_ and stalled_request_map_ are updated in sync, it
+ // is safe to dereference an iterator in stalled_request_map_ to find the
+ // corresponding list element.
+ stalled_request_map_.insert(
+ StalledRequestMap::value_type(handle, iterator));
+ return ERR_IO_PENDING;
+ }
+
+ scoped_ptr<WebSocketTransportConnectJob> connect_job(
+ new WebSocketTransportConnectJob(group_name,
+ priority,
+ casted_params,
+ ConnectionTimeout(),
+ callback,
+ client_socket_factory_,
+ host_resolver_,
+ handle,
+ &connect_job_delegate_,
+ pool_net_log_,
+ request_net_log));
+
+ int rv = connect_job->Connect();
+ // Regardless of the outcome of |connect_job|, it will always be bound to
+ // |handle|, since this pool uses early-binding. So the binding is logged
+ // here, without waiting for the result.
+ request_net_log.AddEvent(
+ NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+ connect_job->net_log().source().ToEventParametersCallback());
+ if (rv == OK) {
+ HandOutSocket(connect_job->PassSocket(),
+ connect_job->connect_timing(),
+ handle,
+ request_net_log);
+ request_net_log.EndEvent(NetLog::TYPE_SOCKET_POOL);
+ } else if (rv == ERR_IO_PENDING) {
+ // TODO(ricea): Implement backup job timer?
+ AddJob(handle, connect_job.Pass());
+ } else {
+ scoped_ptr<StreamSocket> error_socket;
+ connect_job->GetAdditionalErrorState(handle);
+ error_socket = connect_job->PassSocket();
+ if (error_socket) {
+ HandOutSocket(error_socket.Pass(),
+ connect_job->connect_timing(),
+ handle,
+ request_net_log);
+ }
+ }
+
+ if (rv != ERR_IO_PENDING) {
+ request_net_log.EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, rv);
+ }
+
+ return rv;
+}
+
+void WebSocketTransportClientSocketPool::RequestSockets(
+ const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ NOTIMPLEMENTED();
+}
+
+void WebSocketTransportClientSocketPool::CancelRequest(
+ const std::string& group_name,
+ ClientSocketHandle* handle) {
+ if (DeleteStalledRequest(handle))
+ return;
+ if (!DeleteJob(handle))
+ pending_callbacks_.erase(handle);
+ if (!ReachedMaxSocketsLimit() && !stalled_request_queue_.empty())
+ ActivateStalledRequest();
+}
+
+void WebSocketTransportClientSocketPool::ReleaseSocket(
+ const std::string& group_name,
+ scoped_ptr<StreamSocket> socket,
+ int id) {
+ WebSocketEndpointLockManager::GetInstance()->UnlockSocket(socket.get());
+ CHECK_GT(handed_out_socket_count_, 0);
+ --handed_out_socket_count_;
+ if (!ReachedMaxSocketsLimit() && !stalled_request_queue_.empty())
+ ActivateStalledRequest();
+}
+
+void WebSocketTransportClientSocketPool::FlushWithError(int error) {
+ // Sockets which are in LOAD_STATE_CONNECTING are in danger of unlocking
+ // sockets waiting for the endpoint lock. If they connected synchronously,
+ // then OnConnectJobComplete(). The |flushing_| flag tells this object to
+ // ignore spurious calls to OnConnectJobComplete(). It is safe to ignore those
+ // calls because this method will delete the jobs and call their callbacks
+ // anyway.
+ flushing_ = true;
+ for (PendingConnectsMap::iterator it = pending_connects_.begin();
+ it != pending_connects_.end();
+ ++it) {
+ InvokeUserCallbackLater(
+ it->second->handle(), it->second->callback(), error);
+ delete it->second, it->second = NULL;
+ }
+ pending_connects_.clear();
+ for (StalledRequestQueue::iterator it = stalled_request_queue_.begin();
+ it != stalled_request_queue_.end();
+ ++it) {
+ InvokeUserCallbackLater(it->handle, it->callback, error);
+ }
+ stalled_request_map_.clear();
+ stalled_request_queue_.clear();
+ handed_out_socket_count_ = 0;
+ flushing_ = false;
+}
+
+void WebSocketTransportClientSocketPool::CloseIdleSockets() {
+ // We have no idle sockets.
+}
+
+int WebSocketTransportClientSocketPool::IdleSocketCount() const {
+ return 0;
+}
+
+int WebSocketTransportClientSocketPool::IdleSocketCountInGroup(
+ const std::string& group_name) const {
+ return 0;
+}
+
+LoadState WebSocketTransportClientSocketPool::GetLoadState(
+ const std::string& group_name,
+ const ClientSocketHandle* handle) const {
+ if (stalled_request_map_.find(handle) != stalled_request_map_.end())
+ return LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET;
+ if (pending_callbacks_.count(handle))
+ return LOAD_STATE_CONNECTING;
+ return LookupConnectJob(handle)->GetLoadState();
+}
+
+base::DictionaryValue* WebSocketTransportClientSocketPool::GetInfoAsValue(
+ const std::string& name,
+ const std::string& type,
+ bool include_nested_pools) const {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("name", name);
+ dict->SetString("type", type);
+ dict->SetInteger("handed_out_socket_count", handed_out_socket_count_);
+ dict->SetInteger("connecting_socket_count", pending_connects_.size());
+ dict->SetInteger("idle_socket_count", 0);
+ dict->SetInteger("max_socket_count", max_sockets_);
+ dict->SetInteger("max_sockets_per_group", max_sockets_);
+ dict->SetInteger("pool_generation_number", 0);
+ return dict;
+}
+
+TimeDelta WebSocketTransportClientSocketPool::ConnectionTimeout() const {
+ return TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
+}
+
+ClientSocketPoolHistograms* WebSocketTransportClientSocketPool::histograms()
+ const {
+ return histograms_;
+}
+
+bool WebSocketTransportClientSocketPool::IsStalled() const {
+ return !stalled_request_queue_.empty();
+}
+
+void WebSocketTransportClientSocketPool::OnConnectJobComplete(
+ int result,
+ WebSocketTransportConnectJob* job) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ scoped_ptr<StreamSocket> socket = job->PassSocket();
+
+ // See comment in FlushWithError.
+ if (flushing_) {
+ WebSocketEndpointLockManager::GetInstance()->UnlockSocket(socket.get());
+ return;
+ }
+
+ BoundNetLog request_net_log = job->request_net_log();
+ CompletionCallback callback = job->callback();
+ LoadTimingInfo::ConnectTiming connect_timing = job->connect_timing();
+
+ ClientSocketHandle* const handle = job->handle();
+ bool handed_out_socket = false;
+
+ if (result == OK) {
+ DCHECK(socket.get());
+ handed_out_socket = true;
+ HandOutSocket(socket.Pass(), connect_timing, handle, request_net_log);
+ request_net_log.EndEvent(NetLog::TYPE_SOCKET_POOL);
+ } else {
+ // If we got a socket, it must contain error information so pass that
+ // up so that the caller can retrieve it.
+ job->GetAdditionalErrorState(handle);
+ if (socket.get()) {
+ handed_out_socket = true;
+ HandOutSocket(socket.Pass(), connect_timing, handle, request_net_log);
+ }
+ request_net_log.EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, result);
+ }
+ bool delete_succeeded = DeleteJob(handle);
+ DCHECK(delete_succeeded);
+ if (!handed_out_socket && !stalled_request_queue_.empty() &&
+ !ReachedMaxSocketsLimit())
+ ActivateStalledRequest();
+ InvokeUserCallbackLater(handle, callback, result);
+}
+
+void WebSocketTransportClientSocketPool::InvokeUserCallbackLater(
+ ClientSocketHandle* handle,
+ const CompletionCallback& callback,
+ int rv) {
+ DCHECK(!pending_callbacks_.count(handle));
+ pending_callbacks_.insert(handle);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&WebSocketTransportClientSocketPool::InvokeUserCallback,
+ weak_factory_.GetWeakPtr(),
+ handle,
+ callback,
+ rv));
+}
+
+void WebSocketTransportClientSocketPool::InvokeUserCallback(
+ ClientSocketHandle* handle,
+ const CompletionCallback& callback,
+ int rv) {
+ if (pending_callbacks_.erase(handle))
+ callback.Run(rv);
+}
+
+bool WebSocketTransportClientSocketPool::ReachedMaxSocketsLimit() const {
+ return handed_out_socket_count_ >= max_sockets_ ||
+ base::checked_cast<int>(pending_connects_.size()) >=
+ max_sockets_ - handed_out_socket_count_;
+}
+
+void WebSocketTransportClientSocketPool::HandOutSocket(
+ scoped_ptr<StreamSocket> socket,
+ const LoadTimingInfo::ConnectTiming& connect_timing,
+ ClientSocketHandle* handle,
+ const BoundNetLog& net_log) {
+ DCHECK(socket);
+ handle->SetSocket(socket.Pass());
+ DCHECK_EQ(ClientSocketHandle::UNUSED, handle->reuse_type());
+ DCHECK_EQ(0, handle->idle_time().InMicroseconds());
+ handle->set_pool_id(0);
+ handle->set_connect_timing(connect_timing);
+
+ net_log.AddEvent(
+ NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
+ handle->socket()->NetLog().source().ToEventParametersCallback());
+
+ ++handed_out_socket_count_;
+}
+
+void WebSocketTransportClientSocketPool::AddJob(
+ ClientSocketHandle* handle,
+ scoped_ptr<WebSocketTransportConnectJob> connect_job) {
+ bool inserted =
+ pending_connects_.insert(PendingConnectsMap::value_type(
+ handle, connect_job.release())).second;
+ DCHECK(inserted);
+}
+
+bool WebSocketTransportClientSocketPool::DeleteJob(ClientSocketHandle* handle) {
+ PendingConnectsMap::iterator it = pending_connects_.find(handle);
+ if (it == pending_connects_.end())
+ return false;
+ // Deleting a ConnectJob which holds an endpoint lock can lead to a different
+ // ConnectJob proceeding to connect. If the connect proceeds synchronously
+ // (usually because of a failure) then it can trigger that job to be
+ // deleted. |it| remains valid because std::map guarantees that erase() does
+ // not invalid iterators to other entries.
+ delete it->second, it->second = NULL;
+ DCHECK(pending_connects_.find(handle) == it);
+ pending_connects_.erase(it);
+ return true;
+}
+
+const WebSocketTransportConnectJob*
+WebSocketTransportClientSocketPool::LookupConnectJob(
+ const ClientSocketHandle* handle) const {
+ PendingConnectsMap::const_iterator it = pending_connects_.find(handle);
+ CHECK(it != pending_connects_.end());
+ return it->second;
+}
+
+void WebSocketTransportClientSocketPool::ActivateStalledRequest() {
+ DCHECK(!stalled_request_queue_.empty());
+ DCHECK(!ReachedMaxSocketsLimit());
+ // Usually we will only be able to activate one stalled request at a time,
+ // however if all the connects fail synchronously for some reason, we may be
+ // able to clear the whole queue at once.
+ while (!stalled_request_queue_.empty() && !ReachedMaxSocketsLimit()) {
+ StalledRequest request(stalled_request_queue_.front());
+ stalled_request_queue_.pop_front();
+ stalled_request_map_.erase(request.handle);
+ int rv = RequestSocket("ignored",
+ &request.params,
+ request.priority,
+ request.handle,
+ request.callback,
+ request.net_log);
+ // ActivateStalledRequest() never returns synchronously, so it is never
+ // called re-entrantly.
+ if (rv != ERR_IO_PENDING)
+ InvokeUserCallbackLater(request.handle, request.callback, rv);
+ }
+}
+
+bool WebSocketTransportClientSocketPool::DeleteStalledRequest(
+ ClientSocketHandle* handle) {
+ StalledRequestMap::iterator it = stalled_request_map_.find(handle);
+ if (it == stalled_request_map_.end())
+ return false;
+ stalled_request_queue_.erase(it->second);
+ stalled_request_map_.erase(it);
+ return true;
+}
+
+WebSocketTransportClientSocketPool::ConnectJobDelegate::ConnectJobDelegate(
+ WebSocketTransportClientSocketPool* owner)
+ : owner_(owner) {}
+
+WebSocketTransportClientSocketPool::ConnectJobDelegate::~ConnectJobDelegate() {}
+
+void
+WebSocketTransportClientSocketPool::ConnectJobDelegate::OnConnectJobComplete(
+ int result,
+ ConnectJob* job) {
+ owner_->OnConnectJobComplete(result,
+ static_cast<WebSocketTransportConnectJob*>(job));
+}
+
+WebSocketTransportClientSocketPool::StalledRequest::StalledRequest(
+ const scoped_refptr<TransportSocketParams>& params,
+ RequestPriority priority,
+ ClientSocketHandle* handle,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log)
+ : params(params),
+ priority(priority),
+ handle(handle),
+ callback(callback),
+ net_log(net_log) {}
+
+WebSocketTransportClientSocketPool::StalledRequest::~StalledRequest() {}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698