| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 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. |
| 4 |
| 5 #include "net/test/embedded_test_server/request_helpers.h" |
| 6 |
| 7 #include <stdlib.h> |
| 8 #include <ctime> |
| 9 #include <map> |
| 10 #include <sstream> |
| 11 #include <string> |
| 12 |
| 13 #include "base/base64.h" |
| 14 #include "base/files/file_path.h" |
| 15 #include "base/files/file_util.h" |
| 16 #include "base/format_macros.h" |
| 17 #include "base/md5.h" |
| 18 #include "base/path_service.h" |
| 19 #include "base/strings/string_split.h" |
| 20 #include "base/strings/string_util.h" |
| 21 #include "base/strings/stringprintf.h" |
| 22 #include "base/thread_task_runner_handle.h" |
| 23 #include "base/threading/thread_restrictions.h" |
| 24 #include "base/time/time.h" |
| 25 #include "net/base/escape.h" |
| 26 #include "net/base/url_util.h" |
| 27 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 28 #include "net/test/embedded_test_server/http_request.h" |
| 29 #include "net/test/embedded_test_server/http_response.h" |
| 30 |
| 31 #define NOT_HANDLED scoped_ptr<HttpResponse>() |
| 32 |
| 33 net::UnescapeRule::Type UNESCAPE_ALL = |
| 34 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS | |
| 35 net::UnescapeRule::SPOOFING_AND_CONTROL_CHARS | |
| 36 net::UnescapeRule::REPLACE_PLUS_WITH_SPACE; |
| 37 |
| 38 namespace net { |
| 39 namespace test_server { |
| 40 |
| 41 void CustomHttpResponse::SendResponse(SendCallback send, |
| 42 SendDoneCallback done) { |
| 43 std::string response; |
| 44 if (headers_.size() != 0 || contents_.size() != 0) |
| 45 response = headers_ + "\r\n" + contents_; |
| 46 send.Run(response, done); |
| 47 } |
| 48 |
| 49 bool ShouldHandle(const HttpRequest& request, std::string url) { |
| 50 if (request.relative_url.compare(0, url.size(), url) == 0) { |
| 51 if (request.relative_url.size() == url.size()) |
| 52 return true; |
| 53 std::string endings("/?;#"); |
| 54 if (endings.find(request.relative_url.at(url.size())) != std::string::npos) |
| 55 return true; |
| 56 } |
| 57 return false; |
| 58 } |
| 59 |
| 60 GURL ToGURL(const HttpRequest& request) { |
| 61 return GURL("http://localhost" + request.relative_url); |
| 62 } |
| 63 |
| 64 RequestQuery ParseQuery(GURL url) { |
| 65 RequestQuery queries; |
| 66 |
| 67 for (QueryIterator it(url); !it.IsAtEnd(); it.Advance()) { |
| 68 queries[net::UnescapeURLComponent(it.GetKey(), UNESCAPE_ALL)].push_back( |
| 69 it.GetUnescapedValue()); |
| 70 } |
| 71 return queries; |
| 72 } |
| 73 |
| 74 // Handles |request| by serving a file from under |server_root|. |
| 75 scoped_ptr<HttpResponse> HandleFileRequest(const base::FilePath& server_root, |
| 76 const HttpRequest& request) { |
| 77 // This is a test-only server. Ignore I/O thread restrictions. |
| 78 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 79 |
| 80 std::string relative_url(request.relative_url); |
| 81 |
| 82 // A proxy request will have an absolute path. Simulate the proxy by stripping |
| 83 // the scheme, host, and port. |
| 84 GURL request_gurl = ToGURL(request); |
| 85 relative_url = request_gurl.path(); |
| 86 |
| 87 std::string files_prefix("/files/"); |
| 88 if (relative_url.compare(0, files_prefix.size(), files_prefix) == 0) { |
| 89 LOG(ERROR) << "Using old-style /files/ for: " << relative_url; |
| 90 relative_url = relative_url.substr(files_prefix.size() - 1); |
| 91 } |
| 92 |
| 93 std::string post_prefix("/post/"); |
| 94 if (relative_url.compare(0, post_prefix.size(), post_prefix) == 0) { |
| 95 relative_url = relative_url.substr(post_prefix.size() - 1); |
| 96 if (request.method != METHOD_POST) |
| 97 return NOT_HANDLED; |
| 98 } |
| 99 |
| 100 RequestQuery query = ParseQuery(request_gurl); |
| 101 |
| 102 if (query.find("expected_body") != query.end()) { |
| 103 if (request.content.find(query["expected_body"].front()) == |
| 104 std::string::npos) { |
| 105 scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse); |
| 106 not_found_response->set_code(HTTP_NOT_FOUND); |
| 107 return not_found_response.Pass(); |
| 108 } |
| 109 } |
| 110 |
| 111 if (query.find("expected_headers") != query.end()) { |
| 112 std::vector<std::string> headers = query["expected_headers"]; |
| 113 for (size_t i = 0; i < headers.size(); ++i) { |
| 114 std::string key = headers[i].substr(0, headers[i].find(":")); |
| 115 std::string value = headers[i].substr(headers[i].find(":") + 1); |
| 116 if (request.headers.find(key) == request.headers.end() || |
| 117 request.headers.at(key) != value) { |
| 118 scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse); |
| 119 not_found_response->set_code(HTTP_NOT_FOUND); |
| 120 return not_found_response.Pass(); |
| 121 } |
| 122 } |
| 123 } |
| 124 |
| 125 // Trim the first byte ('/'). |
| 126 std::string request_path = relative_url.substr(1); |
| 127 base::FilePath file_path(server_root.AppendASCII(request_path)); |
| 128 std::string file_contents; |
| 129 if (!base::ReadFileToString(file_path, &file_contents)) { |
| 130 file_path = file_path.AppendASCII("index.html"); |
| 131 if (!base::ReadFileToString(file_path, &file_contents)) |
| 132 return NOT_HANDLED; |
| 133 } |
| 134 |
| 135 if (query.find("replace_text") != query.end()) { |
| 136 std::vector<std::string> replacements = query["replace_text"]; |
| 137 for (size_t i = 0; i < replacements.size(); ++i) { |
| 138 std::string find, with; |
| 139 base::Base64Decode(replacements[i].substr(0, replacements[i].find(":")), |
| 140 &find); |
| 141 base::Base64Decode(replacements[i].substr(replacements[i].find(":") + 1), |
| 142 &with); |
| 143 base::ReplaceSubstringsAfterOffset(&file_contents, 0, find, with); |
| 144 } |
| 145 } |
| 146 |
| 147 base::FilePath headers_path( |
| 148 file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers"))); |
| 149 |
| 150 if (base::PathExists(headers_path)) { |
| 151 std::string headers_contents; |
| 152 |
| 153 if (!base::ReadFileToString(headers_path, &headers_contents)) |
| 154 return NOT_HANDLED; |
| 155 |
| 156 if (request.method == METHOD_HEAD) |
| 157 file_contents = ""; |
| 158 |
| 159 scoped_ptr<CustomHttpResponse> http_response( |
| 160 new CustomHttpResponse(headers_contents, file_contents)); |
| 161 return http_response.Pass(); |
| 162 } |
| 163 |
| 164 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 165 http_response->set_code(HTTP_OK); |
| 166 |
| 167 if (request.headers.find("Range") != request.headers.end()) { |
| 168 std::string range_header = request.headers.at("Range"); |
| 169 if (range_header.compare(0, 6, "bytes=") == 0) { |
| 170 range_header = range_header.substr(6); |
| 171 |
| 172 size_t start = 0; |
| 173 size_t end = file_contents.size(); |
| 174 size_t delim = range_header.find("-"); |
| 175 |
| 176 if (delim == std::string::npos) { |
| 177 start = std::atoi(range_header.c_str()); |
| 178 } else if (delim == range_header.size() - 1) { |
| 179 start = std::atoi(range_header.substr(0, delim).c_str()); |
| 180 } else if (delim == 0) { |
| 181 start = end - std::atoi(range_header.substr(1).c_str()); |
| 182 } else { |
| 183 start = std::atoi(range_header.substr(0, delim).c_str()); |
| 184 end = std::atoi(range_header.substr(delim + 1).c_str()); |
| 185 } |
| 186 |
| 187 http_response->set_code(HTTP_PARTIAL_CONTENT); |
| 188 http_response->AddCustomHeader( |
| 189 "Content-Range", |
| 190 base::StringPrintf("bytes %" PRIuS "-%" PRIuS "/%" PRIuS, start, |
| 191 end - 1, file_contents.size())); |
| 192 |
| 193 file_contents = file_contents.substr(start, end - start); |
| 194 } |
| 195 } |
| 196 |
| 197 base::FilePath::StringType ext = file_path.FinalExtension(); |
| 198 if (ext == FILE_PATH_LITERAL(".crx")) |
| 199 http_response->set_content_type("application/x-chrome-extension"); |
| 200 else if (ext == FILE_PATH_LITERAL(".exe")) |
| 201 http_response->set_content_type("application/octet-stream"); |
| 202 else if (ext == FILE_PATH_LITERAL(".gif")) |
| 203 http_response->set_content_type("image/gif"); |
| 204 else if (ext == FILE_PATH_LITERAL(".jpeg") || |
| 205 ext == FILE_PATH_LITERAL(".jpg")) |
| 206 http_response->set_content_type("image/jpeg"); |
| 207 else if (ext == FILE_PATH_LITERAL(".js")) |
| 208 http_response->set_content_type("application/javascript"); |
| 209 else if (ext == FILE_PATH_LITERAL(".json")) |
| 210 http_response->set_content_type("application/json"); |
| 211 else if (ext == FILE_PATH_LITERAL(".pdf")) |
| 212 http_response->set_content_type("application/pdf"); |
| 213 else if (ext == FILE_PATH_LITERAL(".txt")) |
| 214 http_response->set_content_type("text/plain"); |
| 215 else if (ext == FILE_PATH_LITERAL(".wav")) |
| 216 http_response->set_content_type("audio/wav"); |
| 217 else if (ext == FILE_PATH_LITERAL(".xml")) |
| 218 http_response->set_content_type("text/xml"); |
| 219 else if (ext == FILE_PATH_LITERAL(".html") || |
| 220 ext == FILE_PATH_LITERAL(".htm")) |
| 221 http_response->set_content_type("text/html"); |
| 222 |
| 223 http_response->AddCustomHeader("Accept-Ranges", "bytes"); |
| 224 http_response->AddCustomHeader("ETag", "'" + file_path.MaybeAsASCII() + "'"); |
| 225 http_response->set_content(file_contents); |
| 226 return http_response.Pass(); |
| 227 } |
| 228 |
| 229 scoped_ptr<HttpResponse> HandleRedirectConnect(const HttpRequest& request) { |
| 230 if (request.headers.find("Host") == request.headers.end() || |
| 231 request.headers.at("Host") != "www.redirect.com" || |
| 232 request.method != METHOD_CONNECT) |
| 233 return NOT_HANDLED; |
| 234 |
| 235 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 236 http_response->set_code(HTTP_FOUND); |
| 237 http_response->AddCustomHeader("Location", |
| 238 "http://www.destination.com/foo.js"); |
| 239 return http_response.Pass(); |
| 240 } |
| 241 |
| 242 scoped_ptr<HttpResponse> HandleServerAuthConnect(const HttpRequest& request) { |
| 243 if (request.headers.find("Host") == request.headers.end() || |
| 244 request.headers.at("Host") != "www.server-auth.com" || |
| 245 request.method != METHOD_CONNECT) |
| 246 return NOT_HANDLED; |
| 247 |
| 248 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 249 http_response->set_code(HTTP_UNAUTHORIZED); |
| 250 http_response->AddCustomHeader("WWW-Authenticate", |
| 251 "Basic realm=\"WallyWorld\""); |
| 252 return http_response.Pass(); |
| 253 } |
| 254 |
| 255 scoped_ptr<HttpResponse> HandleDefaultConnect(const HttpRequest& request) { |
| 256 if (request.method != METHOD_CONNECT) |
| 257 return NOT_HANDLED; |
| 258 |
| 259 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 260 http_response->set_code(HTTP_BAD_REQUEST); |
| 261 http_response->set_content( |
| 262 "Your client has issued a malformed or illegal request."); |
| 263 http_response->set_content_type("text/html"); |
| 264 return http_response.Pass(); |
| 265 } |
| 266 |
| 267 scoped_ptr<HttpResponse> HandleCacheControl(std::string url, |
| 268 std::string value, |
| 269 const HttpRequest& request) { |
| 270 if (!ShouldHandle(request, url)) |
| 271 return NOT_HANDLED; |
| 272 |
| 273 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 274 http_response->set_code(HTTP_OK); |
| 275 std::stringstream time; |
| 276 time << std::time(0); |
| 277 http_response->set_content("<html><head><title>" + time.str() + |
| 278 "</title></head></html>"); |
| 279 http_response->set_content_type("text/html"); |
| 280 http_response->AddCustomHeader("Cache-Control", value); |
| 281 return http_response.Pass(); |
| 282 } |
| 283 |
| 284 scoped_ptr<HttpResponse> HandleCacheExpires(const HttpRequest& request) { |
| 285 if (!ShouldHandle(request, "/cache/expires")) |
| 286 return NOT_HANDLED; |
| 287 |
| 288 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 289 http_response->set_code(HTTP_OK); |
| 290 std::stringstream time; |
| 291 time << std::time(0); |
| 292 http_response->set_content("<html><head><title>" + time.str() + |
| 293 "</title></head></html>"); |
| 294 http_response->set_content_type("text/html"); |
| 295 http_response->AddCustomHeader("Expires", "Thu, 1 Jan 2099 00:00:00 GMT"); |
| 296 return http_response.Pass(); |
| 297 } |
| 298 |
| 299 scoped_ptr<HttpResponse> HandleEchoHeader(std::string url, |
| 300 std::string value, |
| 301 const HttpRequest& request) { |
| 302 if (!ShouldHandle(request, url)) |
| 303 return NOT_HANDLED; |
| 304 |
| 305 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 306 http_response->set_code(HTTP_OK); |
| 307 |
| 308 GURL request_gurl = ToGURL(request); |
| 309 if (request_gurl.has_query()) { |
| 310 std::string header_name = request_gurl.query(); |
| 311 http_response->AddCustomHeader("Vary", header_name); |
| 312 if (request.headers.find(header_name) != request.headers.end()) |
| 313 http_response->set_content(request.headers.at(header_name)); |
| 314 else |
| 315 http_response->set_content("None"); |
| 316 } |
| 317 |
| 318 http_response->set_content_type("text/plain"); |
| 319 http_response->AddCustomHeader("Cache-Control", value); |
| 320 return http_response.Pass(); |
| 321 } |
| 322 |
| 323 scoped_ptr<HttpResponse> HandleEcho(const HttpRequest& request) { |
| 324 if (!ShouldHandle(request, "/echo")) |
| 325 return NOT_HANDLED; |
| 326 |
| 327 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 328 http_response->set_code(HTTP_OK); |
| 329 |
| 330 GURL request_gurl = ToGURL(request); |
| 331 if (request_gurl.has_query()) { |
| 332 RequestQuery query = ParseQuery(request_gurl); |
| 333 if (query.find("status") != query.end()) |
| 334 http_response->set_code(static_cast<HttpStatusCode>( |
| 335 std::atoi(query["status"].front().c_str()))); |
| 336 } |
| 337 |
| 338 http_response->set_content_type("text/html"); |
| 339 if (request.method != METHOD_POST && request.method != METHOD_PUT) |
| 340 http_response->set_content("Echo"); |
| 341 else |
| 342 http_response->set_content(request.content); |
| 343 return http_response.Pass(); |
| 344 } |
| 345 |
| 346 scoped_ptr<HttpResponse> HandleEchoTitle(const HttpRequest& request) { |
| 347 if (!ShouldHandle(request, "/echotitle")) |
| 348 return NOT_HANDLED; |
| 349 |
| 350 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 351 http_response->set_code(HTTP_OK); |
| 352 http_response->set_content_type("text/html"); |
| 353 http_response->set_content("<html><head><title>" + request.content + |
| 354 "</title></head></html>"); |
| 355 return http_response.Pass(); |
| 356 } |
| 357 |
| 358 scoped_ptr<HttpResponse> HandleEchoAll(const HttpRequest& request) { |
| 359 if (!ShouldHandle(request, "/echoall")) |
| 360 return NOT_HANDLED; |
| 361 |
| 362 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 363 http_response->set_code(HTTP_OK); |
| 364 |
| 365 std::string body; |
| 366 body += |
| 367 "<html><head><style>" |
| 368 "pre { border: 1px solid black; margin: 5px; padding: 5px }" |
| 369 "</style></head><body>" |
| 370 "<div style=\"float: right\">" |
| 371 "<a href=\"/echo\">back to referring page</a></div>" |
| 372 "<h1>Request Body:</h1><pre>"; |
| 373 |
| 374 if (request.has_content) { |
| 375 std::vector<std::string> qs = base::SplitString( |
| 376 request.content, "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| 377 for (size_t i = 0; i < qs.size(); ++i) { |
| 378 body += qs[i] + "\n"; |
| 379 } |
| 380 } |
| 381 |
| 382 body += |
| 383 "</pre>" |
| 384 "<h1>Request Headers:</h1><pre>" + |
| 385 request.all_headers + |
| 386 "</pre>" |
| 387 "</body></html>"; |
| 388 |
| 389 http_response->set_content_type("text/html"); |
| 390 http_response->set_content(body); |
| 391 return http_response.Pass(); |
| 392 } |
| 393 |
| 394 scoped_ptr<HttpResponse> HandleSetCookie(const HttpRequest& request) { |
| 395 if (!ShouldHandle(request, "/set-cookie")) |
| 396 return NOT_HANDLED; |
| 397 |
| 398 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 399 http_response->set_code(HTTP_OK); |
| 400 http_response->set_content_type("text/html"); |
| 401 std::string content; |
| 402 GURL request_gurl = ToGURL(request); |
| 403 if (request_gurl.has_query()) { |
| 404 std::vector<std::string> cookies = base::SplitString( |
| 405 request_gurl.query(), "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| 406 for (size_t i = 0; i < cookies.size(); ++i) { |
| 407 http_response->AddCustomHeader("Set-Cookie", cookies[i]); |
| 408 content += cookies[i]; |
| 409 } |
| 410 } |
| 411 |
| 412 http_response->set_content(content); |
| 413 return http_response.Pass(); |
| 414 } |
| 415 |
| 416 scoped_ptr<HttpResponse> HandleSetManyCookies(const HttpRequest& request) { |
| 417 if (!ShouldHandle(request, "/set-many-cookies")) |
| 418 return NOT_HANDLED; |
| 419 |
| 420 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 421 http_response->set_code(HTTP_OK); |
| 422 http_response->set_content_type("text/html"); |
| 423 std::string content; |
| 424 |
| 425 GURL request_gurl = ToGURL(request); |
| 426 size_t num = 0; |
| 427 if (request_gurl.has_query()) { |
| 428 num = std::atoi(request_gurl.query().c_str()); |
| 429 } |
| 430 |
| 431 for (size_t i = 0; i < num; ++i) { |
| 432 http_response->AddCustomHeader("Set-Cookie", "a="); |
| 433 } |
| 434 |
| 435 http_response->set_content( |
| 436 base::StringPrintf("%" PRIuS " cookies were sent", num)); |
| 437 return http_response.Pass(); |
| 438 } |
| 439 |
| 440 scoped_ptr<HttpResponse> HandleExpectAndSetCookie(const HttpRequest& request) { |
| 441 if (!ShouldHandle(request, "/expect-and-set-cookie")) |
| 442 return NOT_HANDLED; |
| 443 |
| 444 std::vector<std::string> sentCookies; |
| 445 if (request.headers.find("Cookie") != request.headers.end()) { |
| 446 std::vector<std::string> cookies = |
| 447 base::SplitString(request.headers.at("Cookie"), ";", |
| 448 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| 449 for (size_t i = 0; i < cookies.size(); ++i) { |
| 450 sentCookies.push_back(cookies[i]); |
| 451 } |
| 452 } |
| 453 bool gotExpected = true; |
| 454 |
| 455 GURL request_gurl = ToGURL(request); |
| 456 RequestQuery qs = ParseQuery(request_gurl); |
| 457 if (qs.find("expect") != qs.end()) { |
| 458 std::vector<std::string> expected = qs.at("expect"); |
| 459 for (size_t i = 0; i < expected.size(); ++i) { |
| 460 bool found = false; |
| 461 for (size_t j = 0; j < sentCookies.size(); ++j) { |
| 462 if (expected[i] == sentCookies[j]) |
| 463 found = true; |
| 464 } |
| 465 gotExpected &= found; |
| 466 } |
| 467 } |
| 468 |
| 469 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 470 http_response->set_code(HTTP_OK); |
| 471 http_response->set_content_type("text/html"); |
| 472 if (gotExpected) { |
| 473 std::vector<std::string> setCookies = qs.at("set"); |
| 474 for (size_t i = 0; i < setCookies.size(); ++i) { |
| 475 http_response->AddCustomHeader( |
| 476 "Set-Cookie", net::UnescapeURLComponent(setCookies[i], UNESCAPE_ALL)); |
| 477 } |
| 478 } |
| 479 |
| 480 std::string content; |
| 481 if (qs.find("data") != qs.end()) { |
| 482 std::vector<std::string> data = qs.at("data"); |
| 483 for (size_t i = 0; i < data.size(); ++i) { |
| 484 content += data[i]; |
| 485 } |
| 486 } |
| 487 |
| 488 http_response->set_content(content); |
| 489 return http_response.Pass(); |
| 490 } |
| 491 |
| 492 scoped_ptr<HttpResponse> HandleSetHeader(const HttpRequest& request) { |
| 493 if (!ShouldHandle(request, "/set-header")) |
| 494 return NOT_HANDLED; |
| 495 |
| 496 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 497 http_response->set_code(HTTP_OK); |
| 498 http_response->set_content_type("text/html"); |
| 499 |
| 500 std::string content; |
| 501 |
| 502 GURL request_gurl = ToGURL(request); |
| 503 if (request_gurl.has_query()) { |
| 504 RequestQuery headers = ParseQuery(request_gurl); |
| 505 for (RequestQuery::iterator it = headers.begin(); it != headers.end(); |
| 506 ++it) { |
| 507 std::string header = it->first; |
| 508 std::string key = header.substr(0, header.find(": ")); |
| 509 std::string value = header.substr(header.find(": ") + 2); |
| 510 http_response->AddCustomHeader(key, value); |
| 511 content += header; |
| 512 } |
| 513 } |
| 514 |
| 515 http_response->set_content(content); |
| 516 return http_response.Pass(); |
| 517 } |
| 518 |
| 519 scoped_ptr<HttpResponse> HandleNoContent(const HttpRequest& request) { |
| 520 if (!ShouldHandle(request, "/nocontent")) |
| 521 return NOT_HANDLED; |
| 522 |
| 523 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 524 http_response->set_code(HTTP_NO_CONTENT); |
| 525 return http_response.Pass(); |
| 526 } |
| 527 |
| 528 scoped_ptr<HttpResponse> HandleCloseSocket(const HttpRequest& request) { |
| 529 if (!ShouldHandle(request, "/close-socket")) |
| 530 return NOT_HANDLED; |
| 531 |
| 532 scoped_ptr<CustomHttpResponse> http_response(new CustomHttpResponse("", "")); |
| 533 return http_response.Pass(); |
| 534 } |
| 535 |
| 536 scoped_ptr<HttpResponse> HandleAuthBasic(const HttpRequest& request) { |
| 537 if (!ShouldHandle(request, "/auth-basic")) |
| 538 return NOT_HANDLED; |
| 539 |
| 540 std::string username, userpass, password, b64str; |
| 541 std::string expectedPassword = "secret"; |
| 542 std::string realm = "testrealm"; |
| 543 bool setCookie = false; |
| 544 |
| 545 GURL request_gurl = ToGURL(request); |
| 546 RequestQuery query = ParseQuery(request_gurl); |
| 547 |
| 548 if (query.find("set-cookie-if-challenged") != query.end()) |
| 549 setCookie = true; |
| 550 if (query.find("password") != query.end()) |
| 551 expectedPassword = query.at("password").front(); |
| 552 if (query.find("realm") != query.end()) |
| 553 realm = query.at("realm").front(); |
| 554 |
| 555 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 556 bool authed = false; |
| 557 std::string error; |
| 558 std::string auth; |
| 559 if (request.headers.find("Authorization") == request.headers.end()) { |
| 560 error = "Missing Authorization Header"; |
| 561 } else { |
| 562 auth = request.headers.at("Authorization"); |
| 563 b64str = auth.substr(std::string("Basic ").size()); |
| 564 base::Base64Decode(b64str, &userpass); |
| 565 username = userpass.substr(0, userpass.find(":")); |
| 566 password = userpass.substr(userpass.find(":") + 1); |
| 567 if (password == expectedPassword) |
| 568 authed = true; |
| 569 else |
| 570 error = "Invalid Credentials"; |
| 571 } |
| 572 |
| 573 if (!authed) { |
| 574 http_response->set_code(HTTP_UNAUTHORIZED); |
| 575 http_response->set_content_type("text/html"); |
| 576 http_response->AddCustomHeader("WWW-Authenticate", |
| 577 "Basic realm=\"" + realm + "\""); |
| 578 if (setCookie) |
| 579 http_response->AddCustomHeader("Set-Cookie", "got_challenged=true"); |
| 580 http_response->set_content(base::StringPrintf( |
| 581 "<html><head><title>Denied: %s</title></head>" |
| 582 "<body>auth=%s<p>b64str=%s<p>username: %s<p>userpass: %s<p>" |
| 583 "password: %s<p>You sent:<br>%s<p></body></html>", |
| 584 error.c_str(), auth.c_str(), b64str.c_str(), username.c_str(), |
| 585 userpass.c_str(), password.c_str(), request.all_headers.c_str())); |
| 586 return http_response.Pass(); |
| 587 } |
| 588 |
| 589 if (request.headers.find("If-None-Match") != request.headers.end() && |
| 590 request.headers.at("If-None-Match") == "abc") { |
| 591 http_response->set_code(HTTP_NOT_MODIFIED); |
| 592 return http_response.Pass(); |
| 593 } |
| 594 |
| 595 base::FilePath file_path = |
| 596 base::FilePath().AppendASCII(request.relative_url.substr(1)); |
| 597 if (file_path.FinalExtension() == FILE_PATH_LITERAL("gif")) { |
| 598 base::FilePath server_root; |
| 599 PathService::Get(base::DIR_SOURCE_ROOT, &server_root); |
| 600 base::FilePath gif_path = |
| 601 server_root.AppendASCII("chrome/test/data/google/logo.gif"); |
| 602 std::string gif_data; |
| 603 base::ReadFileToString(gif_path, &gif_data); |
| 604 http_response->set_content_type("image/gif"); |
| 605 http_response->set_content(gif_data); |
| 606 } else { |
| 607 http_response->set_content_type("text/html"); |
| 608 http_response->set_content( |
| 609 base::StringPrintf("<html><head><title>%s/%s</title></head>" |
| 610 "<body>auth=%s<p>You sent:<br>%s<p></body></html>", |
| 611 username.c_str(), password.c_str(), auth.c_str(), |
| 612 request.all_headers.c_str())); |
| 613 } |
| 614 |
| 615 http_response->set_code(HTTP_OK); |
| 616 http_response->AddCustomHeader("Cache-Control", "max-age=60000"); |
| 617 http_response->AddCustomHeader("Etag", "abc"); |
| 618 return http_response.Pass(); |
| 619 } |
| 620 |
| 621 scoped_ptr<HttpResponse> HandleAuthDigest(const HttpRequest& request) { |
| 622 if (!ShouldHandle(request, "/auth-digest")) |
| 623 return NOT_HANDLED; |
| 624 |
| 625 std::string nonce = base::MD5String( |
| 626 base::StringPrintf("privatekey%s", request.relative_url.c_str())); |
| 627 std::string opaque = base::MD5String("opaque"); |
| 628 std::string password = "secret"; |
| 629 std::string realm = "testrealm"; |
| 630 |
| 631 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 632 bool authed = false; |
| 633 std::string error; |
| 634 std::string auth; |
| 635 std::string digestStr = "Digest"; |
| 636 std::string username; |
| 637 if (request.headers.find("Authorization") == request.headers.end()) { |
| 638 error = "no auth"; |
| 639 } else if (request.headers.at("Authorization").substr(0, digestStr.size()) != |
| 640 digestStr) { |
| 641 error = "not digest"; |
| 642 } else { |
| 643 auth = request.headers.at("Authorization"); |
| 644 |
| 645 std::map<std::string, std::string> authPairs; |
| 646 std::vector<std::string> authVector = base::SplitString( |
| 647 auth, ", ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| 648 for (size_t i = 0; i < authVector.size(); ++i) { |
| 649 std::string key = authVector[i].substr(0, authVector[i].find("=")); |
| 650 std::string value = authVector[i].substr(authVector[i].find("=") + 1); |
| 651 if (value.substr(0, 1) == "\"" && value.substr(value.size() - 1) == "\"") |
| 652 value = value.substr(1, value.size() - 2); |
| 653 authPairs[key] = value; |
| 654 } |
| 655 |
| 656 if (authPairs["nonce"] != nonce) { |
| 657 error = "wrong nonce"; |
| 658 } else if (authPairs["opaque"] != opaque) { |
| 659 error = "wrong opaque"; |
| 660 } else { |
| 661 username = authPairs["username"]; |
| 662 |
| 663 std::string hA1 = base::MD5String( |
| 664 base::StringPrintf("%s:%s:%s", authPairs["username"].c_str(), |
| 665 realm.c_str(), password.c_str())); |
| 666 std::string hA2 = base::MD5String(base::StringPrintf( |
| 667 "%s:%s", request.method_string.c_str(), authPairs["uri"].c_str())); |
| 668 |
| 669 std::string response; |
| 670 if (authPairs.find("qop") != authPairs.end() && |
| 671 authPairs.find("nc") != authPairs.end() && |
| 672 authPairs.find("cnonce") != authPairs.end()) { |
| 673 response = base::MD5String(base::StringPrintf( |
| 674 "%s:%s:%s:%s:%s:%s", hA1.c_str(), nonce.c_str(), |
| 675 authPairs["nc"].c_str(), authPairs["cnonce"].c_str(), |
| 676 authPairs["qop"].c_str(), hA2.c_str())); |
| 677 } else { |
| 678 response = base::MD5String(base::StringPrintf( |
| 679 "%s:%s:%s", hA1.c_str(), nonce.c_str(), hA2.c_str())); |
| 680 } |
| 681 |
| 682 if (authPairs["response"] == response) |
| 683 authed = true; |
| 684 else |
| 685 error = "wrong password"; |
| 686 } |
| 687 } |
| 688 |
| 689 if (!authed) { |
| 690 http_response->set_code(HTTP_UNAUTHORIZED); |
| 691 http_response->set_content_type("text/html"); |
| 692 std::string authHeader = base::StringPrintf( |
| 693 "Digest realm=\"%s\", " |
| 694 "domain=\"/\", qop=\"auth\", algorithm=MD5, nonce=\"%s\", " |
| 695 "opaque=\"%s\"", |
| 696 realm.c_str(), nonce.c_str(), opaque.c_str()); |
| 697 http_response->AddCustomHeader("WWW-Authenticate", authHeader); |
| 698 http_response->set_content(base::StringPrintf( |
| 699 "<html><head><title>Denied: %s</title></head>" |
| 700 "<body>auth=%s<p>" |
| 701 "You sent:<br>%s<p>We are replying:<br>%s<p></body></html>", |
| 702 error.c_str(), auth.c_str(), request.all_headers.c_str(), |
| 703 authHeader.c_str())); |
| 704 return http_response.Pass(); |
| 705 } |
| 706 |
| 707 http_response->set_content_type("text/html"); |
| 708 http_response->set_content( |
| 709 base::StringPrintf("<html><head><title>%s/%s</title></head>" |
| 710 "<body>auth=%s<p></body></html>", |
| 711 username.c_str(), password.c_str(), auth.c_str())); |
| 712 |
| 713 return http_response.Pass(); |
| 714 } |
| 715 |
| 716 scoped_ptr<HttpResponse> HandleServerRedirect(const HttpRequest& request) { |
| 717 if (!ShouldHandle(request, "/server-redirect")) |
| 718 return NOT_HANDLED; |
| 719 |
| 720 GURL request_gurl = ToGURL(request); |
| 721 std::string dest = |
| 722 net::UnescapeURLComponent(request_gurl.query(), UNESCAPE_ALL); |
| 723 |
| 724 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 725 http_response->set_code(HTTP_MOVED_PERMANENTLY); |
| 726 http_response->AddCustomHeader("Location", dest); |
| 727 http_response->set_content_type("text/html"); |
| 728 http_response->set_content(base::StringPrintf( |
| 729 "<html><head></head><body>Redirecting to %s</body></html>", |
| 730 dest.c_str())); |
| 731 return http_response.Pass(); |
| 732 } |
| 733 |
| 734 scoped_ptr<HttpResponse> HandleCrossSiteRedirect(EmbeddedTestServer* server, |
| 735 const HttpRequest& request) { |
| 736 if (!ShouldHandle(request, "/cross-site")) |
| 737 return NOT_HANDLED; |
| 738 |
| 739 std::string destAll = net::UnescapeURLComponent( |
| 740 request.relative_url.substr(std::string("/cross-site").size() + 1), |
| 741 UNESCAPE_ALL); |
| 742 |
| 743 std::string dest = base::StringPrintf( |
| 744 "//%s:%hu/%s", destAll.substr(0, destAll.find("/")).c_str(), |
| 745 server->port(), destAll.substr(destAll.find("/") + 1).c_str()); |
| 746 |
| 747 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 748 http_response->set_code(HTTP_MOVED_PERMANENTLY); |
| 749 http_response->AddCustomHeader("Location", dest); |
| 750 http_response->set_content_type("text/html"); |
| 751 http_response->set_content(base::StringPrintf( |
| 752 "<html><head></head><body>Redirecting to %s</body></html>", |
| 753 dest.c_str())); |
| 754 return http_response.Pass(); |
| 755 } |
| 756 |
| 757 scoped_ptr<HttpResponse> HandleClientRedirect(const HttpRequest& request) { |
| 758 if (!ShouldHandle(request, "/client-redirect")) |
| 759 return NOT_HANDLED; |
| 760 |
| 761 GURL request_gurl = ToGURL(request); |
| 762 std::string dest = |
| 763 net::UnescapeURLComponent(request_gurl.query(), UNESCAPE_ALL); |
| 764 |
| 765 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 766 http_response->set_code(HTTP_OK); |
| 767 http_response->set_content_type("text/html"); |
| 768 http_response->set_content(base::StringPrintf( |
| 769 "<html><head><meta http-equiv=\"refresh\" content=\"0;url=%s\"></head>" |
| 770 "<body>Redirecting to %s</body></html>", |
| 771 dest.c_str(), dest.c_str())); |
| 772 return http_response.Pass(); |
| 773 } |
| 774 |
| 775 scoped_ptr<HttpResponse> HandleDefaultResponse(const HttpRequest& request) { |
| 776 if (!ShouldHandle(request, "/defaultresponse")) |
| 777 return NOT_HANDLED; |
| 778 |
| 779 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |
| 780 http_response->set_code(HTTP_OK); |
| 781 http_response->set_content_type("text/html"); |
| 782 http_response->set_content("Default response given for path: " + |
| 783 request.relative_url); |
| 784 return http_response.Pass(); |
| 785 } |
| 786 |
| 787 class DelayedHttpResponse : public BasicHttpResponse { |
| 788 public: |
| 789 DelayedHttpResponse(double delay) : delay_(delay) {} |
| 790 |
| 791 void SendResponse(SendCallback send, SendDoneCallback done) override { |
| 792 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 793 FROM_HERE, base::Bind(send, ToResponseString(), done), |
| 794 base::TimeDelta::FromSecondsD(delay_)); |
| 795 } |
| 796 |
| 797 private: |
| 798 double delay_; |
| 799 }; |
| 800 |
| 801 scoped_ptr<HttpResponse> HandleSlowServer(const HttpRequest& request) { |
| 802 if (!ShouldHandle(request, "/slow")) |
| 803 return NOT_HANDLED; |
| 804 |
| 805 double delay = 1.0f; |
| 806 |
| 807 GURL request_gurl = ToGURL(request); |
| 808 if (request_gurl.has_query()) { |
| 809 delay = std::atof(request_gurl.query().c_str()); |
| 810 } |
| 811 |
| 812 scoped_ptr<BasicHttpResponse> http_response(new DelayedHttpResponse(delay)); |
| 813 http_response->set_code(HTTP_OK); |
| 814 http_response->set_content_type("text/plain"); |
| 815 http_response->set_content(base::StringPrintf("waited %.1f seconds", delay)); |
| 816 return http_response.Pass(); |
| 817 } |
| 818 |
| 819 void RegisterDefaultHandlers(EmbeddedTestServer* server) { |
| 820 server->RegisterDefaultHandler(base::Bind(&HandleRedirectConnect)); |
| 821 server->RegisterDefaultHandler(base::Bind(&HandleServerAuthConnect)); |
| 822 server->RegisterDefaultHandler(base::Bind(&HandleDefaultConnect)); |
| 823 |
| 824 server->RegisterDefaultHandler( |
| 825 base::Bind(&HandleCacheControl, "/nocachetime/maxage", "max-age=0")); |
| 826 server->RegisterDefaultHandler( |
| 827 base::Bind(&HandleCacheControl, "/nocachetime", "no-cache")); |
| 828 server->RegisterDefaultHandler( |
| 829 base::Bind(&HandleCacheControl, "/cachetime", "max-age=60")); |
| 830 server->RegisterDefaultHandler(base::Bind(&HandleCacheExpires)); |
| 831 server->RegisterDefaultHandler(base::Bind(&HandleCacheControl, |
| 832 "/cache/proxy-revalidate", |
| 833 "max-age=60, proxy-revalidate")); |
| 834 server->RegisterDefaultHandler( |
| 835 base::Bind(&HandleCacheControl, "/cache/private", "max-age=3, private")); |
| 836 server->RegisterDefaultHandler( |
| 837 base::Bind(&HandleCacheControl, "/cache/public", "max-age=3, public")); |
| 838 server->RegisterDefaultHandler(base::Bind(&HandleCacheControl, |
| 839 "/cache/s-maxage", |
| 840 "public, s-maxage=60, max-age=0")); |
| 841 server->RegisterDefaultHandler(base::Bind( |
| 842 &HandleCacheControl, "/cache/must-revalidate", "must-revalidate")); |
| 843 server->RegisterDefaultHandler(base::Bind(&HandleCacheControl, |
| 844 "/cache/must-revalidate/max-age", |
| 845 "max-age=60, must-revalidate")); |
| 846 server->RegisterDefaultHandler( |
| 847 base::Bind(&HandleCacheControl, "/cache/no-store", "no-store")); |
| 848 server->RegisterDefaultHandler(base::Bind( |
| 849 &HandleCacheControl, "/cache/no-store/max-age", "max-age=60, no-store")); |
| 850 server->RegisterDefaultHandler( |
| 851 base::Bind(&HandleCacheControl, "/cache/no-transform", "no-transform")); |
| 852 |
| 853 server->RegisterDefaultHandler( |
| 854 base::Bind(&HandleEchoHeader, "/echoheader", "no-cache")); |
| 855 server->RegisterDefaultHandler( |
| 856 base::Bind(&HandleEchoHeader, "/echoheadercache", "max-age=60000")); |
| 857 server->RegisterDefaultHandler(base::Bind(&HandleEcho)); |
| 858 server->RegisterDefaultHandler(base::Bind(&HandleEchoTitle)); |
| 859 server->RegisterDefaultHandler(base::Bind(&HandleEchoAll)); |
| 860 |
| 861 server->RegisterDefaultHandler(base::Bind(&HandleSetCookie)); |
| 862 server->RegisterDefaultHandler(base::Bind(&HandleSetManyCookies)); |
| 863 server->RegisterDefaultHandler(base::Bind(&HandleExpectAndSetCookie)); |
| 864 server->RegisterDefaultHandler(base::Bind(&HandleSetHeader)); |
| 865 server->RegisterDefaultHandler(base::Bind(&HandleNoContent)); |
| 866 server->RegisterDefaultHandler(base::Bind(&HandleCloseSocket)); |
| 867 server->RegisterDefaultHandler(base::Bind(&HandleAuthBasic)); |
| 868 server->RegisterDefaultHandler(base::Bind(&HandleAuthDigest)); |
| 869 server->RegisterDefaultHandler(base::Bind(&HandleServerRedirect)); |
| 870 server->RegisterDefaultHandler(base::Bind(&HandleCrossSiteRedirect, server)); |
| 871 server->RegisterDefaultHandler(base::Bind(&HandleClientRedirect)); |
| 872 server->RegisterDefaultHandler(base::Bind(&HandleDefaultResponse)); |
| 873 server->RegisterDefaultHandler(base::Bind(&HandleSlowServer)); |
| 874 |
| 875 // TODO(svaldez): HandleDownload |
| 876 // TODO(svaldez): HandleDownloadFinish |
| 877 // TODO(svaldez): HandleZipFile |
| 878 // TODO(svaldez): HandleRangeReset |
| 879 // TODO(svaldez): HandleSSLManySmallRecords |
| 880 // TODO(svaldez): HandleChunkedServer |
| 881 // TODO(svaldez): HandleGetSSLSessionCache |
| 882 // TODO(svaldez): HandleGetChannelID |
| 883 // TODO(svaldez): HandleGetClientCert |
| 884 // TODO(svaldez): HandleClientCipherList |
| 885 // TODO(svaldez): HandleEchoMultipartPost |
| 886 } |
| 887 |
| 888 void GetFilePathWithReplacements(const std::string& original_file_path, |
| 889 const base::StringPairs& text_to_replace, |
| 890 std::string* replacement_path) { |
| 891 std::string new_file_path = original_file_path; |
| 892 bool first_query_parameter = true; |
| 893 const base::StringPairs::const_iterator end = text_to_replace.end(); |
| 894 for (base::StringPairs::const_iterator it = text_to_replace.begin(); |
| 895 it != end; ++it) { |
| 896 const std::string& old_text = it->first; |
| 897 const std::string& new_text = it->second; |
| 898 std::string base64_old; |
| 899 std::string base64_new; |
| 900 base::Base64Encode(old_text, &base64_old); |
| 901 base::Base64Encode(new_text, &base64_new); |
| 902 if (first_query_parameter) { |
| 903 new_file_path += "?"; |
| 904 first_query_parameter = false; |
| 905 } else { |
| 906 new_file_path += "&"; |
| 907 } |
| 908 new_file_path += "replace_text="; |
| 909 new_file_path += base64_old; |
| 910 new_file_path += ":"; |
| 911 new_file_path += base64_new; |
| 912 } |
| 913 |
| 914 *replacement_path = new_file_path; |
| 915 } |
| 916 |
| 917 } // namespace test_server |
| 918 } // namespace net |
| OLD | NEW |