1 // Copyright 2014 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_mock_data_job.h"
6
7 #include <memory>
8
9 #include "base/functional/bind.h"
10 #include "base/location.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/url_util.h"
16 #include "net/cert/x509_certificate.h"
17 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/http/http_util.h"
20 #include "net/ssl/ssl_cert_request_info.h"
21 #include "net/ssl/ssl_private_key.h"
22 #include "net/url_request/url_request_filter.h"
23
24 namespace net {
25 namespace {
26
27 const char kMockHostname[] = "mock.data";
28
29 // Gets the data from URL of the form:
30 // scheme://kMockHostname/?data=abc&repeat_count=nnn.
GetDataFromRequest(const URLRequest & request)31 std::string GetDataFromRequest(const URLRequest& request) {
32 std::string value;
33 if (!GetValueForKeyInQuery(request.url(), "data", &value))
34 return "default_data";
35 return value;
36 }
37
38 // Gets the numeric repeat count from URL of the form:
39 // scheme://kMockHostname/?data=abc&repeat_count=nnn.
GetRepeatCountFromRequest(const URLRequest & request)40 int GetRepeatCountFromRequest(const URLRequest& request) {
41 std::string value;
42 if (!GetValueForKeyInQuery(request.url(), "repeat", &value))
43 return 1;
44
45 int repeat_count;
46 if (!base::StringToInt(value, &repeat_count))
47 return 1;
48
49 DCHECK_GT(repeat_count, 0);
50
51 return repeat_count;
52 }
53
54 // Gets the requestcert flag from URL.
GetRequestClientCertificate(const URLRequest & request)55 bool GetRequestClientCertificate(const URLRequest& request) {
56 std::string ignored_value;
57 return GetValueForKeyInQuery(request.url(), "requestcert", &ignored_value);
58 }
59
GetMockUrl(const std::string & scheme,const std::string & hostname,const std::string & data,int data_repeat_count,bool request_client_certificate)60 GURL GetMockUrl(const std::string& scheme,
61 const std::string& hostname,
62 const std::string& data,
63 int data_repeat_count,
64 bool request_client_certificate) {
65 DCHECK_GT(data_repeat_count, 0);
66 std::string url(scheme + "://" + hostname + "/");
67 url.append("?data=");
68 url.append(data);
69 url.append("&repeat=");
70 url.append(base::NumberToString(data_repeat_count));
71 if (request_client_certificate)
72 url += "&requestcert=1";
73 return GURL(url);
74 }
75
76 class MockJobInterceptor : public URLRequestInterceptor {
77 public:
78 MockJobInterceptor() = default;
79
80 MockJobInterceptor(const MockJobInterceptor&) = delete;
81 MockJobInterceptor& operator=(const MockJobInterceptor&) = delete;
82
83 ~MockJobInterceptor() override = default;
84
85 // URLRequestInterceptor implementation
MaybeInterceptRequest(URLRequest * request) const86 std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
87 URLRequest* request) const override {
88 return std::make_unique<URLRequestMockDataJob>(
89 request, GetDataFromRequest(*request),
90 GetRepeatCountFromRequest(*request),
91 GetRequestClientCertificate(*request));
92 }
93 };
94
95 } // namespace
96
URLRequestMockDataJob(URLRequest * request,const std::string & data,int data_repeat_count,bool request_client_certificate)97 URLRequestMockDataJob::URLRequestMockDataJob(URLRequest* request,
98 const std::string& data,
99 int data_repeat_count,
100 bool request_client_certificate)
101 : URLRequestJob(request),
102 request_client_certificate_(request_client_certificate) {
103 DCHECK_GT(data_repeat_count, 0);
104 for (int i = 0; i < data_repeat_count; ++i) {
105 data_.append(data);
106 }
107 }
108
109 URLRequestMockDataJob::~URLRequestMockDataJob() = default;
110
OverrideResponseHeaders(const std::string & headers)111 void URLRequestMockDataJob::OverrideResponseHeaders(
112 const std::string& headers) {
113 headers_ = headers;
114 }
115
Start()116 void URLRequestMockDataJob::Start() {
117 // Start reading asynchronously so that all error reporting and data
118 // callbacks happen as they would for network requests.
119 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
120 FROM_HERE, base::BindOnce(&URLRequestMockDataJob::StartAsync,
121 weak_factory_.GetWeakPtr()));
122 }
123
ReadRawData(IOBuffer * buf,int buf_size)124 int URLRequestMockDataJob::ReadRawData(IOBuffer* buf, int buf_size) {
125 int bytes_read =
126 std::min(static_cast<size_t>(buf_size), data_.length() - data_offset_);
127 memcpy(buf->data(), data_.c_str() + data_offset_, bytes_read);
128 data_offset_ += bytes_read;
129 return bytes_read;
130 }
131
ContinueWithCertificate(scoped_refptr<X509Certificate> client_cert,scoped_refptr<SSLPrivateKey> client_private_key)132 void URLRequestMockDataJob::ContinueWithCertificate(
133 scoped_refptr<X509Certificate> client_cert,
134 scoped_refptr<SSLPrivateKey> client_private_key) {
135 DCHECK(request_client_certificate_);
136 NotifyHeadersComplete();
137 }
138
139 // Public virtual version.
GetResponseInfo(HttpResponseInfo * info)140 void URLRequestMockDataJob::GetResponseInfo(HttpResponseInfo* info) {
141 // Forward to private const version.
142 GetResponseInfoConst(info);
143 }
144
145 // Private const version.
GetResponseInfoConst(HttpResponseInfo * info) const146 void URLRequestMockDataJob::GetResponseInfoConst(HttpResponseInfo* info) const {
147 // Send back mock headers.
148 std::string raw_headers;
149 if (headers_.has_value()) {
150 raw_headers = headers_.value();
151 } else {
152 raw_headers.append(
153 "HTTP/1.1 200 OK\n"
154 "Content-type: text/plain\n");
155 raw_headers.append(base::StringPrintf("Content-Length: %1d\n",
156 static_cast<int>(data_.length())));
157 }
158 info->headers = base::MakeRefCounted<HttpResponseHeaders>(
159 HttpUtil::AssembleRawHeaders(raw_headers));
160 }
161
StartAsync()162 void URLRequestMockDataJob::StartAsync() {
163 if (!request_)
164 return;
165
166 set_expected_content_size(data_.length());
167 if (request_client_certificate_) {
168 auto request_all = base::MakeRefCounted<SSLCertRequestInfo>();
169 NotifyCertificateRequested(request_all.get());
170 return;
171 }
172 NotifyHeadersComplete();
173 }
174
175 // static
AddUrlHandler()176 void URLRequestMockDataJob::AddUrlHandler() {
177 return AddUrlHandlerForHostname(kMockHostname);
178 }
179
180 // static
AddUrlHandlerForHostname(const std::string & hostname)181 void URLRequestMockDataJob::AddUrlHandlerForHostname(
182 const std::string& hostname) {
183 // Add |hostname| to URLRequestFilter for HTTP and HTTPS.
184 URLRequestFilter* filter = URLRequestFilter::GetInstance();
185 filter->AddHostnameInterceptor("http", hostname,
186 std::make_unique<MockJobInterceptor>());
187 filter->AddHostnameInterceptor("https", hostname,
188 std::make_unique<MockJobInterceptor>());
189 }
190
191 // static
GetMockHttpUrl(const std::string & data,int repeat_count)192 GURL URLRequestMockDataJob::GetMockHttpUrl(const std::string& data,
193 int repeat_count) {
194 return GetMockHttpUrlForHostname(kMockHostname, data, repeat_count);
195 }
196
197 // static
GetMockHttpsUrl(const std::string & data,int repeat_count)198 GURL URLRequestMockDataJob::GetMockHttpsUrl(const std::string& data,
199 int repeat_count) {
200 return GetMockHttpsUrlForHostname(kMockHostname, data, repeat_count);
201 }
202
GetMockUrlForClientCertificateRequest()203 GURL URLRequestMockDataJob::GetMockUrlForClientCertificateRequest() {
204 return GetMockUrl("https", kMockHostname, "data", 1, true);
205 }
206
207 // static
GetMockHttpUrlForHostname(const std::string & hostname,const std::string & data,int repeat_count)208 GURL URLRequestMockDataJob::GetMockHttpUrlForHostname(
209 const std::string& hostname,
210 const std::string& data,
211 int repeat_count) {
212 return GetMockUrl("http", hostname, data, repeat_count, false);
213 }
214
215 // static
GetMockHttpsUrlForHostname(const std::string & hostname,const std::string & data,int repeat_count)216 GURL URLRequestMockDataJob::GetMockHttpsUrlForHostname(
217 const std::string& hostname,
218 const std::string& data,
219 int repeat_count) {
220 return GetMockUrl("https", hostname, data, repeat_count, false);
221 }
222
223 } // namespace net
224