xref: /aosp_15_r20/external/cronet/components/cronet/testing/test_server/test_server.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2016 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 "components/cronet/testing/test_server/test_server.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/base_paths.h"
11 #include "base/format_macros.h"
12 #include "base/functional/bind.h"
13 #include "base/lazy_instance.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "net/http/http_status_code.h"
19 #include "net/test/embedded_test_server/default_handlers.h"
20 #include "net/test/embedded_test_server/embedded_test_server.h"
21 #include "net/test/embedded_test_server/http_request.h"
22 #include "net/test/embedded_test_server/http_response.h"
23 
24 namespace {
25 
26 // Cronet test data directory, relative to source root.
27 const base::FilePath::CharType kTestDataRelativePath[] =
28     FILE_PATH_LITERAL("components/cronet/testing/test_server/data");
29 
30 const char kSimplePath[] = "/simple";
31 const char kEchoHeaderPath[] = "/echo_header?";
32 const char kEchoMethodPath[] = "/echo_method";
33 const char kEchoAllHeadersPath[] = "/echo_all_headers";
34 const char kRedirectToEchoBodyPath[] = "/redirect_to_echo_body";
35 const char kSetCookiePath[] = "/set_cookie?";
36 const char kBigDataPath[] = "/big_data?";
37 const char kUseEncodingPath[] = "/use_encoding?";
38 const char kEchoBodyPath[] = "/echo_body";
39 
40 const char kSimpleResponse[] = "The quick brown fox jumps over the lazy dog.";
41 
42 std::unique_ptr<net::EmbeddedTestServer> g_test_server;
43 base::LazyInstance<std::string>::Leaky g_big_data_body =
44     LAZY_INSTANCE_INITIALIZER;
45 
SimpleRequest()46 std::unique_ptr<net::test_server::HttpResponse> SimpleRequest() {
47   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
48   http_response->set_code(net::HTTP_OK);
49   http_response->set_content(kSimpleResponse);
50   return std::move(http_response);
51 }
52 
UseEncodingInResponse(const net::test_server::HttpRequest & request)53 std::unique_ptr<net::test_server::HttpResponse> UseEncodingInResponse(
54     const net::test_server::HttpRequest& request) {
55   std::string encoding;
56   DCHECK(base::StartsWith(request.relative_url, kUseEncodingPath,
57                           base::CompareCase::INSENSITIVE_ASCII));
58 
59   encoding = request.relative_url.substr(strlen(kUseEncodingPath));
60   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
61   if (!encoding.compare("brotli")) {
62     const uint8_t quickfoxCompressed[] = {
63         0x0b, 0x15, 0x80, 0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b,
64         0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20, 0x6a,
65         0x75, 0x6d, 0x70, 0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68,
66         0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67, 0x03};
67     std::string quickfoxCompressedStr(
68         reinterpret_cast<const char*>(quickfoxCompressed),
69         sizeof(quickfoxCompressed));
70     http_response->set_code(net::HTTP_OK);
71     http_response->set_content(quickfoxCompressedStr);
72     http_response->AddCustomHeader(std::string("content-encoding"),
73                                    std::string("br"));
74   }
75   return std::move(http_response);
76 }
77 
ReturnBigDataInResponse(const net::test_server::HttpRequest & request)78 std::unique_ptr<net::test_server::HttpResponse> ReturnBigDataInResponse(
79     const net::test_server::HttpRequest& request) {
80   DCHECK(base::StartsWith(request.relative_url, kBigDataPath,
81                           base::CompareCase::INSENSITIVE_ASCII));
82   std::string data_size_str = request.relative_url.substr(strlen(kBigDataPath));
83   int64_t data_size;
84   CHECK(base::StringToInt64(base::StringPiece(data_size_str), &data_size));
85   CHECK(data_size == static_cast<int64_t>(g_big_data_body.Get().size()));
86   return std::make_unique<net::test_server::RawHttpResponse>(
87       std::string(), g_big_data_body.Get());
88 }
89 
SetAndEchoCookieInResponse(const net::test_server::HttpRequest & request)90 std::unique_ptr<net::test_server::HttpResponse> SetAndEchoCookieInResponse(
91     const net::test_server::HttpRequest& request) {
92   std::string cookie_line;
93   DCHECK(base::StartsWith(request.relative_url, kSetCookiePath,
94                           base::CompareCase::INSENSITIVE_ASCII));
95   cookie_line = request.relative_url.substr(strlen(kSetCookiePath));
96   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
97   http_response->set_code(net::HTTP_OK);
98   http_response->set_content(cookie_line);
99   http_response->AddCustomHeader("Set-Cookie", cookie_line);
100   return std::move(http_response);
101 }
102 
CronetTestRequestHandler(const net::test_server::HttpRequest & request)103 std::unique_ptr<net::test_server::HttpResponse> CronetTestRequestHandler(
104     const net::test_server::HttpRequest& request) {
105   if (base::StartsWith(request.relative_url, kSimplePath,
106                        base::CompareCase::INSENSITIVE_ASCII)) {
107     return SimpleRequest();
108   }
109   if (base::StartsWith(request.relative_url, kSetCookiePath,
110                        base::CompareCase::INSENSITIVE_ASCII)) {
111     return SetAndEchoCookieInResponse(request);
112   }
113   if (base::StartsWith(request.relative_url, kBigDataPath,
114                        base::CompareCase::INSENSITIVE_ASCII)) {
115     return ReturnBigDataInResponse(request);
116   }
117   if (base::StartsWith(request.relative_url, kUseEncodingPath,
118                        base::CompareCase::INSENSITIVE_ASCII)) {
119     return UseEncodingInResponse(request);
120   }
121 
122   std::unique_ptr<net::test_server::BasicHttpResponse> response(
123       new net::test_server::BasicHttpResponse());
124   response->set_content_type("text/plain");
125 
126   if (request.relative_url == kEchoBodyPath) {
127     if (request.has_content) {
128       response->set_content(request.content);
129     } else {
130       response->set_content("Request has no body. :(");
131     }
132     return std::move(response);
133   }
134 
135   if (base::StartsWith(request.relative_url, kEchoHeaderPath,
136                        base::CompareCase::SENSITIVE)) {
137     GURL url = g_test_server->GetURL(request.relative_url);
138     auto it = request.headers.find(url.query());
139     if (it != request.headers.end()) {
140       response->set_content(it->second);
141     } else {
142       response->set_content("Header not found. :(");
143     }
144     return std::move(response);
145   }
146 
147   if (request.relative_url == kEchoAllHeadersPath) {
148     response->set_content(request.all_headers);
149     return std::move(response);
150   }
151 
152   if (request.relative_url == kEchoMethodPath) {
153     response->set_content(request.method_string);
154     return std::move(response);
155   }
156 
157   if (request.relative_url == kRedirectToEchoBodyPath) {
158     response->set_code(net::HTTP_TEMPORARY_REDIRECT);
159     response->AddCustomHeader("Location", kEchoBodyPath);
160     return std::move(response);
161   }
162 
163   // Unhandled requests result in the Embedded test server sending a 404.
164   return nullptr;
165 }
166 
167 }  // namespace
168 
169 namespace cronet {
170 
171 /* static */
StartServeFilesFromDirectory(const base::FilePath & test_files_root,net::EmbeddedTestServer::Type server_type,net::EmbeddedTestServer::ServerCertificate server_certificate)172 bool TestServer::StartServeFilesFromDirectory(
173     const base::FilePath& test_files_root,
174     net::EmbeddedTestServer::Type server_type,
175     net::EmbeddedTestServer::ServerCertificate server_certificate) {
176   // Shouldn't happen.
177   if (g_test_server)
178     return false;
179 
180   g_test_server = std::make_unique<net::EmbeddedTestServer>(server_type);
181   g_test_server->RegisterRequestHandler(
182       base::BindRepeating(&CronetTestRequestHandler));
183   g_test_server->ServeFilesFromDirectory(test_files_root);
184   net::test_server::RegisterDefaultHandlers(g_test_server.get());
185   g_test_server->SetSSLConfig(server_certificate);
186   CHECK(g_test_server->Start());
187   return true;
188 }
189 
190 /* static */
Start()191 bool TestServer::Start() {
192   base::FilePath src_root;
193   CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_root));
194   return StartServeFilesFromDirectory(
195       src_root.Append(kTestDataRelativePath),
196       net::test_server::EmbeddedTestServer::TYPE_HTTP,
197       net::test_server::EmbeddedTestServer::CERT_OK);
198 }
199 
200 /* static */
Shutdown()201 void TestServer::Shutdown() {
202   if (!g_test_server)
203     return;
204   g_test_server.reset();
205 }
206 
207 /* static */
GetPort()208 int TestServer::GetPort() {
209   DCHECK(g_test_server);
210   return g_test_server->port();
211 }
212 
213 /* static */
GetHostPort()214 std::string TestServer::GetHostPort() {
215   DCHECK(g_test_server);
216   return net::HostPortPair::FromURL(g_test_server->base_url()).ToString();
217 }
218 
219 /* static */
GetSimpleURL()220 std::string TestServer::GetSimpleURL() {
221   return GetFileURL(kSimplePath);
222 }
223 
224 /* static */
GetEchoMethodURL()225 std::string TestServer::GetEchoMethodURL() {
226   return GetFileURL(kEchoMethodPath);
227 }
228 
229 /* static */
GetEchoHeaderURL(const std::string & header_name)230 std::string TestServer::GetEchoHeaderURL(const std::string& header_name) {
231   return GetFileURL(kEchoHeaderPath + header_name);
232 }
233 
234 /* static */
GetUseEncodingURL(const std::string & encoding_name)235 std::string TestServer::GetUseEncodingURL(const std::string& encoding_name) {
236   return GetFileURL(kUseEncodingPath + encoding_name);
237 }
238 
239 /* static */
GetSetCookieURL(const std::string & cookie_line)240 std::string TestServer::GetSetCookieURL(const std::string& cookie_line) {
241   return GetFileURL(kSetCookiePath + cookie_line);
242 }
243 
244 /* static */
GetEchoAllHeadersURL()245 std::string TestServer::GetEchoAllHeadersURL() {
246   return GetFileURL(kEchoAllHeadersPath);
247 }
248 
249 /* static */
GetEchoRequestBodyURL()250 std::string TestServer::GetEchoRequestBodyURL() {
251   return GetFileURL(kEchoBodyPath);
252 }
253 
254 /* static */
GetRedirectToEchoBodyURL()255 std::string TestServer::GetRedirectToEchoBodyURL() {
256   return GetFileURL(kRedirectToEchoBodyPath);
257 }
258 
259 /* static */
GetExabyteResponseURL()260 std::string TestServer::GetExabyteResponseURL() {
261   return GetFileURL("/exabyte_response");
262 }
263 
264 /* static */
PrepareBigDataURL(size_t data_size)265 std::string TestServer::PrepareBigDataURL(size_t data_size) {
266   DCHECK(g_test_server);
267   DCHECK(g_big_data_body.Get().empty());
268   // Response line with headers.
269   std::string response_builder;
270   base::StringAppendF(&response_builder, "HTTP/1.1 200 OK\r\n");
271   base::StringAppendF(&response_builder, "Content-Length: %" PRIuS "\r\n",
272                       data_size);
273   base::StringAppendF(&response_builder, "\r\n");
274   response_builder += std::string(data_size, 'c');
275   g_big_data_body.Get() = response_builder;
276   return g_test_server
277       ->GetURL(kBigDataPath + base::NumberToString(response_builder.size()))
278       .spec();
279 }
280 
281 /* static */
ReleaseBigDataURL()282 void TestServer::ReleaseBigDataURL() {
283   DCHECK(!g_big_data_body.Get().empty());
284   g_big_data_body.Get() = std::string();
285 }
286 
287 /* static */
GetFileURL(const std::string & file_path)288 std::string TestServer::GetFileURL(const std::string& file_path) {
289   DCHECK(g_test_server);
290   return g_test_server->GetURL(file_path).spec();
291 }
292 
293 }  // namespace cronet
294