123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353 |
- // Copyright (c) 2012 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_stream_factory_job.h"
- #include <algorithm>
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/check_op.h"
- #include "base/containers/contains.h"
- #include "base/feature_list.h"
- #include "base/location.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/sparse_histogram.h"
- #include "base/notreached.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_util.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/trace_event/trace_event.h"
- #include "base/values.h"
- #include "build/build_config.h"
- #include "net/base/host_port_pair.h"
- #include "net/base/port_util.h"
- #include "net/base/proxy_delegate.h"
- #include "net/base/trace_constants.h"
- #include "net/cert/cert_verifier.h"
- #include "net/dns/public/secure_dns_policy.h"
- #include "net/http/bidirectional_stream_impl.h"
- #include "net/http/http_basic_stream.h"
- #include "net/http/http_network_session.h"
- #include "net/http/http_request_info.h"
- #include "net/http/http_server_properties.h"
- #include "net/http/http_stream_factory.h"
- #include "net/http/proxy_fallback.h"
- #include "net/log/net_log.h"
- #include "net/log/net_log_capture_mode.h"
- #include "net/log/net_log_event_type.h"
- #include "net/log/net_log_source.h"
- #include "net/log/net_log_source_type.h"
- #include "net/quic/bidirectional_stream_quic_impl.h"
- #include "net/quic/quic_http_stream.h"
- #include "net/socket/client_socket_handle.h"
- #include "net/socket/client_socket_pool_manager.h"
- #include "net/socket/connect_job.h"
- #include "net/socket/ssl_client_socket.h"
- #include "net/socket/stream_socket.h"
- #include "net/spdy/bidirectional_stream_spdy_impl.h"
- #include "net/spdy/http2_push_promise_index.h"
- #include "net/spdy/spdy_http_stream.h"
- #include "net/spdy/spdy_session.h"
- #include "net/ssl/ssl_cert_request_info.h"
- #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
- #include "url/scheme_host_port.h"
- #include "url/url_constants.h"
- namespace net {
- namespace {
- // Experiment to preconnect only one connection if HttpServerProperties is
- // not supported or initialized.
- const base::Feature kLimitEarlyPreconnectsExperiment{
- "LimitEarlyPreconnects", base::FEATURE_ENABLED_BY_DEFAULT};
- } // namespace
- const char* NetLogHttpStreamJobType(HttpStreamFactory::JobType job_type) {
- switch (job_type) {
- case HttpStreamFactory::MAIN:
- return "main";
- case HttpStreamFactory::ALTERNATIVE:
- return "alternative";
- case HttpStreamFactory::DNS_ALPN_H3:
- return "dns_alpn_h3";
- case HttpStreamFactory::PRECONNECT:
- return "preconnect";
- case HttpStreamFactory::PRECONNECT_DNS_ALPN_H3:
- return "preconnect_dns_alpn_h3";
- }
- return "";
- }
- // Returns parameters associated with the start of a HTTP stream job.
- base::Value NetLogHttpStreamJobParams(const NetLogSource& source,
- const GURL& original_url,
- const GURL& url,
- bool expect_spdy,
- bool using_quic,
- HttpStreamFactory::JobType job_type,
- RequestPriority priority) {
- base::Value::Dict dict;
- if (source.IsValid())
- source.AddToEventParameters(dict);
- dict.Set("original_url", original_url.DeprecatedGetOriginAsURL().spec());
- dict.Set("url", url.DeprecatedGetOriginAsURL().spec());
- dict.Set("expect_spdy", expect_spdy);
- dict.Set("using_quic", using_quic);
- dict.Set("priority", RequestPriorityToString(priority));
- dict.Set("type", NetLogHttpStreamJobType(job_type));
- return base::Value(std::move(dict));
- }
- // Returns parameters associated with the ALPN protocol of a HTTP stream.
- base::Value NetLogHttpStreamProtoParams(NextProto negotiated_protocol) {
- base::Value::Dict dict;
- dict.Set("proto", NextProtoToString(negotiated_protocol));
- return base::Value(std::move(dict));
- }
- HttpStreamFactory::Job::Job(Delegate* delegate,
- JobType job_type,
- HttpNetworkSession* session,
- const HttpRequestInfo& request_info,
- RequestPriority priority,
- const ProxyInfo& proxy_info,
- const SSLConfig& server_ssl_config,
- const SSLConfig& proxy_ssl_config,
- url::SchemeHostPort destination,
- GURL origin_url,
- NextProto alternative_protocol,
- quic::ParsedQuicVersion quic_version,
- bool is_websocket,
- bool enable_ip_based_pooling,
- NetLog* net_log)
- : request_info_(request_info),
- priority_(priority),
- proxy_info_(proxy_info),
- server_ssl_config_(server_ssl_config),
- proxy_ssl_config_(proxy_ssl_config),
- net_log_(
- NetLogWithSource::Make(net_log, NetLogSourceType::HTTP_STREAM_JOB)),
- io_callback_(
- base::BindRepeating(&Job::OnIOComplete, base::Unretained(this))),
- connection_(std::make_unique<ClientSocketHandle>()),
- session_(session),
- destination_(std::move(destination)),
- origin_url_(origin_url),
- is_websocket_(is_websocket),
- try_websocket_over_http2_(
- is_websocket_ && origin_url_.SchemeIs(url::kWssScheme) &&
- // TODO(https://crbug.com/1277306): Remove the proxy check.
- proxy_info_.is_direct()),
- // Don't use IP connection pooling for HTTP over HTTPS proxies. It doesn't
- // get us much, and testing it is more effort than its worth.
- enable_ip_based_pooling_(
- enable_ip_based_pooling &&
- !(proxy_info_.proxy_server().is_secure_http_like() &&
- origin_url_.SchemeIs(url::kHttpScheme))),
- delegate_(delegate),
- job_type_(job_type),
- using_ssl_(origin_url_.SchemeIs(url::kHttpsScheme) ||
- origin_url_.SchemeIs(url::kWssScheme)),
- using_quic_(
- alternative_protocol == kProtoQUIC ||
- (ShouldForceQuic(session, destination_, proxy_info, using_ssl_)) ||
- job_type == DNS_ALPN_H3 || job_type == PRECONNECT_DNS_ALPN_H3),
- quic_version_(quic_version),
- expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_),
- quic_request_(session_->quic_stream_factory()),
- pushed_stream_id_(kNoPushedStreamFound),
- spdy_session_key_(
- using_quic_ ? SpdySessionKey()
- : GetSpdySessionKey(proxy_info_.proxy_server(),
- origin_url_,
- request_info_.privacy_mode,
- request_info_.socket_tag,
- request_info_.network_isolation_key,
- request_info_.secure_dns_policy)) {
- // Websocket `destination` schemes should be converted to HTTP(S).
- DCHECK(base::EqualsCaseInsensitiveASCII(destination_.scheme(),
- url::kHttpScheme) ||
- base::EqualsCaseInsensitiveASCII(destination_.scheme(),
- url::kHttpsScheme));
- // This class is specific to a single `ProxyServer`, so `proxy_info_` must be
- // non-empty. Entries beyond the first are ignored. It should simply take a
- // `ProxyServer`, but the full `ProxyInfo` is passed back to
- // `HttpNetworkTransaction`, which consumes additional fields.
- DCHECK(!proxy_info_.is_empty());
- // QUIC can only be spoken to servers, never to proxies.
- if (alternative_protocol == kProtoQUIC)
- DCHECK(proxy_info_.is_direct());
- // The Job is forced to use QUIC without a designated version, try the
- // preferred QUIC version that is supported by default.
- if (quic_version_ == quic::ParsedQuicVersion::Unsupported() &&
- ShouldForceQuic(session, destination_, proxy_info, using_ssl_)) {
- quic_version_ =
- session->context().quic_context->params()->supported_versions[0];
- }
- if (using_quic_) {
- DCHECK((quic_version_ != quic::ParsedQuicVersion::Unsupported()) ||
- (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3));
- }
- DCHECK(session);
- if (alternative_protocol != kProtoUnknown) {
- // If the alternative service protocol is specified, then the job type must
- // be either ALTERNATIVE or PRECONNECT.
- DCHECK(job_type_ == ALTERNATIVE || job_type_ == PRECONNECT);
- }
- if (expect_spdy_) {
- DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
- }
- if (using_quic_) {
- DCHECK(session_->IsQuicEnabled());
- }
- if (job_type_ == PRECONNECT || is_websocket_) {
- DCHECK(request_info_.socket_tag == SocketTag());
- }
- if (is_websocket_) {
- DCHECK(origin_url_.SchemeIsWSOrWSS());
- } else {
- DCHECK(!origin_url_.SchemeIsWSOrWSS());
- }
- }
- HttpStreamFactory::Job::~Job() {
- net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB);
- // When we're in a partially constructed state, waiting for the user to
- // provide certificate handling information or authentication, we can't reuse
- // this stream at all.
- if (next_state_ == STATE_WAITING_USER_ACTION) {
- connection_->socket()->Disconnect();
- connection_.reset();
- }
- // The stream could be in a partial state. It is not reusable.
- if (stream_.get() && next_state_ != STATE_DONE)
- stream_->Close(true /* not reusable */);
- }
- void HttpStreamFactory::Job::Start(HttpStreamRequest::StreamType stream_type) {
- stream_type_ = stream_type;
- StartInternal();
- }
- int HttpStreamFactory::Job::Preconnect(int num_streams) {
- DCHECK_GT(num_streams, 0);
- HttpServerProperties* http_server_properties =
- session_->http_server_properties();
- DCHECK(http_server_properties);
- // Preconnect one connection if either of the following is true:
- // (1) kLimitEarlyPreconnectsStreamExperiment is turned on,
- // HttpServerProperties is not initialized, and url scheme is cryptographic.
- // (2) The server supports H2 or QUIC.
- bool connect_one_stream =
- base::FeatureList::IsEnabled(kLimitEarlyPreconnectsExperiment) &&
- !http_server_properties->IsInitialized() &&
- request_info_.url.SchemeIsCryptographic();
- if (connect_one_stream || http_server_properties->SupportsRequestPriority(
- url::SchemeHostPort(request_info_.url),
- request_info_.network_isolation_key)) {
- num_streams_ = 1;
- } else {
- num_streams_ = num_streams;
- }
- return StartInternal();
- }
- int HttpStreamFactory::Job::RestartTunnelWithProxyAuth() {
- DCHECK(establishing_tunnel_);
- DCHECK(restart_with_auth_callback_);
- std::move(restart_with_auth_callback_).Run();
- return ERR_IO_PENDING;
- }
- LoadState HttpStreamFactory::Job::GetLoadState() const {
- switch (next_state_) {
- case STATE_INIT_CONNECTION_COMPLETE:
- case STATE_CREATE_STREAM_COMPLETE:
- return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
- default:
- return LOAD_STATE_IDLE;
- }
- }
- void HttpStreamFactory::Job::Resume() {
- DCHECK_EQ(job_type_, MAIN);
- DCHECK_EQ(next_state_, STATE_WAIT_COMPLETE);
- OnIOComplete(OK);
- }
- void HttpStreamFactory::Job::Orphan() {
- DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
- net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_ORPHANED);
- // Watching for SPDY sessions isn't supported on orphaned jobs.
- // TODO(mmenke): Fix that.
- spdy_session_request_.reset();
- }
- void HttpStreamFactory::Job::SetPriority(RequestPriority priority) {
- priority_ = priority;
- // Ownership of |connection_| is passed to the newly created stream
- // or H2 session in DoCreateStream(), and the consumer is not
- // notified immediately, so this call may occur when |connection_|
- // is null.
- //
- // Note that streams are created without a priority associated with them,
- // and it is up to the consumer to set their priority via
- // HttpStream::InitializeStream(). So there is no need for this code
- // to propagate priority changes to the newly created stream.
- if (connection_ && connection_->is_initialized())
- connection_->SetPriority(priority);
- // TODO(akalin): Maybe Propagate this to the preconnect state.
- }
- bool HttpStreamFactory::Job::HasAvailableSpdySession() const {
- return !using_quic_ && CanUseExistingSpdySession() &&
- session_->spdy_session_pool()->HasAvailableSession(spdy_session_key_,
- is_websocket_);
- }
- bool HttpStreamFactory::Job::HasAvailableQuicSession() const {
- if (!using_quic_)
- return false;
- bool require_dns_https_alpn = (job_type_ == DNS_ALPN_H3);
- return quic_request_.CanUseExistingSession(
- origin_url_, request_info_.privacy_mode, request_info_.socket_tag,
- request_info_.network_isolation_key, request_info_.secure_dns_policy,
- require_dns_https_alpn, destination_);
- }
- bool HttpStreamFactory::Job::TargettedSocketGroupHasActiveSocket() const {
- DCHECK(!using_quic_);
- DCHECK(!is_websocket_);
- ClientSocketPool* pool = session_->GetSocketPool(
- HttpNetworkSession::NORMAL_SOCKET_POOL, proxy_info_.proxy_server());
- DCHECK(pool);
- ClientSocketPool::GroupId connection_group(
- destination_, request_info_.privacy_mode,
- request_info_.network_isolation_key, request_info_.secure_dns_policy);
- return pool->HasActiveSocket(connection_group);
- }
- bool HttpStreamFactory::Job::was_alpn_negotiated() const {
- return was_alpn_negotiated_;
- }
- NextProto HttpStreamFactory::Job::negotiated_protocol() const {
- return negotiated_protocol_;
- }
- bool HttpStreamFactory::Job::using_spdy() const {
- return using_spdy_;
- }
- const ProxyInfo& HttpStreamFactory::Job::proxy_info() const {
- return proxy_info_;
- }
- ResolveErrorInfo HttpStreamFactory::Job::resolve_error_info() const {
- return resolve_error_info_;
- }
- void HttpStreamFactory::Job::GetSSLInfo(SSLInfo* ssl_info) {
- DCHECK(using_ssl_);
- DCHECK(!establishing_tunnel_);
- DCHECK(connection_.get() && connection_->socket());
- connection_->socket()->GetSSLInfo(ssl_info);
- }
- // static
- bool HttpStreamFactory::Job::ShouldForceQuic(
- HttpNetworkSession* session,
- const url::SchemeHostPort& destination,
- const ProxyInfo& proxy_info,
- bool using_ssl) {
- if (!session->IsQuicEnabled())
- return false;
- // If this is going through a QUIC proxy, only force QUIC for insecure
- // requests. If the request is secure, a tunnel will be needed, and those are
- // handled by the socket pools, using an HttpProxyConnectJob.
- if (proxy_info.is_quic())
- return !using_ssl;
- const QuicParams* quic_params = session->context().quic_context->params();
- // TODO(crbug.com/1206799): Consider converting `origins_to_force_quic_on` to
- // use url::SchemeHostPort.
- return (base::Contains(quic_params->origins_to_force_quic_on,
- HostPortPair()) ||
- base::Contains(quic_params->origins_to_force_quic_on,
- HostPortPair::FromSchemeHostPort(destination))) &&
- proxy_info.is_direct() &&
- base::EqualsCaseInsensitiveASCII(destination.scheme(),
- url::kHttpsScheme);
- }
- // static
- SpdySessionKey HttpStreamFactory::Job::GetSpdySessionKey(
- const ProxyServer& proxy_server,
- const GURL& origin_url,
- PrivacyMode privacy_mode,
- const SocketTag& socket_tag,
- const NetworkIsolationKey& network_isolation_key,
- SecureDnsPolicy secure_dns_policy) {
- // In the case that we're using an HTTPS proxy for an HTTP url, look for a
- // HTTP/2 proxy session *to* the proxy, instead of to the origin server.
- if (proxy_server.is_https() && origin_url.SchemeIs(url::kHttpScheme)) {
- return SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kTrue, socket_tag,
- network_isolation_key, secure_dns_policy);
- }
- return SpdySessionKey(HostPortPair::FromURL(origin_url), proxy_server,
- privacy_mode, SpdySessionKey::IsProxySession::kFalse,
- socket_tag, network_isolation_key, secure_dns_policy);
- }
- bool HttpStreamFactory::Job::CanUseExistingSpdySession() const {
- DCHECK(!using_quic_);
- if (proxy_info_.is_direct() &&
- session_->http_server_properties()->RequiresHTTP11(
- url::SchemeHostPort(request_info_.url),
- request_info_.network_isolation_key)) {
- return false;
- }
- if (is_websocket_)
- return try_websocket_over_http2_;
- DCHECK(origin_url_.SchemeIsHTTPOrHTTPS());
- // We need to make sure that if a HTTP/2 session was created for
- // https://somehost/ then we do not use that session for http://somehost:443/.
- // The only time we can use an existing session is if the request URL is
- // https (the normal case) or if we are connecting to a HTTP/2 proxy.
- // https://crbug.com/133176
- return origin_url_.SchemeIs(url::kHttpsScheme) ||
- proxy_info_.proxy_server().is_https();
- }
- void HttpStreamFactory::Job::OnStreamReadyCallback() {
- DCHECK(stream_.get());
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(!is_websocket_ || try_websocket_over_http2_);
- MaybeCopyConnectionAttemptsFromHandle();
- delegate_->OnStreamReady(this, server_ssl_config_);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnWebSocketHandshakeStreamReadyCallback() {
- DCHECK(websocket_stream_);
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(is_websocket_);
- MaybeCopyConnectionAttemptsFromHandle();
- delegate_->OnWebSocketHandshakeStreamReady(
- this, server_ssl_config_, proxy_info_, std::move(websocket_stream_));
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnBidirectionalStreamImplReadyCallback() {
- DCHECK(bidirectional_stream_impl_);
- MaybeCopyConnectionAttemptsFromHandle();
- delegate_->OnBidirectionalStreamImplReady(this, server_ssl_config_,
- proxy_info_);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnStreamFailedCallback(int result) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- MaybeCopyConnectionAttemptsFromHandle();
- delegate_->OnStreamFailed(this, result, server_ssl_config_);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnCertificateErrorCallback(
- int result,
- const SSLInfo& ssl_info) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(!spdy_session_request_);
- MaybeCopyConnectionAttemptsFromHandle();
- delegate_->OnCertificateError(this, result, server_ssl_config_, ssl_info);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnNeedsProxyAuthCallback(
- const HttpResponseInfo& response,
- HttpAuthController* auth_controller,
- base::OnceClosure restart_with_auth_callback) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(establishing_tunnel_);
- DCHECK(!restart_with_auth_callback_);
- restart_with_auth_callback_ = std::move(restart_with_auth_callback);
- // This is called out of band, so need to abort the SpdySessionRequest to
- // prevent being passed a new session while waiting on proxy auth credentials.
- spdy_session_request_.reset();
- delegate_->OnNeedsProxyAuth(this, response, server_ssl_config_, proxy_info_,
- auth_controller);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnNeedsClientAuthCallback(
- SSLCertRequestInfo* cert_info) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(!spdy_session_request_);
- delegate_->OnNeedsClientAuth(this, server_ssl_config_, cert_info);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnPreconnectsComplete(int result) {
- delegate_->OnPreconnectsComplete(this, result);
- // |this| may be deleted after this call.
- }
- void HttpStreamFactory::Job::OnIOComplete(int result) {
- TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::OnIOComplete");
- RunLoop(result);
- }
- void HttpStreamFactory::Job::RunLoop(int result) {
- TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::RunLoop");
- result = DoLoop(result);
- if (result == ERR_IO_PENDING)
- return;
- // Stop watching for new SpdySessions, to avoid receiving a new SPDY session
- // while doing anything other than waiting to establish a connection.
- spdy_session_request_.reset();
- if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&HttpStreamFactory::Job::OnPreconnectsComplete,
- ptr_factory_.GetWeakPtr(), result));
- return;
- }
- if (IsCertificateError(result)) {
- // Retrieve SSL information from the socket.
- SSLInfo ssl_info;
- GetSSLInfo(&ssl_info);
- next_state_ = STATE_WAITING_USER_ACTION;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&HttpStreamFactory::Job::OnCertificateErrorCallback,
- ptr_factory_.GetWeakPtr(), result, ssl_info));
- return;
- }
- switch (result) {
- case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(
- &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
- base::RetainedRef(connection_->ssl_cert_request_info())));
- return;
- case OK:
- next_state_ = STATE_DONE;
- if (is_websocket_) {
- DCHECK(websocket_stream_);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&Job::OnWebSocketHandshakeStreamReadyCallback,
- ptr_factory_.GetWeakPtr()));
- } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
- if (!bidirectional_stream_impl_) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
- ptr_factory_.GetWeakPtr(), ERR_FAILED));
- } else {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&Job::OnBidirectionalStreamImplReadyCallback,
- ptr_factory_.GetWeakPtr()));
- }
- } else {
- DCHECK(stream_.get());
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&Job::OnStreamReadyCallback,
- ptr_factory_.GetWeakPtr()));
- }
- return;
- default:
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
- ptr_factory_.GetWeakPtr(), result));
- return;
- }
- }
- int HttpStreamFactory::Job::DoLoop(int result) {
- DCHECK_NE(next_state_, STATE_NONE);
- int rv = result;
- do {
- State state = next_state_;
- next_state_ = STATE_NONE;
- switch (state) {
- case STATE_START:
- DCHECK_EQ(OK, rv);
- rv = DoStart();
- break;
- case STATE_WAIT:
- DCHECK_EQ(OK, rv);
- rv = DoWait();
- break;
- case STATE_WAIT_COMPLETE:
- rv = DoWaitComplete(rv);
- break;
- case STATE_INIT_CONNECTION:
- DCHECK_EQ(OK, rv);
- rv = DoInitConnection();
- break;
- case STATE_INIT_CONNECTION_COMPLETE:
- rv = DoInitConnectionComplete(rv);
- break;
- case STATE_WAITING_USER_ACTION:
- rv = DoWaitingUserAction(rv);
- break;
- case STATE_CREATE_STREAM:
- DCHECK_EQ(OK, rv);
- rv = DoCreateStream();
- break;
- case STATE_CREATE_STREAM_COMPLETE:
- rv = DoCreateStreamComplete(rv);
- break;
- default:
- NOTREACHED() << "bad state";
- rv = ERR_FAILED;
- break;
- }
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
- return rv;
- }
- int HttpStreamFactory::Job::StartInternal() {
- CHECK_EQ(STATE_NONE, next_state_);
- next_state_ = STATE_START;
- RunLoop(OK);
- return ERR_IO_PENDING;
- }
- int HttpStreamFactory::Job::DoStart() {
- const NetLogWithSource* net_log = delegate_->GetNetLog();
- if (net_log) {
- net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB, [&] {
- return NetLogHttpStreamJobParams(net_log->source(), request_info_.url,
- origin_url_, expect_spdy_, using_quic_,
- job_type_, priority_);
- });
- net_log->AddEventReferencingSource(
- NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB, net_log_.source());
- }
- // Don't connect to restricted ports.
- if (!IsPortAllowedForScheme(destination_.port(),
- request_info_.url.scheme_piece())) {
- return ERR_UNSAFE_PORT;
- }
- if (!session_->params().enable_quic_proxies_for_https_urls &&
- proxy_info_.is_quic() && request_info_.url.SchemeIsCryptographic()) {
- return ERR_NOT_IMPLEMENTED;
- }
- next_state_ = STATE_WAIT;
- return OK;
- }
- int HttpStreamFactory::Job::DoWait() {
- next_state_ = STATE_WAIT_COMPLETE;
- bool should_wait = delegate_->ShouldWait(this);
- net_log_.AddEntryWithBoolParams(NetLogEventType::HTTP_STREAM_JOB_WAITING,
- NetLogEventPhase::BEGIN, "should_wait",
- should_wait);
- if (should_wait)
- return ERR_IO_PENDING;
- return OK;
- }
- int HttpStreamFactory::Job::DoWaitComplete(int result) {
- net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_WAITING);
- DCHECK_EQ(OK, result);
- next_state_ = STATE_INIT_CONNECTION;
- return OK;
- }
- void HttpStreamFactory::Job::ResumeInitConnection() {
- if (init_connection_already_resumed_)
- return;
- DCHECK_EQ(next_state_, STATE_INIT_CONNECTION);
- net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_RESUME_INIT_CONNECTION);
- init_connection_already_resumed_ = true;
- OnIOComplete(OK);
- }
- int HttpStreamFactory::Job::DoInitConnection() {
- net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
- int result = DoInitConnectionImpl();
- if (!expect_on_quic_host_resolution_) {
- delegate_->OnConnectionInitialized(this, result);
- }
- return result;
- }
- int HttpStreamFactory::Job::DoInitConnectionImpl() {
- DCHECK(!connection_->is_initialized());
- if (using_quic_ && !proxy_info_.is_quic() && !proxy_info_.is_direct()) {
- // QUIC can not be spoken to non-QUIC proxies. This error should not be
- // user visible, because the non-alternative Job should be resumed.
- return ERR_NO_SUPPORTED_PROXIES;
- }
- DCHECK(proxy_info_.proxy_server().is_valid());
- next_state_ = STATE_INIT_CONNECTION_COMPLETE;
- if (proxy_info_.is_secure_http_like()) {
- // Disable network fetches for HTTPS proxies, since the network requests
- // are probably going to need to go through the proxy too.
- proxy_ssl_config_.disable_cert_verification_network_fetches = true;
- }
- if (using_ssl_) {
- // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
- // TLS client authentication after the HTTP request was sent. Allow
- // renegotiation for only those connections.
- //
- // Note that this does NOT implement the provision in
- // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
- // server to request a renegotiation immediately before sending the
- // connection preface as waiting for the preface would cost the round trip
- // that False Start otherwise saves.
- server_ssl_config_.renego_allowed_default = true;
- server_ssl_config_.renego_allowed_for_protos.push_back(kProtoHTTP11);
- }
- server_ssl_config_.alpn_protos = session_->GetAlpnProtos();
- proxy_ssl_config_.alpn_protos = session_->GetAlpnProtos();
- server_ssl_config_.application_settings = session_->GetApplicationSettings();
- proxy_ssl_config_.application_settings = session_->GetApplicationSettings();
- server_ssl_config_.ignore_certificate_errors =
- session_->params().ignore_certificate_errors;
- proxy_ssl_config_.ignore_certificate_errors =
- session_->params().ignore_certificate_errors;
- // TODO(https://crbug.com/964642): Also enable 0-RTT for TLS proxies.
- server_ssl_config_.early_data_enabled = session_->params().enable_early_data;
- if (using_quic_)
- return DoInitConnectionImplQuic();
- // Check first if there is a pushed stream matching the request, or an HTTP/2
- // connection this request can pool to. If so, then go straight to using
- // that.
- if (CanUseExistingSpdySession()) {
- if (!is_websocket_) {
- session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
- spdy_session_key_, origin_url_, request_info_,
- &existing_spdy_session_, &pushed_stream_id_);
- }
- if (!existing_spdy_session_) {
- if (!spdy_session_request_) {
- // If not currently watching for an H2 session, use
- // SpdySessionPool::RequestSession() to check for a session, and start
- // watching for one.
- bool should_throttle_connect = ShouldThrottleConnectForSpdy();
- base::RepeatingClosure resume_callback =
- should_throttle_connect
- ? base::BindRepeating(
- &HttpStreamFactory::Job::ResumeInitConnection,
- ptr_factory_.GetWeakPtr())
- : base::RepeatingClosure();
- bool is_blocking_request_for_session;
- existing_spdy_session_ = session_->spdy_session_pool()->RequestSession(
- spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
- net_log_, resume_callback, this, &spdy_session_request_,
- &is_blocking_request_for_session);
- if (!existing_spdy_session_ && should_throttle_connect &&
- !is_blocking_request_for_session) {
- net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_THROTTLED);
- next_state_ = STATE_INIT_CONNECTION;
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, resume_callback, base::Milliseconds(kHTTP2ThrottleMs));
- return ERR_IO_PENDING;
- }
- } else if (enable_ip_based_pooling_) {
- // If already watching for an H2 session, still need to check for an
- // existing connection that can be reused through IP pooling, as those
- // don't post session available notifications.
- //
- // TODO(mmenke): Make sessions created through IP pooling invoke the
- // callback.
- existing_spdy_session_ =
- session_->spdy_session_pool()->FindAvailableSession(
- spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
- net_log_);
- }
- }
- if (existing_spdy_session_) {
- // Stop watching for SpdySessions.
- spdy_session_request_.reset();
- // If we're preconnecting, but we already have a SpdySession, we don't
- // actually need to preconnect any sockets, so we're done.
- if (job_type_ == PRECONNECT)
- return OK;
- using_spdy_ = true;
- next_state_ = STATE_CREATE_STREAM;
- return OK;
- }
- }
- if (proxy_info_.is_http_like())
- establishing_tunnel_ = using_ssl_;
- HttpServerProperties* http_server_properties =
- session_->http_server_properties();
- if (http_server_properties) {
- http_server_properties->MaybeForceHTTP11(
- url::SchemeHostPort(request_info_.url),
- request_info_.network_isolation_key, &server_ssl_config_);
- if (proxy_info_.is_https()) {
- http_server_properties->MaybeForceHTTP11(
- url::SchemeHostPort(
- url::kHttpsScheme,
- proxy_info_.proxy_server().host_port_pair().host(),
- proxy_info_.proxy_server().host_port_pair().port()),
- request_info_.network_isolation_key, &proxy_ssl_config_);
- }
- }
- if (job_type_ == PRECONNECT) {
- DCHECK(!is_websocket_);
- DCHECK(request_info_.socket_tag == SocketTag());
- // The lifeime of the preconnect tasks is not controlled by |connection_|.
- // It may outlives |this|. So we can't use |io_callback_| which holds
- // base::Unretained(this).
- auto callback =
- base::BindOnce(&Job::OnIOComplete, ptr_factory_.GetWeakPtr());
- return PreconnectSocketsForHttpRequest(
- destination_, request_info_.load_flags, priority_, session_,
- proxy_info_, server_ssl_config_, proxy_ssl_config_,
- request_info_.privacy_mode, request_info_.network_isolation_key,
- request_info_.secure_dns_policy, net_log_, num_streams_,
- std::move(callback));
- }
- ClientSocketPool::ProxyAuthCallback proxy_auth_callback =
- base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback,
- base::Unretained(this));
- if (is_websocket_) {
- DCHECK(request_info_.socket_tag == SocketTag());
- DCHECK_EQ(SecureDnsPolicy::kAllow, request_info_.secure_dns_policy);
- // Only offer HTTP/1.1 for WebSockets. Although RFC 8441 defines WebSockets
- // over HTTP/2, a single WSS/HTTPS origin may support HTTP over HTTP/2
- // without supporting WebSockets over HTTP/2. Offering HTTP/2 for a fresh
- // connection would break such origins.
- //
- // However, still offer HTTP/1.1 rather than skipping ALPN entirely. While
- // this will not change the application protocol (HTTP/1.1 is default), it
- // provides hardens against cross-protocol attacks and allows for the False
- // Start (RFC 7918) optimization.
- SSLConfig websocket_server_ssl_config = server_ssl_config_;
- websocket_server_ssl_config.alpn_protos = {kProtoHTTP11};
- return InitSocketHandleForWebSocketRequest(
- destination_, request_info_.load_flags, priority_, session_,
- proxy_info_, websocket_server_ssl_config, proxy_ssl_config_,
- request_info_.privacy_mode, request_info_.network_isolation_key,
- net_log_, connection_.get(), io_callback_, proxy_auth_callback);
- }
- return InitSocketHandleForHttpRequest(
- destination_, request_info_.load_flags, priority_, session_, proxy_info_,
- server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode,
- request_info_.network_isolation_key, request_info_.secure_dns_policy,
- request_info_.socket_tag, net_log_, connection_.get(), io_callback_,
- proxy_auth_callback);
- }
- int HttpStreamFactory::Job::DoInitConnectionImplQuic() {
- url::SchemeHostPort destination;
- SSLConfig* ssl_config;
- GURL url(request_info_.url);
- if (proxy_info_.is_quic()) {
- ssl_config = &proxy_ssl_config_;
- const HostPortPair& proxy_endpoint =
- proxy_info_.proxy_server().host_port_pair();
- destination = url::SchemeHostPort(url::kHttpsScheme, proxy_endpoint.host(),
- proxy_endpoint.port());
- url = destination.GetURL();
- } else {
- DCHECK(using_ssl_);
- destination = destination_;
- ssl_config = &server_ssl_config_;
- }
- DCHECK(url.SchemeIs(url::kHttpsScheme));
- bool require_dns_https_alpn =
- (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3);
- int rv = quic_request_.Request(
- std::move(destination), quic_version_, request_info_.privacy_mode,
- priority_, request_info_.socket_tag, request_info_.network_isolation_key,
- request_info_.secure_dns_policy, proxy_info_.is_direct(),
- require_dns_https_alpn, ssl_config->GetCertVerifyFlags(), url, net_log_,
- &net_error_details_,
- base::BindOnce(&Job::OnFailedOnDefaultNetwork, ptr_factory_.GetWeakPtr()),
- io_callback_);
- if (rv == OK) {
- using_existing_quic_session_ = true;
- } else if (rv == ERR_IO_PENDING) {
- // There's no available QUIC session. Inform the delegate how long to
- // delay the main job.
- delegate_->MaybeSetWaitTimeForMainJob(
- quic_request_.GetTimeDelayForWaitingJob());
- expect_on_quic_host_resolution_ = quic_request_.WaitForHostResolution(
- base::BindOnce(&Job::OnQuicHostResolution, base::Unretained(this)));
- }
- return rv;
- }
- void HttpStreamFactory::Job::OnQuicHostResolution(int result) {
- DCHECK(expect_on_quic_host_resolution_);
- expect_on_quic_host_resolution_ = false;
- delegate_->OnConnectionInitialized(this, result);
- }
- void HttpStreamFactory::Job::OnFailedOnDefaultNetwork(int result) {
- DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
- DCHECK(using_quic_);
- delegate_->OnFailedOnDefaultNetwork(this);
- }
- int HttpStreamFactory::Job::DoInitConnectionComplete(int result) {
- net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
- // No need to continue waiting for a session, once a connection is
- // established.
- spdy_session_request_.reset();
- if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
- if (using_quic_)
- return result;
- DCHECK_EQ(OK, result);
- return OK;
- }
- resolve_error_info_ = connection_->resolve_error_info();
- // Determine the protocol (HTTP/1.1, HTTP/2, or HTTP/3). This covers both the
- // origin and some proxy cases. First, if the URL is HTTPS (or WSS), we may
- // negotiate HTTP/2 or HTTP/3 with the origin. Second, non-tunneled requests
- // (i.e. HTTP URLs) through an HTTPS or QUIC proxy work by sending the request
- // to the proxy directly. In that case, this logic also handles the proxy's
- // negotiated protocol. HTTPS requests are always tunneled, so at most one of
- // these applies.
- //
- // Tunneled requests may also negotiate ALPN at the proxy, but
- // HttpProxyConnectJob handles ALPN. The resulting StreamSocket will not
- // report an ALPN protocol.
- if (result == OK) {
- if (using_quic_) {
- // TODO(davidben): Record these values consistently between QUIC and TCP
- // below. In the QUIC case, we only record it for origin connections. In
- // the TCP case, we also record it for non-tunneled, proxied requests.
- if (using_ssl_) {
- was_alpn_negotiated_ = true;
- negotiated_protocol_ = kProtoQUIC;
- }
- } else if (connection_->socket()->WasAlpnNegotiated()) {
- // Only connections that use TLS can negotiate ALPN.
- DCHECK(using_ssl_ || proxy_info_.is_secure_http_like());
- was_alpn_negotiated_ = true;
- negotiated_protocol_ = connection_->socket()->GetNegotiatedProtocol();
- net_log_.AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_PROTO, [&] {
- return NetLogHttpStreamProtoParams(negotiated_protocol_);
- });
- if (negotiated_protocol_ == kProtoHTTP2) {
- if (is_websocket_) {
- // WebSocket is not supported over a fresh HTTP/2 connection. This
- // should not be reachable. For the origin, we do not request HTTP/2
- // on fresh WebSockets connections, because not all HTTP/2 servers
- // implement RFC 8441. For proxies, WebSockets are always tunneled.
- //
- // TODO(davidben): This isn't a CHECK() because, previously, it was
- // reachable in https://crbug.com/828865. However, if reachable, it
- // means a bug in the socket pools. The socket pools have since been
- // cleaned up, so this may no longer be reachable. Restore the CHECK
- // and see if this is still needed.
- return ERR_NOT_IMPLEMENTED;
- }
- using_spdy_ = true;
- }
- }
- }
- if (proxy_info_.is_quic() && using_quic_ && result < 0)
- return ReconsiderProxyAfterError(result);
- if (expect_spdy_ && !using_spdy_)
- return ERR_ALPN_NEGOTIATION_FAILED;
- // |result| may be the result of any of the stacked protocols. The following
- // logic is used when determining how to interpret an error.
- // If |result| < 0:
- // and connection_->socket() != NULL, then the SSL handshake ran and it
- // is a potentially recoverable error.
- // and connection_->socket == NULL and connection_->is_ssl_error() is true,
- // then the SSL handshake ran with an unrecoverable error.
- // otherwise, the error came from one of the other protocols.
- bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
- connection_->is_ssl_error());
- if (!ssl_started && result < 0 && (expect_spdy_ || using_quic_))
- return result;
- if (using_quic_) {
- if (result < 0)
- return result;
- if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
- std::unique_ptr<QuicChromiumClientSession::Handle> session =
- quic_request_.ReleaseSessionHandle();
- if (!session) {
- // Quic session is closed before stream can be created.
- return ERR_CONNECTION_CLOSED;
- }
- bidirectional_stream_impl_ =
- std::make_unique<BidirectionalStreamQuicImpl>(std::move(session));
- } else {
- std::unique_ptr<QuicChromiumClientSession::Handle> session =
- quic_request_.ReleaseSessionHandle();
- if (!session) {
- // Quic session is closed before stream can be created.
- return ERR_CONNECTION_CLOSED;
- }
- auto dns_aliases =
- session->GetDnsAliasesForSessionKey(quic_request_.session_key());
- stream_ = std::make_unique<QuicHttpStream>(std::move(session),
- std::move(dns_aliases));
- }
- next_state_ = STATE_NONE;
- return OK;
- }
- if (result < 0 && !ssl_started)
- return ReconsiderProxyAfterError(result);
- establishing_tunnel_ = false;
- // Handle SSL errors below.
- if (using_ssl_) {
- DCHECK(ssl_started);
- if (IsCertificateError(result)) {
- SSLInfo ssl_info;
- GetSSLInfo(&ssl_info);
- if (ssl_info.cert) {
- // Add the bad certificate to the set of allowed certificates in the
- // SSL config object. This data structure will be consulted after
- // calling RestartIgnoringLastError(). And the user will be asked
- // interactively before RestartIgnoringLastError() is ever called.
- server_ssl_config_.allowed_bad_certs.emplace_back(ssl_info.cert,
- ssl_info.cert_status);
- }
- }
- if (result < 0)
- return result;
- }
- next_state_ = STATE_CREATE_STREAM;
- return OK;
- }
- int HttpStreamFactory::Job::DoWaitingUserAction(int result) {
- // This state indicates that the stream request is in a partially
- // completed state, and we've called back to the delegate for more
- // information.
- // We're always waiting here for the delegate to call us back.
- return ERR_IO_PENDING;
- }
- int HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl(
- base::WeakPtr<SpdySession> session) {
- DCHECK(using_spdy_);
- auto dns_aliases = session_->spdy_session_pool()->GetDnsAliasesForSessionKey(
- spdy_session_key_);
- if (is_websocket_) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(delegate_->websocket_handshake_stream_create_helper());
- if (!try_websocket_over_http2_) {
- // TODO(davidben): Is this reachable? We shouldn't receive a SpdySession
- // if not requested.
- return ERR_NOT_IMPLEMENTED;
- }
- websocket_stream_ =
- delegate_->websocket_handshake_stream_create_helper()
- ->CreateHttp2Stream(session, std::move(dns_aliases));
- return OK;
- }
- if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
- bidirectional_stream_impl_ = std::make_unique<BidirectionalStreamSpdyImpl>(
- session, net_log_.source());
- return OK;
- }
- // TODO(willchan): Delete this code, because eventually, the HttpStreamFactory
- // will be creating all the SpdyHttpStreams, since it will know when
- // SpdySessions become available.
- stream_ = std::make_unique<SpdyHttpStream>(
- session, pushed_stream_id_, net_log_.source(), std::move(dns_aliases));
- return OK;
- }
- int HttpStreamFactory::Job::DoCreateStream() {
- DCHECK(connection_->socket() || existing_spdy_session_.get());
- DCHECK(!using_quic_);
- next_state_ = STATE_CREATE_STREAM_COMPLETE;
- if (!using_spdy_) {
- DCHECK(!expect_spdy_);
- bool using_proxy = (proxy_info_.is_http_like()) &&
- request_info_.url.SchemeIs(url::kHttpScheme);
- if (is_websocket_) {
- DCHECK_NE(job_type_, PRECONNECT);
- DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
- DCHECK(delegate_->websocket_handshake_stream_create_helper());
- websocket_stream_ =
- delegate_->websocket_handshake_stream_create_helper()
- ->CreateBasicStream(std::move(connection_), using_proxy,
- session_->websocket_endpoint_lock_manager());
- } else {
- if (request_info_.upload_data_stream &&
- !request_info_.upload_data_stream->AllowHTTP1()) {
- return ERR_H2_OR_QUIC_REQUIRED;
- }
- stream_ = std::make_unique<HttpBasicStream>(std::move(connection_),
- using_proxy);
- }
- return OK;
- }
- CHECK(!stream_.get());
- // It is possible that a pushed stream has been opened by a server since last
- // time Job checked above.
- if (!existing_spdy_session_) {
- // WebSocket over HTTP/2 is only allowed to use existing HTTP/2 connections.
- // Therefore |using_spdy_| could not have been set unless a connection had
- // already been found.
- DCHECK(!is_websocket_);
- session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
- spdy_session_key_, origin_url_, request_info_, &existing_spdy_session_,
- &pushed_stream_id_);
- // It is also possible that an HTTP/2 connection has been established since
- // last time Job checked above.
- if (!existing_spdy_session_) {
- existing_spdy_session_ =
- session_->spdy_session_pool()->FindAvailableSession(
- spdy_session_key_, enable_ip_based_pooling_,
- /* is_websocket = */ false, net_log_);
- }
- }
- if (existing_spdy_session_) {
- // We picked up an existing session, so we don't need our socket.
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
- int set_result =
- SetSpdyHttpStreamOrBidirectionalStreamImpl(existing_spdy_session_);
- existing_spdy_session_.reset();
- return set_result;
- }
- // Close idle sockets in this group, since subsequent requests will go over
- // |spdy_session|.
- if (connection_->socket()->IsConnected())
- connection_->CloseIdleSocketsInGroup("Switching to HTTP2 session");
- base::WeakPtr<SpdySession> spdy_session;
- int rv =
- session_->spdy_session_pool()->CreateAvailableSessionFromSocketHandle(
- spdy_session_key_, std::move(connection_), net_log_, &spdy_session);
- if (rv != OK) {
- return rv;
- }
- url::SchemeHostPort scheme_host_port(
- using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
- spdy_session_key_.host_port_pair().host(),
- spdy_session_key_.host_port_pair().port());
- HttpServerProperties* http_server_properties =
- session_->http_server_properties();
- if (http_server_properties) {
- http_server_properties->SetSupportsSpdy(scheme_host_port,
- request_info_.network_isolation_key,
- true /* supports_spdy */);
- }
- // Create a SpdyHttpStream or a BidirectionalStreamImpl attached to the
- // session.
- return SetSpdyHttpStreamOrBidirectionalStreamImpl(spdy_session);
- }
- int HttpStreamFactory::Job::DoCreateStreamComplete(int result) {
- if (result < 0)
- return result;
- session_->proxy_resolution_service()->ReportSuccess(proxy_info_);
- next_state_ = STATE_NONE;
- return OK;
- }
- void HttpStreamFactory::Job::OnSpdySessionAvailable(
- base::WeakPtr<SpdySession> spdy_session) {
- DCHECK(spdy_session);
- // No need for the connection any more, since |spdy_session| can be used
- // instead, and there's no benefit from keeping the old ConnectJob in the
- // socket pool.
- if (connection_)
- connection_->ResetAndCloseSocket();
- // Once a connection is initialized, or if there's any out-of-band callback,
- // like proxy auth challenge, the SpdySessionRequest is cancelled.
- DCHECK(next_state_ == STATE_INIT_CONNECTION ||
- next_state_ == STATE_INIT_CONNECTION_COMPLETE);
- // Ignore calls to ResumeInitConnection() from either the timer or the
- // SpdySessionPool.
- init_connection_already_resumed_ = true;
- // If this is a preconnect, nothing left do to.
- if (job_type_ == PRECONNECT) {
- OnPreconnectsComplete(OK);
- return;
- }
- using_spdy_ = true;
- existing_spdy_session_ = spdy_session;
- next_state_ = STATE_CREATE_STREAM;
- // This will synchronously close |connection_|, so no need to worry about it
- // calling back into |this|.
- RunLoop(net::OK);
- }
- int HttpStreamFactory::Job::ReconsiderProxyAfterError(int error) {
- // Check if the error was a proxy failure.
- if (!CanFalloverToNextProxy(proxy_info_.proxy_server(), error, &error))
- return error;
- should_reconsider_proxy_ = true;
- return error;
- }
- void HttpStreamFactory::Job::MaybeCopyConnectionAttemptsFromHandle() {
- if (!connection_)
- return;
- delegate_->AddConnectionAttemptsToRequest(this,
- connection_->connection_attempts());
- }
- HttpStreamFactory::JobFactory::JobFactory() = default;
- HttpStreamFactory::JobFactory::~JobFactory() = default;
- std::unique_ptr<HttpStreamFactory::Job>
- HttpStreamFactory::JobFactory::CreateJob(
- HttpStreamFactory::Job::Delegate* delegate,
- HttpStreamFactory::JobType job_type,
- HttpNetworkSession* session,
- const HttpRequestInfo& request_info,
- RequestPriority priority,
- const ProxyInfo& proxy_info,
- const SSLConfig& server_ssl_config,
- const SSLConfig& proxy_ssl_config,
- url::SchemeHostPort destination,
- GURL origin_url,
- bool is_websocket,
- bool enable_ip_based_pooling,
- NetLog* net_log,
- NextProto alternative_protocol,
- quic::ParsedQuicVersion quic_version) {
- return std::make_unique<HttpStreamFactory::Job>(
- delegate, job_type, session, request_info, priority, proxy_info,
- server_ssl_config, proxy_ssl_config, std::move(destination), origin_url,
- alternative_protocol, quic_version, is_websocket, enable_ip_based_pooling,
- net_log);
- }
- bool HttpStreamFactory::Job::ShouldThrottleConnectForSpdy() const {
- DCHECK(!using_quic_);
- DCHECK(!spdy_session_request_);
- // If the job has previously been throttled, don't throttle it again.
- if (init_connection_already_resumed_)
- return false;
- url::SchemeHostPort scheme_host_port(
- using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
- spdy_session_key_.host_port_pair().host(),
- spdy_session_key_.host_port_pair().port());
- // Only throttle the request if the server is believed to support H2.
- return session_->http_server_properties()->GetSupportsSpdy(
- scheme_host_port, request_info_.network_isolation_key);
- }
- } // namespace net
|