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