1 // Copyright (c) 2012 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 "content/test/net/url_request_mock_http_job.h" 6 7 #include "base/file_util.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/strings/string_util.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/threading/thread_restrictions.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "content/public/common/url_constants.h" 15 #include "net/base/net_util.h" 16 #include "net/http/http_response_headers.h" 17 #include "net/url_request/url_request_filter.h" 18 19 const char kMockHostname[] = "mock.http"; 20 const base::FilePath::CharType kMockHeaderFileSuffix[] = 21 FILE_PATH_LITERAL(".mock-http-headers"); 22 23 namespace content { 24 25 namespace { 26 27 class ProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler { 28 public: 29 // When |map_all_requests_to_base_path| is true, all request should return the 30 // contents of the file at |base_path|. When |map_all_requests_to_base_path| 31 // is false, |base_path| is the file path leading to the root of the directory 32 // to use as the root of the HTTP server. 33 explicit ProtocolHandler(const base::FilePath& base_path, 34 bool map_all_requests_to_base_path) 35 : base_path_(base_path), 36 map_all_requests_to_base_path_(map_all_requests_to_base_path) {} 37 virtual ~ProtocolHandler() {} 38 39 // net::URLRequestJobFactory::ProtocolHandler implementation 40 virtual net::URLRequestJob* MaybeCreateJob( 41 net::URLRequest* request, 42 net::NetworkDelegate* network_delegate) const OVERRIDE { 43 return new URLRequestMockHTTPJob(request, network_delegate, 44 map_all_requests_to_base_path_ ? base_path_ : GetOnDiskPath(request)); 45 } 46 47 private: 48 base::FilePath GetOnDiskPath(net::URLRequest* request) const { 49 // Conceptually we just want to "return base_path_ + request->url().path()". 50 // But path in the request URL is in URL space (i.e. %-encoded spaces). 51 // So first we convert base FilePath to a URL, then append the URL 52 // path to that, and convert the final URL back to a FilePath. 53 GURL file_url(net::FilePathToFileURL(base_path_)); 54 std::string url = file_url.spec() + request->url().path(); 55 base::FilePath file_path; 56 net::FileURLToFilePath(GURL(url), &file_path); 57 return file_path; 58 } 59 60 const base::FilePath base_path_; 61 const bool map_all_requests_to_base_path_; 62 63 DISALLOW_COPY_AND_ASSIGN(ProtocolHandler); 64 }; 65 66 } // namespace 67 68 // static 69 void URLRequestMockHTTPJob::AddUrlHandler(const base::FilePath& base_path) { 70 // Add kMockHostname to net::URLRequestFilter. 71 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); 72 filter->AddHostnameProtocolHandler("http", kMockHostname, 73 CreateProtocolHandler(base_path)); 74 } 75 76 // static 77 void URLRequestMockHTTPJob::AddHostnameToFileHandler( 78 const std::string& hostname, 79 const base::FilePath& file) { 80 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); 81 filter->AddHostnameProtocolHandler( 82 "http", hostname, CreateProtocolHandlerForSingleFile(file)); 83 } 84 85 // static 86 GURL URLRequestMockHTTPJob::GetMockUrl(const base::FilePath& path) { 87 std::string url = "http://"; 88 url.append(kMockHostname); 89 url.append("/"); 90 std::string path_str = path.MaybeAsASCII(); 91 DCHECK(!path_str.empty()); // We only expect ASCII paths in tests. 92 url.append(path_str); 93 return GURL(url); 94 } 95 96 // static 97 GURL URLRequestMockHTTPJob::GetMockViewSourceUrl(const base::FilePath& path) { 98 std::string url = kViewSourceScheme; 99 url.append(":"); 100 url.append(GetMockUrl(path).spec()); 101 return GURL(url); 102 } 103 104 // static 105 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> 106 URLRequestMockHTTPJob::CreateProtocolHandler(const base::FilePath& base_path) { 107 return scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>( 108 new ProtocolHandler(base_path, false)); 109 } 110 111 // static 112 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> 113 URLRequestMockHTTPJob::CreateProtocolHandlerForSingleFile( 114 const base::FilePath& file) { 115 return scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>( 116 new ProtocolHandler(file, true)); 117 } 118 119 URLRequestMockHTTPJob::URLRequestMockHTTPJob( 120 net::URLRequest* request, net::NetworkDelegate* network_delegate, 121 const base::FilePath& file_path) 122 : net::URLRequestFileJob( 123 request, network_delegate, file_path, 124 content::BrowserThread::GetBlockingPool()-> 125 GetTaskRunnerWithShutdownBehavior( 126 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)) {} 127 128 URLRequestMockHTTPJob::~URLRequestMockHTTPJob() { } 129 130 // Public virtual version. 131 void URLRequestMockHTTPJob::GetResponseInfo(net::HttpResponseInfo* info) { 132 // Forward to private const version. 133 GetResponseInfoConst(info); 134 } 135 136 bool URLRequestMockHTTPJob::IsRedirectResponse(GURL* location, 137 int* http_status_code) { 138 // Override the net::URLRequestFileJob implementation to invoke the default 139 // one based on HttpResponseInfo. 140 return net::URLRequestJob::IsRedirectResponse(location, http_status_code); 141 } 142 143 // Private const version. 144 void URLRequestMockHTTPJob::GetResponseInfoConst( 145 net::HttpResponseInfo* info) const { 146 // We have to load our headers from disk, but we only use this class 147 // from tests, so allow these IO operations to happen on any thread. 148 base::ThreadRestrictions::ScopedAllowIO allow_io; 149 150 base::FilePath header_file = 151 base::FilePath(file_path_.value() + kMockHeaderFileSuffix); 152 std::string raw_headers; 153 if (!base::ReadFileToString(header_file, &raw_headers)) 154 return; 155 156 // Handle CRLF line-endings. 157 ReplaceSubstringsAfterOffset(&raw_headers, 0, "\r\n", "\n"); 158 // ParseRawHeaders expects \0 to end each header line. 159 ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1)); 160 info->headers = new net::HttpResponseHeaders(raw_headers); 161 } 162 163 bool URLRequestMockHTTPJob::GetMimeType(std::string* mime_type) const { 164 net::HttpResponseInfo info; 165 GetResponseInfoConst(&info); 166 return info.headers.get() && info.headers->GetMimeType(mime_type); 167 } 168 169 int URLRequestMockHTTPJob::GetResponseCode() const { 170 net::HttpResponseInfo info; 171 GetResponseInfoConst(&info); 172 // If we have headers, get the response code from them. 173 if (info.headers.get()) 174 return info.headers->response_code(); 175 return net::URLRequestJob::GetResponseCode(); 176 } 177 178 bool URLRequestMockHTTPJob::GetCharset(std::string* charset) { 179 net::HttpResponseInfo info; 180 GetResponseInfo(&info); 181 return info.headers.get() && info.headers->GetCharset(charset); 182 } 183 184 } // namespace content 185