1 // Copyright 2024 The Chromium Authors
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_stream_pool_job.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "base/memory/raw_ptr.h"
11 #include "base/task/sequenced_task_runner.h"
12 #include "net/base/net_error_details.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/net_export.h"
15 #include "net/base/port_util.h"
16 #include "net/dns/public/resolve_error_info.h"
17 #include "net/http/http_network_session.h"
18 #include "net/http/http_stream_pool.h"
19 #include "net/http/http_stream_pool_attempt_manager.h"
20 #include "net/http/http_stream_pool_group.h"
21 #include "net/proxy_resolution/proxy_resolution_service.h"
22 #include "net/socket/connection_attempts.h"
23 #include "net/socket/next_proto.h"
24 #include "net/socket/stream_socket.h"
25 #include "net/ssl/ssl_cert_request_info.h"
26 #include "net/ssl/ssl_info.h"
27 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
28
29 namespace net {
30
31 namespace {
32
CalculateAllowedAlpns(NextProto expected_protocol,bool is_http1_allowed)33 NextProtoSet CalculateAllowedAlpns(NextProto expected_protocol,
34 bool is_http1_allowed) {
35 NextProtoSet allowed_alpns = expected_protocol == NextProto::kProtoUnknown
36 ? NextProtoSet::All()
37 : NextProtoSet({expected_protocol});
38 if (!is_http1_allowed) {
39 static constexpr NextProtoSet kHttp11Protocols = {NextProto::kProtoUnknown,
40 NextProto::kProtoHTTP11};
41 allowed_alpns.RemoveAll(kHttp11Protocols);
42 }
43 return allowed_alpns;
44 }
45
46 } // namespace
47
Job(Delegate * delegate,AttemptManager * attempt_manager,quic::ParsedQuicVersion quic_version,NextProto expected_protocol,const NetLogWithSource & net_log)48 HttpStreamPool::Job::Job(Delegate* delegate,
49 AttemptManager* attempt_manager,
50 quic::ParsedQuicVersion quic_version,
51 NextProto expected_protocol,
52 const NetLogWithSource& net_log)
53 : delegate_(delegate),
54 attempt_manager_(attempt_manager),
55 quic_version_(quic_version),
56 allowed_alpns_(CalculateAllowedAlpns(expected_protocol,
57 delegate_->is_http1_allowed())),
58 net_log_(net_log) {
59 CHECK(delegate_->is_http1_allowed() ||
60 expected_protocol != NextProto::kProtoHTTP11);
61 }
62
~Job()63 HttpStreamPool::Job::~Job() {
64 CHECK(attempt_manager_);
65 // `attempt_manager_` may be deleted after this call.
66 attempt_manager_.ExtractAsDangling()->OnJobComplete(this);
67 }
68
Start()69 void HttpStreamPool::Job::Start() {
70 const url::SchemeHostPort& destination =
71 attempt_manager_->group()->stream_key().destination();
72 if (!IsPortAllowedForScheme(destination.port(), destination.scheme())) {
73 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
74 FROM_HERE,
75 base::BindOnce(&Job::OnStreamFailed, weak_ptr_factory_.GetWeakPtr(),
76 ERR_UNSAFE_PORT, NetErrorDetails(), ResolveErrorInfo()));
77 return;
78 }
79
80 attempt_manager_->StartJob(this, priority(), delegate_->allowed_bad_certs(),
81 quic_version_, net_log_);
82 }
83
GetLoadState() const84 LoadState HttpStreamPool::Job::GetLoadState() const {
85 CHECK(attempt_manager_);
86 return attempt_manager_->GetLoadState();
87 }
88
SetPriority(RequestPriority priority)89 void HttpStreamPool::Job::SetPriority(RequestPriority priority) {
90 CHECK(attempt_manager_);
91 attempt_manager_->SetJobPriority(this, priority);
92 }
93
AddConnectionAttempts(const ConnectionAttempts & attempts)94 void HttpStreamPool::Job::AddConnectionAttempts(
95 const ConnectionAttempts& attempts) {
96 for (const auto& attempt : attempts) {
97 connection_attempts_.emplace_back(attempt);
98 }
99 }
100
OnStreamReady(std::unique_ptr<HttpStream> stream,NextProto negotiated_protocol)101 void HttpStreamPool::Job::OnStreamReady(std::unique_ptr<HttpStream> stream,
102 NextProto negotiated_protocol) {
103 CHECK(delegate_);
104
105 int result = OK;
106 if (!allowed_alpns_.Has(negotiated_protocol)) {
107 const bool is_h2_or_h3_required = !delegate_->is_http1_allowed();
108 const bool is_h2_or_h3 = negotiated_protocol == NextProto::kProtoHTTP2 ||
109 negotiated_protocol == NextProto::kProtoQUIC;
110 if (is_h2_or_h3_required && !is_h2_or_h3) {
111 result = ERR_H2_OR_QUIC_REQUIRED;
112 } else {
113 result = ERR_ALPN_NEGOTIATION_FAILED;
114 }
115 }
116
117 if (result != OK) {
118 OnStreamFailed(result, NetErrorDetails(), ResolveErrorInfo());
119 return;
120 }
121
122 attempt_manager_->group()
123 ->http_network_session()
124 ->proxy_resolution_service()
125 ->ReportSuccess(delegate_->proxy_info());
126 delegate_->OnStreamReady(this, std::move(stream), negotiated_protocol);
127 }
128
OnStreamFailed(int status,const NetErrorDetails & net_error_details,ResolveErrorInfo resolve_error_info)129 void HttpStreamPool::Job::OnStreamFailed(
130 int status,
131 const NetErrorDetails& net_error_details,
132 ResolveErrorInfo resolve_error_info) {
133 CHECK(delegate_);
134 delegate_->OnStreamFailed(this, status, net_error_details,
135 resolve_error_info);
136 }
137
OnCertificateError(int status,const SSLInfo & ssl_info)138 void HttpStreamPool::Job::OnCertificateError(int status,
139 const SSLInfo& ssl_info) {
140 CHECK(delegate_);
141 delegate_->OnCertificateError(this, status, ssl_info);
142 }
143
OnNeedsClientAuth(SSLCertRequestInfo * cert_info)144 void HttpStreamPool::Job::OnNeedsClientAuth(SSLCertRequestInfo* cert_info) {
145 CHECK(delegate_);
146 delegate_->OnNeedsClientAuth(this, cert_info);
147 }
148
149 } // namespace net
150