1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/test/url_request/url_request_mock_http_job.h"
7 #include "base/files/file_util.h"
8 #include "base/macros.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/task_runner_util.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "net/base/filename_util.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_filter.h"
21 #include "net/url_request/url_request_interceptor.h"
27 const char kMockHostname
[] = "mock.http";
28 const base::FilePath::CharType kMockHeaderFileSuffix
[] =
29 FILE_PATH_LITERAL(".mock-http-headers");
31 class MockJobInterceptor
: public URLRequestInterceptor
{
33 // When |map_all_requests_to_base_path| is true, all request should return the
34 // contents of the file at |base_path|. When |map_all_requests_to_base_path|
35 // is false, |base_path| is the file path leading to the root of the directory
36 // to use as the root of the HTTP server.
38 const base::FilePath
& base_path
,
39 bool map_all_requests_to_base_path
,
40 const scoped_refptr
<base::SequencedWorkerPool
>& worker_pool
)
41 : base_path_(base_path
),
42 map_all_requests_to_base_path_(map_all_requests_to_base_path
),
43 worker_pool_(worker_pool
) {}
44 ~MockJobInterceptor() override
{}
46 // URLRequestJobFactory::ProtocolHandler implementation
47 URLRequestJob
* MaybeInterceptRequest(
49 NetworkDelegate
* network_delegate
) const override
{
50 return new URLRequestMockHTTPJob(
53 map_all_requests_to_base_path_
? base_path_
: GetOnDiskPath(request
),
54 worker_pool_
->GetTaskRunnerWithShutdownBehavior(
55 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
));
59 base::FilePath
GetOnDiskPath(URLRequest
* request
) const {
60 // Conceptually we just want to "return base_path_ + request->url().path()".
61 // But path in the request URL is in URL space (i.e. %-encoded spaces).
62 // So first we convert base FilePath to a URL, then append the URL
63 // path to that, and convert the final URL back to a FilePath.
64 GURL
file_url(FilePathToFileURL(base_path_
));
65 std::string url
= file_url
.spec() + request
->url().path();
66 base::FilePath file_path
;
67 FileURLToFilePath(GURL(url
), &file_path
);
71 const base::FilePath base_path_
;
72 const bool map_all_requests_to_base_path_
;
73 const scoped_refptr
<base::SequencedWorkerPool
> worker_pool_
;
75 DISALLOW_COPY_AND_ASSIGN(MockJobInterceptor
);
78 std::string
DoFileIO(const base::FilePath
& file_path
) {
79 base::FilePath header_file
=
80 base::FilePath(file_path
.value() + kMockHeaderFileSuffix
);
82 if (!base::PathExists(header_file
)) {
83 // If there is no mock-http-headers file, fake a 200 OK.
84 return "HTTP/1.0 200 OK\n";
87 std::string raw_headers
;
88 base::ReadFileToString(header_file
, &raw_headers
);
92 // For a given file |path| and |scheme|, return the URL served by the
93 // URlRequestMockHTTPJob.
94 GURL
GetMockUrlForScheme(const std::string
& path
, const std::string
& scheme
) {
95 return GURL(scheme
+ "://" + kMockHostname
+ "/" + path
);
101 void URLRequestMockHTTPJob::AddUrlHandlers(
102 const base::FilePath
& base_path
,
103 const scoped_refptr
<base::SequencedWorkerPool
>& worker_pool
) {
104 // Add kMockHostname to URLRequestFilter, for both HTTP and HTTPS.
105 URLRequestFilter
* filter
= URLRequestFilter::GetInstance();
106 filter
->AddHostnameInterceptor(
107 "http", kMockHostname
, CreateInterceptor(base_path
, worker_pool
));
108 filter
->AddHostnameInterceptor("https", kMockHostname
,
109 CreateInterceptor(base_path
, worker_pool
));
113 GURL
URLRequestMockHTTPJob::GetMockUrl(const std::string
& path
) {
114 return GetMockUrlForScheme(path
, "http");
118 GURL
URLRequestMockHTTPJob::GetMockHttpsUrl(const std::string
& path
) {
119 return GetMockUrlForScheme(path
, "https");
123 GURL
URLRequestMockHTTPJob::GetMockUrl(const base::FilePath
& path
) {
124 std::string path_str
= path
.MaybeAsASCII();
125 DCHECK(!path_str
.empty()); // We only expect ASCII paths in tests.
126 return GetMockUrlForScheme(path_str
, "http");
130 GURL
URLRequestMockHTTPJob::GetMockHttpsUrl(const base::FilePath
& path
) {
131 std::string path_str
= path
.MaybeAsASCII();
132 DCHECK(!path_str
.empty()); // We only expect ASCII paths in tests.
133 return GetMockUrlForScheme(path_str
, "https");
137 scoped_ptr
<URLRequestInterceptor
> URLRequestMockHTTPJob::CreateInterceptor(
138 const base::FilePath
& base_path
,
139 const scoped_refptr
<base::SequencedWorkerPool
>& worker_pool
) {
140 return scoped_ptr
<URLRequestInterceptor
>(
141 new MockJobInterceptor(base_path
, false, worker_pool
));
145 scoped_ptr
<URLRequestInterceptor
>
146 URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
147 const base::FilePath
& file
,
148 const scoped_refptr
<base::SequencedWorkerPool
>& worker_pool
) {
149 return scoped_ptr
<URLRequestInterceptor
>(
150 new MockJobInterceptor(file
, true, worker_pool
));
153 URLRequestMockHTTPJob::URLRequestMockHTTPJob(
155 NetworkDelegate
* network_delegate
,
156 const base::FilePath
& file_path
,
157 const scoped_refptr
<base::TaskRunner
>& task_runner
)
158 : URLRequestFileJob(request
, network_delegate
, file_path
, task_runner
),
159 task_runner_(task_runner
),
160 weak_ptr_factory_(this) {
163 URLRequestMockHTTPJob::~URLRequestMockHTTPJob() {
166 // Public virtual version.
167 void URLRequestMockHTTPJob::GetResponseInfo(HttpResponseInfo
* info
) {
168 // Forward to private const version.
169 GetResponseInfoConst(info
);
172 bool URLRequestMockHTTPJob::IsRedirectResponse(GURL
* location
,
173 int* http_status_code
) {
174 // Override the URLRequestFileJob implementation to invoke the default
175 // one based on HttpResponseInfo.
176 return URLRequestJob::IsRedirectResponse(location
, http_status_code
);
179 // Public virtual version.
180 void URLRequestMockHTTPJob::Start() {
181 base::PostTaskAndReplyWithResult(
184 base::Bind(&DoFileIO
, file_path_
),
185 base::Bind(&URLRequestMockHTTPJob::SetHeadersAndStart
,
186 weak_ptr_factory_
.GetWeakPtr()));
189 void URLRequestMockHTTPJob::SetHeadersAndStart(const std::string
& raw_headers
) {
190 raw_headers_
= raw_headers
;
191 // Handle CRLF line-endings.
192 base::ReplaceSubstringsAfterOffset(&raw_headers_
, 0, "\r\n", "\n");
193 // ParseRawHeaders expects \0 to end each header line.
194 base::ReplaceSubstringsAfterOffset(
195 &raw_headers_
, 0, "\n", base::StringPiece("\0", 1));
196 URLRequestFileJob::Start();
199 // Private const version.
200 void URLRequestMockHTTPJob::GetResponseInfoConst(HttpResponseInfo
* info
) const {
201 info
->headers
= new HttpResponseHeaders(raw_headers_
);
204 bool URLRequestMockHTTPJob::GetMimeType(std::string
* mime_type
) const {
205 HttpResponseInfo info
;
206 GetResponseInfoConst(&info
);
207 return info
.headers
.get() && info
.headers
->GetMimeType(mime_type
);
210 int URLRequestMockHTTPJob::GetResponseCode() const {
211 HttpResponseInfo info
;
212 GetResponseInfoConst(&info
);
213 // If we have headers, get the response code from them.
214 if (info
.headers
.get())
215 return info
.headers
->response_code();
216 return URLRequestJob::GetResponseCode();
219 bool URLRequestMockHTTPJob::GetCharset(std::string
* charset
) {
220 HttpResponseInfo info
;
221 GetResponseInfo(&info
);
222 return info
.headers
.get() && info
.headers
->GetCharset(charset
);