1 // Copyright 2012 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/test/url_request/url_request_failed_job.h"
11
12 #include "base/check_op.h"
13 #include "base/functional/bind.h"
14 #include "base/location.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/url_util.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_filter.h"
22 #include "net/url_request/url_request_interceptor.h"
23
24 namespace net {
25
26 namespace {
27
28 const char kMockHostname[] = "mock.failed.request";
29
30 // String names of failure phases matching FailurePhase enum.
31 const char* kFailurePhase[]{
32 "start", // START
33 "readsync", // READ_SYNC
34 "readasync", // READ_ASYNC
35 };
36
37 static_assert(std::size(kFailurePhase) ==
38 URLRequestFailedJob::FailurePhase::MAX_FAILURE_PHASE,
39 "kFailurePhase must match FailurePhase enum");
40
41 class MockJobInterceptor : public URLRequestInterceptor {
42 public:
43 MockJobInterceptor() = default;
44
45 MockJobInterceptor(const MockJobInterceptor&) = delete;
46 MockJobInterceptor& operator=(const MockJobInterceptor&) = delete;
47
48 ~MockJobInterceptor() override = default;
49
50 // URLRequestJobFactory::ProtocolHandler implementation:
MaybeInterceptRequest(URLRequest * request) const51 std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
52 URLRequest* request) const override {
53 int net_error = OK;
54 URLRequestFailedJob::FailurePhase phase =
55 URLRequestFailedJob::FailurePhase::MAX_FAILURE_PHASE;
56 for (size_t i = 0; i < std::size(kFailurePhase); i++) {
57 std::string phase_error_string;
58 if (GetValueForKeyInQuery(request->url(), kFailurePhase[i],
59 &phase_error_string)) {
60 if (base::StringToInt(phase_error_string, &net_error)) {
61 phase = static_cast<URLRequestFailedJob::FailurePhase>(i);
62 break;
63 }
64 }
65 }
66 return std::make_unique<URLRequestFailedJob>(request, phase, net_error);
67 }
68 };
69
GetMockUrl(const std::string & scheme,const std::string & hostname,URLRequestFailedJob::FailurePhase phase,int net_error)70 GURL GetMockUrl(const std::string& scheme,
71 const std::string& hostname,
72 URLRequestFailedJob::FailurePhase phase,
73 int net_error) {
74 CHECK_GE(phase, URLRequestFailedJob::FailurePhase::START);
75 CHECK_LE(phase, URLRequestFailedJob::FailurePhase::READ_ASYNC);
76 CHECK_LT(net_error, OK);
77 return GURL(scheme + "://" + hostname + "/error?" + kFailurePhase[phase] +
78 "=" + base::NumberToString(net_error));
79 }
80
81 } // namespace
82
URLRequestFailedJob(URLRequest * request,FailurePhase phase,int net_error)83 URLRequestFailedJob::URLRequestFailedJob(URLRequest* request,
84 FailurePhase phase,
85 int net_error)
86 : URLRequestJob(request), phase_(phase), net_error_(net_error) {
87 CHECK_GE(phase, URLRequestFailedJob::FailurePhase::START);
88 CHECK_LE(phase, URLRequestFailedJob::FailurePhase::READ_ASYNC);
89 CHECK_LT(net_error, OK);
90 }
91
URLRequestFailedJob(URLRequest * request,int net_error)92 URLRequestFailedJob::URLRequestFailedJob(URLRequest* request, int net_error)
93 : URLRequestFailedJob(request, START, net_error) {}
94
95 URLRequestFailedJob::~URLRequestFailedJob() = default;
96
Start()97 void URLRequestFailedJob::Start() {
98 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
99 FROM_HERE, base::BindOnce(&URLRequestFailedJob::StartAsync,
100 weak_factory_.GetWeakPtr()));
101 }
102
ReadRawData(IOBuffer * buf,int buf_size)103 int URLRequestFailedJob::ReadRawData(IOBuffer* buf, int buf_size) {
104 CHECK(phase_ == READ_SYNC || phase_ == READ_ASYNC);
105 if (net_error_ == ERR_IO_PENDING || phase_ == READ_SYNC)
106 return net_error_;
107
108 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
109 FROM_HERE, base::BindOnce(&URLRequestFailedJob::ReadRawDataComplete,
110 weak_factory_.GetWeakPtr(), net_error_));
111 return ERR_IO_PENDING;
112 }
113
GetResponseInfo(HttpResponseInfo * info)114 void URLRequestFailedJob::GetResponseInfo(HttpResponseInfo* info) {
115 *info = response_info_;
116 }
117
PopulateNetErrorDetails(NetErrorDetails * details) const118 void URLRequestFailedJob::PopulateNetErrorDetails(
119 NetErrorDetails* details) const {
120 if (net_error_ == ERR_QUIC_PROTOCOL_ERROR) {
121 details->quic_connection_error = quic::QUIC_INTERNAL_ERROR;
122 } else if (net_error_ == ERR_NETWORK_CHANGED) {
123 details->quic_connection_error =
124 quic::QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK;
125 }
126 }
127
GetTotalReceivedBytes() const128 int64_t URLRequestFailedJob::GetTotalReceivedBytes() const {
129 return total_received_bytes_;
130 }
131
132 // static
AddUrlHandler()133 void URLRequestFailedJob::AddUrlHandler() {
134 return AddUrlHandlerForHostname(kMockHostname);
135 }
136
137 // static
AddUrlHandlerForHostname(const std::string & hostname)138 void URLRequestFailedJob::AddUrlHandlerForHostname(
139 const std::string& hostname) {
140 URLRequestFilter* filter = URLRequestFilter::GetInstance();
141 // Add |hostname| to URLRequestFilter for HTTP and HTTPS.
142 filter->AddHostnameInterceptor("http", hostname,
143 std::make_unique<MockJobInterceptor>());
144 filter->AddHostnameInterceptor("https", hostname,
145 std::make_unique<MockJobInterceptor>());
146 }
147
148 // static
GetMockHttpUrl(int net_error)149 GURL URLRequestFailedJob::GetMockHttpUrl(int net_error) {
150 return GetMockHttpUrlForHostname(net_error, kMockHostname);
151 }
152
153 // static
GetMockHttpsUrl(int net_error)154 GURL URLRequestFailedJob::GetMockHttpsUrl(int net_error) {
155 return GetMockHttpsUrlForHostname(net_error, kMockHostname);
156 }
157
158 // static
GetMockHttpUrlWithFailurePhase(FailurePhase phase,int net_error)159 GURL URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(FailurePhase phase,
160 int net_error) {
161 return GetMockUrl("http", kMockHostname, phase, net_error);
162 }
163
164 // static
GetMockHttpUrlForHostname(int net_error,const std::string & hostname)165 GURL URLRequestFailedJob::GetMockHttpUrlForHostname(
166 int net_error,
167 const std::string& hostname) {
168 return GetMockUrl("http", hostname, START, net_error);
169 }
170
171 // static
GetMockHttpsUrlForHostname(int net_error,const std::string & hostname)172 GURL URLRequestFailedJob::GetMockHttpsUrlForHostname(
173 int net_error,
174 const std::string& hostname) {
175 return GetMockUrl("https", hostname, START, net_error);
176 }
177
StartAsync()178 void URLRequestFailedJob::StartAsync() {
179 if (phase_ == START) {
180 if (net_error_ != ERR_IO_PENDING) {
181 NotifyStartError(net_error_);
182 return;
183 }
184 return;
185 }
186 const std::string headers = "HTTP/1.1 200 OK";
187 response_info_.headers =
188 base::MakeRefCounted<net::HttpResponseHeaders>(headers);
189 total_received_bytes_ = headers.size();
190 NotifyHeadersComplete();
191 }
192
193 } // namespace net
194