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