| 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_handler_util.h" |  | 
| 6 |  | 
| 7 #include <stdlib.h> |  | 
| 8 #include <ctime> |  | 
| 9 #include <sstream> |  | 
| 10 |  | 
| 11 #include "base/base64.h" |  | 
| 12 #include "base/files/file_util.h" |  | 
| 13 #include "base/format_macros.h" |  | 
| 14 #include "base/strings/string_util.h" |  | 
| 15 #include "base/strings/stringprintf.h" |  | 
| 16 #include "base/threading/thread_restrictions.h" |  | 
| 17 #include "net/base/escape.h" |  | 
| 18 #include "net/base/url_util.h" |  | 
| 19 #include "net/http/http_byte_range.h" |  | 
| 20 #include "net/http/http_util.h" |  | 
| 21 #include "net/test/embedded_test_server/http_request.h" |  | 
| 22 #include "url/gurl.h" |  | 
| 23 |  | 
| 24 namespace net { |  | 
| 25 namespace test_server { |  | 
| 26 namespace { |  | 
| 27 |  | 
| 28 const UnescapeRule::Type kUnescapeAll = |  | 
| 29     UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS | |  | 
| 30     UnescapeRule::SPOOFING_AND_CONTROL_CHARS | |  | 
| 31     UnescapeRule::REPLACE_PLUS_WITH_SPACE; |  | 
| 32 |  | 
| 33 std::string GetContentType(const base::FilePath& path) { |  | 
| 34   if (path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) |  | 
| 35     return "application/x-chrome-extension"; |  | 
| 36   if (path.MatchesExtension(FILE_PATH_LITERAL(".exe"))) |  | 
| 37     return "application/octet-stream"; |  | 
| 38   if (path.MatchesExtension(FILE_PATH_LITERAL(".gif"))) |  | 
| 39     return "image/gif"; |  | 
| 40   if (path.MatchesExtension(FILE_PATH_LITERAL(".jpeg")) || |  | 
| 41       path.MatchesExtension(FILE_PATH_LITERAL(".jpg"))) { |  | 
| 42     return "image/jpeg"; |  | 
| 43   } |  | 
| 44   if (path.MatchesExtension(FILE_PATH_LITERAL(".js"))) |  | 
| 45     return "application/javascript"; |  | 
| 46   if (path.MatchesExtension(FILE_PATH_LITERAL(".json"))) |  | 
| 47     return "application/json"; |  | 
| 48   if (path.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) |  | 
| 49     return "application/pdf"; |  | 
| 50   if (path.MatchesExtension(FILE_PATH_LITERAL(".txt"))) |  | 
| 51     return "text/plain"; |  | 
| 52   if (path.MatchesExtension(FILE_PATH_LITERAL(".wav"))) |  | 
| 53     return "audio/wav"; |  | 
| 54   if (path.MatchesExtension(FILE_PATH_LITERAL(".xml"))) |  | 
| 55     return "text/xml"; |  | 
| 56   if (path.MatchesExtension(FILE_PATH_LITERAL(".html")) || |  | 
| 57       path.MatchesExtension(FILE_PATH_LITERAL(".htm"))) { |  | 
| 58     return "text/html"; |  | 
| 59   } |  | 
| 60   return ""; |  | 
| 61 } |  | 
| 62 |  | 
| 63 }  // namespace |  | 
| 64 |  | 
| 65 bool ShouldHandle(const HttpRequest& request, const std::string& path_prefix) { |  | 
| 66   GURL url = request.GetURL(); |  | 
| 67   return url.path() == path_prefix || |  | 
| 68          base::StartsWith(url.path(), path_prefix + "/", |  | 
| 69                           base::CompareCase::SENSITIVE); |  | 
| 70 } |  | 
| 71 |  | 
| 72 scoped_ptr<HttpResponse> HandlePrefixedRequest( |  | 
| 73     const std::string& prefix, |  | 
| 74     const EmbeddedTestServer::HandleRequestCallback& handler, |  | 
| 75     const HttpRequest& request) { |  | 
| 76   if (ShouldHandle(request, prefix)) |  | 
| 77     return handler.Run(request); |  | 
| 78   return nullptr; |  | 
| 79 } |  | 
| 80 |  | 
| 81 RequestQuery ParseQuery(const GURL& url) { |  | 
| 82   RequestQuery queries; |  | 
| 83   for (QueryIterator it(url); !it.IsAtEnd(); it.Advance()) { |  | 
| 84     queries[net::UnescapeURLComponent(it.GetKey(), kUnescapeAll)].push_back( |  | 
| 85         it.GetUnescapedValue()); |  | 
| 86   } |  | 
| 87   return queries; |  | 
| 88 } |  | 
| 89 |  | 
| 90 void GetFilePathWithReplacements(const std::string& original_file_path, |  | 
| 91                                  const base::StringPairs& text_to_replace, |  | 
| 92                                  std::string* replacement_path) { |  | 
| 93   std::string new_file_path = original_file_path; |  | 
| 94   for (const auto& replacement : text_to_replace) { |  | 
| 95     const std::string& old_text = replacement.first; |  | 
| 96     const std::string& new_text = replacement.second; |  | 
| 97     std::string base64_old; |  | 
| 98     std::string base64_new; |  | 
| 99     base::Base64Encode(old_text, &base64_old); |  | 
| 100     base::Base64Encode(new_text, &base64_new); |  | 
| 101     if (new_file_path == original_file_path) |  | 
| 102       new_file_path += "?"; |  | 
| 103     else |  | 
| 104       new_file_path += "&"; |  | 
| 105     new_file_path += "replace_text="; |  | 
| 106     new_file_path += base64_old; |  | 
| 107     new_file_path += ":"; |  | 
| 108     new_file_path += base64_new; |  | 
| 109   } |  | 
| 110 |  | 
| 111   *replacement_path = new_file_path; |  | 
| 112 } |  | 
| 113 |  | 
| 114 // Handles |request| by serving a file from under |server_root|. |  | 
| 115 scoped_ptr<HttpResponse> HandleFileRequest(const base::FilePath& server_root, |  | 
| 116                                            const HttpRequest& request) { |  | 
| 117   // This is a test-only server. Ignore I/O thread restrictions. |  | 
| 118   // TODO(svaldez): Figure out why thread is I/O restricted in the first place. |  | 
| 119   base::ThreadRestrictions::ScopedAllowIO allow_io; |  | 
| 120 |  | 
| 121   // A proxy request will have an absolute path. Simulate the proxy by stripping |  | 
| 122   // the scheme, host, and port. |  | 
| 123   GURL request_url = request.GetURL(); |  | 
| 124   std::string relative_path(request_url.path()); |  | 
| 125 |  | 
| 126   std::string post_prefix("/post/"); |  | 
| 127   if (base::StartsWith(relative_path, post_prefix, |  | 
| 128                        base::CompareCase::SENSITIVE)) { |  | 
| 129     if (request.method != METHOD_POST) |  | 
| 130       return nullptr; |  | 
| 131     relative_path = relative_path.substr(post_prefix.size() - 1); |  | 
| 132   } |  | 
| 133 |  | 
| 134   RequestQuery query = ParseQuery(request_url); |  | 
| 135 |  | 
| 136   scoped_ptr<BasicHttpResponse> failed_response(new BasicHttpResponse); |  | 
| 137   failed_response->set_code(HTTP_NOT_FOUND); |  | 
| 138 |  | 
| 139   if (query.find("expected_body") != query.end()) { |  | 
| 140     if (request.content.find(query["expected_body"].front()) == |  | 
| 141         std::string::npos) { |  | 
| 142       return failed_response.Pass(); |  | 
| 143     } |  | 
| 144   } |  | 
| 145 |  | 
| 146   if (query.find("expected_headers") != query.end()) { |  | 
| 147     for (const auto& header : query["expected_headers"]) { |  | 
| 148       if (header.find(":") == std::string::npos) |  | 
| 149         return failed_response.Pass(); |  | 
| 150       std::string key = header.substr(0, header.find(":")); |  | 
| 151       std::string value = header.substr(header.find(":") + 1); |  | 
| 152       if (request.headers.find(key) == request.headers.end() || |  | 
| 153           request.headers.at(key) != value) { |  | 
| 154         return failed_response.Pass(); |  | 
| 155       } |  | 
| 156     } |  | 
| 157   } |  | 
| 158 |  | 
| 159   // Trim the first byte ('/'). |  | 
| 160   DCHECK(base::StartsWith(relative_path, "/", base::CompareCase::SENSITIVE)); |  | 
| 161   std::string request_path = relative_path.substr(1); |  | 
| 162   base::FilePath file_path(server_root.AppendASCII(request_path)); |  | 
| 163   std::string file_contents; |  | 
| 164   if (!base::ReadFileToString(file_path, &file_contents)) { |  | 
| 165     file_path = file_path.AppendASCII("index.html"); |  | 
| 166     if (!base::ReadFileToString(file_path, &file_contents)) |  | 
| 167       return nullptr; |  | 
| 168   } |  | 
| 169 |  | 
| 170   if (request.method == METHOD_HEAD) |  | 
| 171     file_contents = ""; |  | 
| 172 |  | 
| 173   if (query.find("replace_text") != query.end()) { |  | 
| 174     for (const auto& replacement : query["replace_text"]) { |  | 
| 175       if (replacement.find(":") == std::string::npos) |  | 
| 176         return failed_response.Pass(); |  | 
| 177       std::string find; |  | 
| 178       std::string with; |  | 
| 179       base::Base64Decode(replacement.substr(0, replacement.find(":")), &find); |  | 
| 180       base::Base64Decode(replacement.substr(replacement.find(":") + 1), &with); |  | 
| 181       base::ReplaceSubstringsAfterOffset(&file_contents, 0, find, with); |  | 
| 182     } |  | 
| 183   } |  | 
| 184 |  | 
| 185   base::FilePath headers_path( |  | 
| 186       file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers"))); |  | 
| 187 |  | 
| 188   if (base::PathExists(headers_path)) { |  | 
| 189     std::string headers_contents; |  | 
| 190 |  | 
| 191     if (!base::ReadFileToString(headers_path, &headers_contents)) |  | 
| 192       return nullptr; |  | 
| 193 |  | 
| 194     return make_scoped_ptr( |  | 
| 195         new RawHttpResponse(headers_contents, file_contents)); |  | 
| 196   } |  | 
| 197 |  | 
| 198   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); |  | 
| 199   http_response->set_code(HTTP_OK); |  | 
| 200 |  | 
| 201   if (request.headers.find("Range") != request.headers.end()) { |  | 
| 202     std::vector<HttpByteRange> ranges; |  | 
| 203 |  | 
| 204     if (HttpUtil::ParseRangeHeader(request.headers.at("Range"), &ranges) && |  | 
| 205         ranges.size() == 1) { |  | 
| 206       ranges[0].ComputeBounds(file_contents.size()); |  | 
| 207       size_t start = ranges[0].first_byte_position(); |  | 
| 208       size_t end = ranges[0].last_byte_position(); |  | 
| 209 |  | 
| 210       http_response->set_code(HTTP_PARTIAL_CONTENT); |  | 
| 211       http_response->AddCustomHeader( |  | 
| 212           "Content-Range", |  | 
| 213           base::StringPrintf("bytes %" PRIuS "-%" PRIuS "/%" PRIuS, start, end, |  | 
| 214                              file_contents.size())); |  | 
| 215 |  | 
| 216       file_contents = file_contents.substr(start, end - start + 1); |  | 
| 217     } |  | 
| 218   } |  | 
| 219 |  | 
| 220   http_response->set_content_type(GetContentType(file_path)); |  | 
| 221   http_response->AddCustomHeader("Accept-Ranges", "bytes"); |  | 
| 222   http_response->AddCustomHeader("ETag", "'" + file_path.MaybeAsASCII() + "'"); |  | 
| 223   http_response->set_content(file_contents); |  | 
| 224   return http_response.Pass(); |  | 
| 225 } |  | 
| 226 |  | 
| 227 }  // namespace test_server |  | 
| 228 }  // namespace net |  | 
| OLD | NEW | 
|---|