| Index: net/test/embedded_test_server/request_helpers.cc
|
| diff --git a/net/test/embedded_test_server/request_helpers.cc b/net/test/embedded_test_server/request_helpers.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e2ed006c2f149f1aed293875cf7a6fe2be95d984
|
| --- /dev/null
|
| +++ b/net/test/embedded_test_server/request_helpers.cc
|
| @@ -0,0 +1,918 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/test/embedded_test_server/request_helpers.h"
|
| +
|
| +#include <stdlib.h>
|
| +#include <ctime>
|
| +#include <map>
|
| +#include <sstream>
|
| +#include <string>
|
| +
|
| +#include "base/base64.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/format_macros.h"
|
| +#include "base/md5.h"
|
| +#include "base/path_service.h"
|
| +#include "base/strings/string_split.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| +#include "base/threading/thread_restrictions.h"
|
| +#include "base/time/time.h"
|
| +#include "net/base/escape.h"
|
| +#include "net/base/url_util.h"
|
| +#include "net/test/embedded_test_server/embedded_test_server.h"
|
| +#include "net/test/embedded_test_server/http_request.h"
|
| +#include "net/test/embedded_test_server/http_response.h"
|
| +
|
| +#define NOT_HANDLED scoped_ptr<HttpResponse>()
|
| +
|
| +net::UnescapeRule::Type UNESCAPE_ALL =
|
| + net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS |
|
| + net::UnescapeRule::SPOOFING_AND_CONTROL_CHARS |
|
| + net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
|
| +
|
| +namespace net {
|
| +namespace test_server {
|
| +
|
| +void CustomHttpResponse::SendResponse(SendCallback send,
|
| + SendDoneCallback done) {
|
| + std::string response;
|
| + if (headers_.size() != 0 || contents_.size() != 0)
|
| + response = headers_ + "\r\n" + contents_;
|
| + send.Run(response, done);
|
| +}
|
| +
|
| +bool ShouldHandle(const HttpRequest& request, std::string url) {
|
| + if (request.relative_url.compare(0, url.size(), url) == 0) {
|
| + if (request.relative_url.size() == url.size())
|
| + return true;
|
| + std::string endings("/?;#");
|
| + if (endings.find(request.relative_url.at(url.size())) != std::string::npos)
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +GURL ToGURL(const HttpRequest& request) {
|
| + return GURL("http://localhost" + request.relative_url);
|
| +}
|
| +
|
| +RequestQuery ParseQuery(GURL url) {
|
| + RequestQuery queries;
|
| +
|
| + for (QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
|
| + queries[net::UnescapeURLComponent(it.GetKey(), UNESCAPE_ALL)].push_back(
|
| + it.GetUnescapedValue());
|
| + }
|
| + return queries;
|
| +}
|
| +
|
| +// Handles |request| by serving a file from under |server_root|.
|
| +scoped_ptr<HttpResponse> HandleFileRequest(const base::FilePath& server_root,
|
| + const HttpRequest& request) {
|
| + // This is a test-only server. Ignore I/O thread restrictions.
|
| + base::ThreadRestrictions::ScopedAllowIO allow_io;
|
| +
|
| + std::string relative_url(request.relative_url);
|
| +
|
| + // A proxy request will have an absolute path. Simulate the proxy by stripping
|
| + // the scheme, host, and port.
|
| + GURL request_gurl = ToGURL(request);
|
| + relative_url = request_gurl.path();
|
| +
|
| + std::string files_prefix("/files/");
|
| + if (relative_url.compare(0, files_prefix.size(), files_prefix) == 0) {
|
| + LOG(ERROR) << "Using old-style /files/ for: " << relative_url;
|
| + relative_url = relative_url.substr(files_prefix.size() - 1);
|
| + }
|
| +
|
| + std::string post_prefix("/post/");
|
| + if (relative_url.compare(0, post_prefix.size(), post_prefix) == 0) {
|
| + relative_url = relative_url.substr(post_prefix.size() - 1);
|
| + if (request.method != METHOD_POST)
|
| + return NOT_HANDLED;
|
| + }
|
| +
|
| + RequestQuery query = ParseQuery(request_gurl);
|
| +
|
| + if (query.find("expected_body") != query.end()) {
|
| + if (request.content.find(query["expected_body"].front()) ==
|
| + std::string::npos) {
|
| + scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse);
|
| + not_found_response->set_code(HTTP_NOT_FOUND);
|
| + return not_found_response.Pass();
|
| + }
|
| + }
|
| +
|
| + if (query.find("expected_headers") != query.end()) {
|
| + std::vector<std::string> headers = query["expected_headers"];
|
| + for (size_t i = 0; i < headers.size(); ++i) {
|
| + std::string key = headers[i].substr(0, headers[i].find(":"));
|
| + std::string value = headers[i].substr(headers[i].find(":") + 1);
|
| + if (request.headers.find(key) == request.headers.end() ||
|
| + request.headers.at(key) != value) {
|
| + scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse);
|
| + not_found_response->set_code(HTTP_NOT_FOUND);
|
| + return not_found_response.Pass();
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Trim the first byte ('/').
|
| + std::string request_path = relative_url.substr(1);
|
| + base::FilePath file_path(server_root.AppendASCII(request_path));
|
| + std::string file_contents;
|
| + if (!base::ReadFileToString(file_path, &file_contents)) {
|
| + file_path = file_path.AppendASCII("index.html");
|
| + if (!base::ReadFileToString(file_path, &file_contents))
|
| + return NOT_HANDLED;
|
| + }
|
| +
|
| + if (query.find("replace_text") != query.end()) {
|
| + std::vector<std::string> replacements = query["replace_text"];
|
| + for (size_t i = 0; i < replacements.size(); ++i) {
|
| + std::string find, with;
|
| + base::Base64Decode(replacements[i].substr(0, replacements[i].find(":")),
|
| + &find);
|
| + base::Base64Decode(replacements[i].substr(replacements[i].find(":") + 1),
|
| + &with);
|
| + base::ReplaceSubstringsAfterOffset(&file_contents, 0, find, with);
|
| + }
|
| + }
|
| +
|
| + base::FilePath headers_path(
|
| + file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers")));
|
| +
|
| + if (base::PathExists(headers_path)) {
|
| + std::string headers_contents;
|
| +
|
| + if (!base::ReadFileToString(headers_path, &headers_contents))
|
| + return NOT_HANDLED;
|
| +
|
| + if (request.method == METHOD_HEAD)
|
| + file_contents = "";
|
| +
|
| + scoped_ptr<CustomHttpResponse> http_response(
|
| + new CustomHttpResponse(headers_contents, file_contents));
|
| + return http_response.Pass();
|
| + }
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| +
|
| + if (request.headers.find("Range") != request.headers.end()) {
|
| + std::string range_header = request.headers.at("Range");
|
| + if (range_header.compare(0, 6, "bytes=") == 0) {
|
| + range_header = range_header.substr(6);
|
| +
|
| + size_t start = 0;
|
| + size_t end = file_contents.size();
|
| + size_t delim = range_header.find("-");
|
| +
|
| + if (delim == std::string::npos) {
|
| + start = std::atoi(range_header.c_str());
|
| + } else if (delim == range_header.size() - 1) {
|
| + start = std::atoi(range_header.substr(0, delim).c_str());
|
| + } else if (delim == 0) {
|
| + start = end - std::atoi(range_header.substr(1).c_str());
|
| + } else {
|
| + start = std::atoi(range_header.substr(0, delim).c_str());
|
| + end = std::atoi(range_header.substr(delim + 1).c_str());
|
| + }
|
| +
|
| + http_response->set_code(HTTP_PARTIAL_CONTENT);
|
| + http_response->AddCustomHeader(
|
| + "Content-Range",
|
| + base::StringPrintf("bytes %" PRIuS "-%" PRIuS "/%" PRIuS, start,
|
| + end - 1, file_contents.size()));
|
| +
|
| + file_contents = file_contents.substr(start, end - start);
|
| + }
|
| + }
|
| +
|
| + base::FilePath::StringType ext = file_path.FinalExtension();
|
| + if (ext == FILE_PATH_LITERAL(".crx"))
|
| + http_response->set_content_type("application/x-chrome-extension");
|
| + else if (ext == FILE_PATH_LITERAL(".exe"))
|
| + http_response->set_content_type("application/octet-stream");
|
| + else if (ext == FILE_PATH_LITERAL(".gif"))
|
| + http_response->set_content_type("image/gif");
|
| + else if (ext == FILE_PATH_LITERAL(".jpeg") ||
|
| + ext == FILE_PATH_LITERAL(".jpg"))
|
| + http_response->set_content_type("image/jpeg");
|
| + else if (ext == FILE_PATH_LITERAL(".js"))
|
| + http_response->set_content_type("application/javascript");
|
| + else if (ext == FILE_PATH_LITERAL(".json"))
|
| + http_response->set_content_type("application/json");
|
| + else if (ext == FILE_PATH_LITERAL(".pdf"))
|
| + http_response->set_content_type("application/pdf");
|
| + else if (ext == FILE_PATH_LITERAL(".txt"))
|
| + http_response->set_content_type("text/plain");
|
| + else if (ext == FILE_PATH_LITERAL(".wav"))
|
| + http_response->set_content_type("audio/wav");
|
| + else if (ext == FILE_PATH_LITERAL(".xml"))
|
| + http_response->set_content_type("text/xml");
|
| + else if (ext == FILE_PATH_LITERAL(".html") ||
|
| + ext == FILE_PATH_LITERAL(".htm"))
|
| + http_response->set_content_type("text/html");
|
| +
|
| + http_response->AddCustomHeader("Accept-Ranges", "bytes");
|
| + http_response->AddCustomHeader("ETag", "'" + file_path.MaybeAsASCII() + "'");
|
| + http_response->set_content(file_contents);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleRedirectConnect(const HttpRequest& request) {
|
| + if (request.headers.find("Host") == request.headers.end() ||
|
| + request.headers.at("Host") != "www.redirect.com" ||
|
| + request.method != METHOD_CONNECT)
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_FOUND);
|
| + http_response->AddCustomHeader("Location",
|
| + "http://www.destination.com/foo.js");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleServerAuthConnect(const HttpRequest& request) {
|
| + if (request.headers.find("Host") == request.headers.end() ||
|
| + request.headers.at("Host") != "www.server-auth.com" ||
|
| + request.method != METHOD_CONNECT)
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_UNAUTHORIZED);
|
| + http_response->AddCustomHeader("WWW-Authenticate",
|
| + "Basic realm=\"WallyWorld\"");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleDefaultConnect(const HttpRequest& request) {
|
| + if (request.method != METHOD_CONNECT)
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_BAD_REQUEST);
|
| + http_response->set_content(
|
| + "Your client has issued a malformed or illegal request.");
|
| + http_response->set_content_type("text/html");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleCacheControl(std::string url,
|
| + std::string value,
|
| + const HttpRequest& request) {
|
| + if (!ShouldHandle(request, url))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + std::stringstream time;
|
| + time << std::time(0);
|
| + http_response->set_content("<html><head><title>" + time.str() +
|
| + "</title></head></html>");
|
| + http_response->set_content_type("text/html");
|
| + http_response->AddCustomHeader("Cache-Control", value);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleCacheExpires(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/cache/expires"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + std::stringstream time;
|
| + time << std::time(0);
|
| + http_response->set_content("<html><head><title>" + time.str() +
|
| + "</title></head></html>");
|
| + http_response->set_content_type("text/html");
|
| + http_response->AddCustomHeader("Expires", "Thu, 1 Jan 2099 00:00:00 GMT");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleEchoHeader(std::string url,
|
| + std::string value,
|
| + const HttpRequest& request) {
|
| + if (!ShouldHandle(request, url))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + if (request_gurl.has_query()) {
|
| + std::string header_name = request_gurl.query();
|
| + http_response->AddCustomHeader("Vary", header_name);
|
| + if (request.headers.find(header_name) != request.headers.end())
|
| + http_response->set_content(request.headers.at(header_name));
|
| + else
|
| + http_response->set_content("None");
|
| + }
|
| +
|
| + http_response->set_content_type("text/plain");
|
| + http_response->AddCustomHeader("Cache-Control", value);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleEcho(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/echo"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + if (request_gurl.has_query()) {
|
| + RequestQuery query = ParseQuery(request_gurl);
|
| + if (query.find("status") != query.end())
|
| + http_response->set_code(static_cast<HttpStatusCode>(
|
| + std::atoi(query["status"].front().c_str())));
|
| + }
|
| +
|
| + http_response->set_content_type("text/html");
|
| + if (request.method != METHOD_POST && request.method != METHOD_PUT)
|
| + http_response->set_content("Echo");
|
| + else
|
| + http_response->set_content(request.content);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleEchoTitle(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/echotitle"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content("<html><head><title>" + request.content +
|
| + "</title></head></html>");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleEchoAll(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/echoall"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| +
|
| + std::string body;
|
| + body +=
|
| + "<html><head><style>"
|
| + "pre { border: 1px solid black; margin: 5px; padding: 5px }"
|
| + "</style></head><body>"
|
| + "<div style=\"float: right\">"
|
| + "<a href=\"/echo\">back to referring page</a></div>"
|
| + "<h1>Request Body:</h1><pre>";
|
| +
|
| + if (request.has_content) {
|
| + std::vector<std::string> qs = base::SplitString(
|
| + request.content, "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
| + for (size_t i = 0; i < qs.size(); ++i) {
|
| + body += qs[i] + "\n";
|
| + }
|
| + }
|
| +
|
| + body +=
|
| + "</pre>"
|
| + "<h1>Request Headers:</h1><pre>" +
|
| + request.all_headers +
|
| + "</pre>"
|
| + "</body></html>";
|
| +
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(body);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleSetCookie(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/set-cookie"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + std::string content;
|
| + GURL request_gurl = ToGURL(request);
|
| + if (request_gurl.has_query()) {
|
| + std::vector<std::string> cookies = base::SplitString(
|
| + request_gurl.query(), "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
| + for (size_t i = 0; i < cookies.size(); ++i) {
|
| + http_response->AddCustomHeader("Set-Cookie", cookies[i]);
|
| + content += cookies[i];
|
| + }
|
| + }
|
| +
|
| + http_response->set_content(content);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleSetManyCookies(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/set-many-cookies"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + std::string content;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + size_t num = 0;
|
| + if (request_gurl.has_query()) {
|
| + num = std::atoi(request_gurl.query().c_str());
|
| + }
|
| +
|
| + for (size_t i = 0; i < num; ++i) {
|
| + http_response->AddCustomHeader("Set-Cookie", "a=");
|
| + }
|
| +
|
| + http_response->set_content(
|
| + base::StringPrintf("%" PRIuS " cookies were sent", num));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleExpectAndSetCookie(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/expect-and-set-cookie"))
|
| + return NOT_HANDLED;
|
| +
|
| + std::vector<std::string> sentCookies;
|
| + if (request.headers.find("Cookie") != request.headers.end()) {
|
| + std::vector<std::string> cookies =
|
| + base::SplitString(request.headers.at("Cookie"), ";",
|
| + base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
| + for (size_t i = 0; i < cookies.size(); ++i) {
|
| + sentCookies.push_back(cookies[i]);
|
| + }
|
| + }
|
| + bool gotExpected = true;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + RequestQuery qs = ParseQuery(request_gurl);
|
| + if (qs.find("expect") != qs.end()) {
|
| + std::vector<std::string> expected = qs.at("expect");
|
| + for (size_t i = 0; i < expected.size(); ++i) {
|
| + bool found = false;
|
| + for (size_t j = 0; j < sentCookies.size(); ++j) {
|
| + if (expected[i] == sentCookies[j])
|
| + found = true;
|
| + }
|
| + gotExpected &= found;
|
| + }
|
| + }
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + if (gotExpected) {
|
| + std::vector<std::string> setCookies = qs.at("set");
|
| + for (size_t i = 0; i < setCookies.size(); ++i) {
|
| + http_response->AddCustomHeader(
|
| + "Set-Cookie", net::UnescapeURLComponent(setCookies[i], UNESCAPE_ALL));
|
| + }
|
| + }
|
| +
|
| + std::string content;
|
| + if (qs.find("data") != qs.end()) {
|
| + std::vector<std::string> data = qs.at("data");
|
| + for (size_t i = 0; i < data.size(); ++i) {
|
| + content += data[i];
|
| + }
|
| + }
|
| +
|
| + http_response->set_content(content);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleSetHeader(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/set-header"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| +
|
| + std::string content;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + if (request_gurl.has_query()) {
|
| + RequestQuery headers = ParseQuery(request_gurl);
|
| + for (RequestQuery::iterator it = headers.begin(); it != headers.end();
|
| + ++it) {
|
| + std::string header = it->first;
|
| + std::string key = header.substr(0, header.find(": "));
|
| + std::string value = header.substr(header.find(": ") + 2);
|
| + http_response->AddCustomHeader(key, value);
|
| + content += header;
|
| + }
|
| + }
|
| +
|
| + http_response->set_content(content);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleNoContent(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/nocontent"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_NO_CONTENT);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleCloseSocket(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/close-socket"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<CustomHttpResponse> http_response(new CustomHttpResponse("", ""));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleAuthBasic(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/auth-basic"))
|
| + return NOT_HANDLED;
|
| +
|
| + std::string username, userpass, password, b64str;
|
| + std::string expectedPassword = "secret";
|
| + std::string realm = "testrealm";
|
| + bool setCookie = false;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + RequestQuery query = ParseQuery(request_gurl);
|
| +
|
| + if (query.find("set-cookie-if-challenged") != query.end())
|
| + setCookie = true;
|
| + if (query.find("password") != query.end())
|
| + expectedPassword = query.at("password").front();
|
| + if (query.find("realm") != query.end())
|
| + realm = query.at("realm").front();
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + bool authed = false;
|
| + std::string error;
|
| + std::string auth;
|
| + if (request.headers.find("Authorization") == request.headers.end()) {
|
| + error = "Missing Authorization Header";
|
| + } else {
|
| + auth = request.headers.at("Authorization");
|
| + b64str = auth.substr(std::string("Basic ").size());
|
| + base::Base64Decode(b64str, &userpass);
|
| + username = userpass.substr(0, userpass.find(":"));
|
| + password = userpass.substr(userpass.find(":") + 1);
|
| + if (password == expectedPassword)
|
| + authed = true;
|
| + else
|
| + error = "Invalid Credentials";
|
| + }
|
| +
|
| + if (!authed) {
|
| + http_response->set_code(HTTP_UNAUTHORIZED);
|
| + http_response->set_content_type("text/html");
|
| + http_response->AddCustomHeader("WWW-Authenticate",
|
| + "Basic realm=\"" + realm + "\"");
|
| + if (setCookie)
|
| + http_response->AddCustomHeader("Set-Cookie", "got_challenged=true");
|
| + http_response->set_content(base::StringPrintf(
|
| + "<html><head><title>Denied: %s</title></head>"
|
| + "<body>auth=%s<p>b64str=%s<p>username: %s<p>userpass: %s<p>"
|
| + "password: %s<p>You sent:<br>%s<p></body></html>",
|
| + error.c_str(), auth.c_str(), b64str.c_str(), username.c_str(),
|
| + userpass.c_str(), password.c_str(), request.all_headers.c_str()));
|
| + return http_response.Pass();
|
| + }
|
| +
|
| + if (request.headers.find("If-None-Match") != request.headers.end() &&
|
| + request.headers.at("If-None-Match") == "abc") {
|
| + http_response->set_code(HTTP_NOT_MODIFIED);
|
| + return http_response.Pass();
|
| + }
|
| +
|
| + base::FilePath file_path =
|
| + base::FilePath().AppendASCII(request.relative_url.substr(1));
|
| + if (file_path.FinalExtension() == FILE_PATH_LITERAL("gif")) {
|
| + base::FilePath server_root;
|
| + PathService::Get(base::DIR_SOURCE_ROOT, &server_root);
|
| + base::FilePath gif_path =
|
| + server_root.AppendASCII("chrome/test/data/google/logo.gif");
|
| + std::string gif_data;
|
| + base::ReadFileToString(gif_path, &gif_data);
|
| + http_response->set_content_type("image/gif");
|
| + http_response->set_content(gif_data);
|
| + } else {
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(
|
| + base::StringPrintf("<html><head><title>%s/%s</title></head>"
|
| + "<body>auth=%s<p>You sent:<br>%s<p></body></html>",
|
| + username.c_str(), password.c_str(), auth.c_str(),
|
| + request.all_headers.c_str()));
|
| + }
|
| +
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->AddCustomHeader("Cache-Control", "max-age=60000");
|
| + http_response->AddCustomHeader("Etag", "abc");
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleAuthDigest(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/auth-digest"))
|
| + return NOT_HANDLED;
|
| +
|
| + std::string nonce = base::MD5String(
|
| + base::StringPrintf("privatekey%s", request.relative_url.c_str()));
|
| + std::string opaque = base::MD5String("opaque");
|
| + std::string password = "secret";
|
| + std::string realm = "testrealm";
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + bool authed = false;
|
| + std::string error;
|
| + std::string auth;
|
| + std::string digestStr = "Digest";
|
| + std::string username;
|
| + if (request.headers.find("Authorization") == request.headers.end()) {
|
| + error = "no auth";
|
| + } else if (request.headers.at("Authorization").substr(0, digestStr.size()) !=
|
| + digestStr) {
|
| + error = "not digest";
|
| + } else {
|
| + auth = request.headers.at("Authorization");
|
| +
|
| + std::map<std::string, std::string> authPairs;
|
| + std::vector<std::string> authVector = base::SplitString(
|
| + auth, ", ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
| + for (size_t i = 0; i < authVector.size(); ++i) {
|
| + std::string key = authVector[i].substr(0, authVector[i].find("="));
|
| + std::string value = authVector[i].substr(authVector[i].find("=") + 1);
|
| + if (value.substr(0, 1) == "\"" && value.substr(value.size() - 1) == "\"")
|
| + value = value.substr(1, value.size() - 2);
|
| + authPairs[key] = value;
|
| + }
|
| +
|
| + if (authPairs["nonce"] != nonce) {
|
| + error = "wrong nonce";
|
| + } else if (authPairs["opaque"] != opaque) {
|
| + error = "wrong opaque";
|
| + } else {
|
| + username = authPairs["username"];
|
| +
|
| + std::string hA1 = base::MD5String(
|
| + base::StringPrintf("%s:%s:%s", authPairs["username"].c_str(),
|
| + realm.c_str(), password.c_str()));
|
| + std::string hA2 = base::MD5String(base::StringPrintf(
|
| + "%s:%s", request.method_string.c_str(), authPairs["uri"].c_str()));
|
| +
|
| + std::string response;
|
| + if (authPairs.find("qop") != authPairs.end() &&
|
| + authPairs.find("nc") != authPairs.end() &&
|
| + authPairs.find("cnonce") != authPairs.end()) {
|
| + response = base::MD5String(base::StringPrintf(
|
| + "%s:%s:%s:%s:%s:%s", hA1.c_str(), nonce.c_str(),
|
| + authPairs["nc"].c_str(), authPairs["cnonce"].c_str(),
|
| + authPairs["qop"].c_str(), hA2.c_str()));
|
| + } else {
|
| + response = base::MD5String(base::StringPrintf(
|
| + "%s:%s:%s", hA1.c_str(), nonce.c_str(), hA2.c_str()));
|
| + }
|
| +
|
| + if (authPairs["response"] == response)
|
| + authed = true;
|
| + else
|
| + error = "wrong password";
|
| + }
|
| + }
|
| +
|
| + if (!authed) {
|
| + http_response->set_code(HTTP_UNAUTHORIZED);
|
| + http_response->set_content_type("text/html");
|
| + std::string authHeader = base::StringPrintf(
|
| + "Digest realm=\"%s\", "
|
| + "domain=\"/\", qop=\"auth\", algorithm=MD5, nonce=\"%s\", "
|
| + "opaque=\"%s\"",
|
| + realm.c_str(), nonce.c_str(), opaque.c_str());
|
| + http_response->AddCustomHeader("WWW-Authenticate", authHeader);
|
| + http_response->set_content(base::StringPrintf(
|
| + "<html><head><title>Denied: %s</title></head>"
|
| + "<body>auth=%s<p>"
|
| + "You sent:<br>%s<p>We are replying:<br>%s<p></body></html>",
|
| + error.c_str(), auth.c_str(), request.all_headers.c_str(),
|
| + authHeader.c_str()));
|
| + return http_response.Pass();
|
| + }
|
| +
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(
|
| + base::StringPrintf("<html><head><title>%s/%s</title></head>"
|
| + "<body>auth=%s<p></body></html>",
|
| + username.c_str(), password.c_str(), auth.c_str()));
|
| +
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleServerRedirect(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/server-redirect"))
|
| + return NOT_HANDLED;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + std::string dest =
|
| + net::UnescapeURLComponent(request_gurl.query(), UNESCAPE_ALL);
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_MOVED_PERMANENTLY);
|
| + http_response->AddCustomHeader("Location", dest);
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(base::StringPrintf(
|
| + "<html><head></head><body>Redirecting to %s</body></html>",
|
| + dest.c_str()));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleCrossSiteRedirect(EmbeddedTestServer* server,
|
| + const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/cross-site"))
|
| + return NOT_HANDLED;
|
| +
|
| + std::string destAll = net::UnescapeURLComponent(
|
| + request.relative_url.substr(std::string("/cross-site").size() + 1),
|
| + UNESCAPE_ALL);
|
| +
|
| + std::string dest = base::StringPrintf(
|
| + "//%s:%hu/%s", destAll.substr(0, destAll.find("/")).c_str(),
|
| + server->port(), destAll.substr(destAll.find("/") + 1).c_str());
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_MOVED_PERMANENTLY);
|
| + http_response->AddCustomHeader("Location", dest);
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(base::StringPrintf(
|
| + "<html><head></head><body>Redirecting to %s</body></html>",
|
| + dest.c_str()));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleClientRedirect(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/client-redirect"))
|
| + return NOT_HANDLED;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + std::string dest =
|
| + net::UnescapeURLComponent(request_gurl.query(), UNESCAPE_ALL);
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content(base::StringPrintf(
|
| + "<html><head><meta http-equiv=\"refresh\" content=\"0;url=%s\"></head>"
|
| + "<body>Redirecting to %s</body></html>",
|
| + dest.c_str(), dest.c_str()));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +scoped_ptr<HttpResponse> HandleDefaultResponse(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/defaultresponse"))
|
| + return NOT_HANDLED;
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| + http_response->set_content("Default response given for path: " +
|
| + request.relative_url);
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +class DelayedHttpResponse : public BasicHttpResponse {
|
| + public:
|
| + DelayedHttpResponse(double delay) : delay_(delay) {}
|
| +
|
| + void SendResponse(SendCallback send, SendDoneCallback done) override {
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE, base::Bind(send, ToResponseString(), done),
|
| + base::TimeDelta::FromSecondsD(delay_));
|
| + }
|
| +
|
| + private:
|
| + double delay_;
|
| +};
|
| +
|
| +scoped_ptr<HttpResponse> HandleSlowServer(const HttpRequest& request) {
|
| + if (!ShouldHandle(request, "/slow"))
|
| + return NOT_HANDLED;
|
| +
|
| + double delay = 1.0f;
|
| +
|
| + GURL request_gurl = ToGURL(request);
|
| + if (request_gurl.has_query()) {
|
| + delay = std::atof(request_gurl.query().c_str());
|
| + }
|
| +
|
| + scoped_ptr<BasicHttpResponse> http_response(new DelayedHttpResponse(delay));
|
| + http_response->set_code(HTTP_OK);
|
| + http_response->set_content_type("text/plain");
|
| + http_response->set_content(base::StringPrintf("waited %.1f seconds", delay));
|
| + return http_response.Pass();
|
| +}
|
| +
|
| +void RegisterDefaultHandlers(EmbeddedTestServer* server) {
|
| + server->RegisterDefaultHandler(base::Bind(&HandleRedirectConnect));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleServerAuthConnect));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleDefaultConnect));
|
| +
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/nocachetime/maxage", "max-age=0"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/nocachetime", "no-cache"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/cachetime", "max-age=60"));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCacheExpires));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCacheControl,
|
| + "/cache/proxy-revalidate",
|
| + "max-age=60, proxy-revalidate"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/cache/private", "max-age=3, private"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/cache/public", "max-age=3, public"));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCacheControl,
|
| + "/cache/s-maxage",
|
| + "public, s-maxage=60, max-age=0"));
|
| + server->RegisterDefaultHandler(base::Bind(
|
| + &HandleCacheControl, "/cache/must-revalidate", "must-revalidate"));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCacheControl,
|
| + "/cache/must-revalidate/max-age",
|
| + "max-age=60, must-revalidate"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/cache/no-store", "no-store"));
|
| + server->RegisterDefaultHandler(base::Bind(
|
| + &HandleCacheControl, "/cache/no-store/max-age", "max-age=60, no-store"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleCacheControl, "/cache/no-transform", "no-transform"));
|
| +
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleEchoHeader, "/echoheader", "no-cache"));
|
| + server->RegisterDefaultHandler(
|
| + base::Bind(&HandleEchoHeader, "/echoheadercache", "max-age=60000"));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleEcho));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleEchoTitle));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleEchoAll));
|
| +
|
| + server->RegisterDefaultHandler(base::Bind(&HandleSetCookie));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleSetManyCookies));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleExpectAndSetCookie));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleSetHeader));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleNoContent));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCloseSocket));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleAuthBasic));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleAuthDigest));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleServerRedirect));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleCrossSiteRedirect, server));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleClientRedirect));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleDefaultResponse));
|
| + server->RegisterDefaultHandler(base::Bind(&HandleSlowServer));
|
| +
|
| + // TODO(svaldez): HandleDownload
|
| + // TODO(svaldez): HandleDownloadFinish
|
| + // TODO(svaldez): HandleZipFile
|
| + // TODO(svaldez): HandleRangeReset
|
| + // TODO(svaldez): HandleSSLManySmallRecords
|
| + // TODO(svaldez): HandleChunkedServer
|
| + // TODO(svaldez): HandleGetSSLSessionCache
|
| + // TODO(svaldez): HandleGetChannelID
|
| + // TODO(svaldez): HandleGetClientCert
|
| + // TODO(svaldez): HandleClientCipherList
|
| + // TODO(svaldez): HandleEchoMultipartPost
|
| +}
|
| +
|
| +void GetFilePathWithReplacements(const std::string& original_file_path,
|
| + const base::StringPairs& text_to_replace,
|
| + std::string* replacement_path) {
|
| + std::string new_file_path = original_file_path;
|
| + bool first_query_parameter = true;
|
| + const base::StringPairs::const_iterator end = text_to_replace.end();
|
| + for (base::StringPairs::const_iterator it = text_to_replace.begin();
|
| + it != end; ++it) {
|
| + const std::string& old_text = it->first;
|
| + const std::string& new_text = it->second;
|
| + std::string base64_old;
|
| + std::string base64_new;
|
| + base::Base64Encode(old_text, &base64_old);
|
| + base::Base64Encode(new_text, &base64_new);
|
| + if (first_query_parameter) {
|
| + new_file_path += "?";
|
| + first_query_parameter = false;
|
| + } else {
|
| + new_file_path += "&";
|
| + }
|
| + new_file_path += "replace_text=";
|
| + new_file_path += base64_old;
|
| + new_file_path += ":";
|
| + new_file_path += base64_new;
|
| + }
|
| +
|
| + *replacement_path = new_file_path;
|
| +}
|
| +
|
| +} // namespace test_server
|
| +} // namespace net
|
|
|