xref: /aosp_15_r20/external/cronet/net/test/url_request/url_request_mock_http_job.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_mock_http_job.h"
6 
7 #include <string_view>
8 
9 #include "base/files/file_util.h"
10 #include "base/functional/bind.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/task/thread_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"
22 
23 namespace net {
24 
25 namespace {
26 
27 const char kMockHostname[] = "mock.http";
28 const base::FilePath::CharType kMockHeaderFileSuffix[] =
29     FILE_PATH_LITERAL(".mock-http-headers");
30 
31 class MockJobInterceptor : public URLRequestInterceptor {
32  public:
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.
MockJobInterceptor(const base::FilePath & base_path,bool map_all_requests_to_base_path)37   MockJobInterceptor(const base::FilePath& base_path,
38                      bool map_all_requests_to_base_path)
39       : base_path_(base_path),
40         map_all_requests_to_base_path_(map_all_requests_to_base_path) {}
41 
42   MockJobInterceptor(const MockJobInterceptor&) = delete;
43   MockJobInterceptor& operator=(const MockJobInterceptor&) = delete;
44 
45   ~MockJobInterceptor() override = default;
46 
47   // URLRequestJobFactory::ProtocolHandler implementation
MaybeInterceptRequest(URLRequest * request) const48   std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
49       URLRequest* request) const override {
50     return std::make_unique<URLRequestMockHTTPJob>(
51         request,
52         map_all_requests_to_base_path_ ? base_path_ : GetOnDiskPath(request));
53   }
54 
55  private:
GetOnDiskPath(URLRequest * request) const56   base::FilePath GetOnDiskPath(URLRequest* request) const {
57     // Conceptually we just want to "return base_path_ + request->url().path()".
58     // But path in the request URL is in URL space (i.e. %-encoded spaces).
59     // So first we convert base FilePath to a URL, then append the URL
60     // path to that, and convert the final URL back to a FilePath.
61     GURL file_url(FilePathToFileURL(base_path_));
62     std::string url = file_url.spec() + request->url().path();
63     base::FilePath file_path;
64     FileURLToFilePath(GURL(url), &file_path);
65     return file_path;
66   }
67 
68   const base::FilePath base_path_;
69   const bool map_all_requests_to_base_path_;
70 };
71 
DoFileIO(const base::FilePath & file_path)72 std::string DoFileIO(const base::FilePath& file_path) {
73   base::FilePath header_file =
74       base::FilePath(file_path.value() + kMockHeaderFileSuffix);
75 
76   if (!base::PathExists(header_file)) {
77     // If there is no mock-http-headers file, fake a 200 OK.
78     return "HTTP/1.0 200 OK\n";
79   }
80 
81   std::string raw_headers;
82   base::ReadFileToString(header_file, &raw_headers);
83   return raw_headers;
84 }
85 
86 // For a given file |path| and |scheme|, return the URL served by the
87 // URlRequestMockHTTPJob.
GetMockUrlForScheme(const std::string & path,const std::string & scheme)88 GURL GetMockUrlForScheme(const std::string& path, const std::string& scheme) {
89   return GURL(scheme + "://" + kMockHostname + "/" + path);
90 }
91 
92 }  // namespace
93 
94 // static
AddUrlHandlers(const base::FilePath & base_path)95 void URLRequestMockHTTPJob::AddUrlHandlers(const base::FilePath& base_path) {
96   // Add kMockHostname to URLRequestFilter, for both HTTP and HTTPS.
97   URLRequestFilter* filter = URLRequestFilter::GetInstance();
98   filter->AddHostnameInterceptor("http", kMockHostname,
99                                  CreateInterceptor(base_path));
100   filter->AddHostnameInterceptor("https", kMockHostname,
101                                  CreateInterceptor(base_path));
102 }
103 
104 // static
GetMockUrl(const std::string & path)105 GURL URLRequestMockHTTPJob::GetMockUrl(const std::string& path) {
106   return GetMockUrlForScheme(path, "http");
107 }
108 
109 // static
GetMockHttpsUrl(const std::string & path)110 GURL URLRequestMockHTTPJob::GetMockHttpsUrl(const std::string& path) {
111   return GetMockUrlForScheme(path, "https");
112 }
113 
114 // static
CreateInterceptor(const base::FilePath & base_path)115 std::unique_ptr<URLRequestInterceptor> URLRequestMockHTTPJob::CreateInterceptor(
116     const base::FilePath& base_path) {
117   return std::make_unique<MockJobInterceptor>(base_path, false);
118 }
119 
120 // static
121 std::unique_ptr<URLRequestInterceptor>
CreateInterceptorForSingleFile(const base::FilePath & file)122 URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
123     const base::FilePath& file) {
124   return std::make_unique<MockJobInterceptor>(file, true);
125 }
126 
URLRequestMockHTTPJob(URLRequest * request,const base::FilePath & file_path)127 URLRequestMockHTTPJob::URLRequestMockHTTPJob(URLRequest* request,
128                                              const base::FilePath& file_path)
129     : URLRequestTestJobBackedByFile(
130           request,
131           file_path,
132           base::ThreadPool::CreateTaskRunner({base::MayBlock()})) {}
133 
134 URLRequestMockHTTPJob::~URLRequestMockHTTPJob() = default;
135 
136 // Public virtual version.
GetResponseInfo(HttpResponseInfo * info)137 void URLRequestMockHTTPJob::GetResponseInfo(HttpResponseInfo* info) {
138   // Forward to private const version.
139   GetResponseInfoConst(info);
140 }
141 
IsRedirectResponse(GURL * location,int * http_status_code,bool * insecure_scheme_was_upgraded)142 bool URLRequestMockHTTPJob::IsRedirectResponse(
143     GURL* location,
144     int* http_status_code,
145     bool* insecure_scheme_was_upgraded) {
146   // Override the URLRequestTestJobBackedByFile implementation to invoke the
147   // default one based on HttpResponseInfo.
148   return URLRequestJob::IsRedirectResponse(location, http_status_code,
149                                            insecure_scheme_was_upgraded);
150 }
151 
OnReadComplete(net::IOBuffer * buffer,int result)152 void URLRequestMockHTTPJob::OnReadComplete(net::IOBuffer* buffer, int result) {
153   if (result >= 0)
154     total_received_bytes_ += result;
155 }
156 
157 // Public virtual version.
Start()158 void URLRequestMockHTTPJob::Start() {
159   base::ThreadPool::PostTaskAndReplyWithResult(
160       FROM_HERE, {base::MayBlock()}, base::BindOnce(&DoFileIO, file_path_),
161       base::BindOnce(&URLRequestMockHTTPJob::SetHeadersAndStart,
162                      weak_ptr_factory_.GetWeakPtr()));
163 }
164 
SetHeadersAndStart(const std::string & raw_headers)165 void URLRequestMockHTTPJob::SetHeadersAndStart(const std::string& raw_headers) {
166   raw_headers_ = raw_headers;
167   // Handle CRLF line-endings.
168   base::ReplaceSubstringsAfterOffset(&raw_headers_, 0, "\r\n", "\n");
169   // ParseRawHeaders expects \0 to end each header line.
170   base::ReplaceSubstringsAfterOffset(&raw_headers_, 0, "\n",
171                                      std::string_view("\0", 1));
172   total_received_bytes_ += raw_headers_.size();
173   URLRequestTestJobBackedByFile::Start();
174 }
175 
176 // Private const version.
GetResponseInfoConst(HttpResponseInfo * info) const177 void URLRequestMockHTTPJob::GetResponseInfoConst(HttpResponseInfo* info) const {
178   info->headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers_);
179 }
180 
GetTotalReceivedBytes() const181 int64_t URLRequestMockHTTPJob::GetTotalReceivedBytes() const {
182   return total_received_bytes_;
183 }
184 
GetMimeType(std::string * mime_type) const185 bool URLRequestMockHTTPJob::GetMimeType(std::string* mime_type) const {
186   HttpResponseInfo info;
187   GetResponseInfoConst(&info);
188   return info.headers.get() && info.headers->GetMimeType(mime_type);
189 }
190 
GetCharset(std::string * charset)191 bool URLRequestMockHTTPJob::GetCharset(std::string* charset) {
192   HttpResponseInfo info;
193   GetResponseInfo(&info);
194   return info.headers.get() && info.headers->GetCharset(charset);
195 }
196 
197 }  // namespace net
198