Home | History | Annotate | Download | only in url_request
      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 "net/test/url_request/url_request_mock_http_job.h"
      6 
      7 #include "base/files/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/task_runner_util.h"
     12 #include "base/threading/sequenced_worker_pool.h"
     13 #include "base/threading/thread_restrictions.h"
     14 #include "net/base/filename_util.h"
     15 #include "net/http/http_response_headers.h"
     16 #include "net/url_request/url_request_filter.h"
     17 #include "net/url_request/url_request_interceptor.h"
     18 
     19 const char kMockHostname[] = "mock.http";
     20 const base::FilePath::CharType kMockHeaderFileSuffix[] =
     21     FILE_PATH_LITERAL(".mock-http-headers");
     22 
     23 namespace net {
     24 
     25 namespace {
     26 
     27 class MockJobInterceptor : public net::URLRequestInterceptor {
     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   MockJobInterceptor(
     34       const base::FilePath& base_path,
     35       bool map_all_requests_to_base_path,
     36       const scoped_refptr<base::SequencedWorkerPool>& worker_pool)
     37       : base_path_(base_path),
     38         map_all_requests_to_base_path_(map_all_requests_to_base_path),
     39         worker_pool_(worker_pool) {}
     40   virtual ~MockJobInterceptor() {}
     41 
     42   // net::URLRequestJobFactory::ProtocolHandler implementation
     43   virtual net::URLRequestJob* MaybeInterceptRequest(
     44       net::URLRequest* request,
     45       net::NetworkDelegate* network_delegate) const OVERRIDE {
     46     return new URLRequestMockHTTPJob(
     47         request,
     48         network_delegate,
     49         map_all_requests_to_base_path_ ? base_path_ : GetOnDiskPath(request),
     50         worker_pool_->GetTaskRunnerWithShutdownBehavior(
     51             base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
     52   }
     53 
     54  private:
     55   base::FilePath GetOnDiskPath(net::URLRequest* request) const {
     56     // Conceptually we just want to "return base_path_ + request->url().path()".
     57     // But path in the request URL is in URL space (i.e. %-encoded spaces).
     58     // So first we convert base FilePath to a URL, then append the URL
     59     // path to that, and convert the final URL back to a FilePath.
     60     GURL file_url(net::FilePathToFileURL(base_path_));
     61     std::string url = file_url.spec() + request->url().path();
     62     base::FilePath file_path;
     63     net::FileURLToFilePath(GURL(url), &file_path);
     64     return file_path;
     65   }
     66 
     67   const base::FilePath base_path_;
     68   const bool map_all_requests_to_base_path_;
     69   const scoped_refptr<base::SequencedWorkerPool> worker_pool_;
     70 
     71   DISALLOW_COPY_AND_ASSIGN(MockJobInterceptor);
     72 };
     73 
     74 std::string DoFileIO(const base::FilePath& file_path) {
     75   base::FilePath header_file =
     76       base::FilePath(file_path.value() + kMockHeaderFileSuffix);
     77 
     78   if (!base::PathExists(header_file)) {
     79     // If there is no mock-http-headers file, fake a 200 OK.
     80     return "HTTP/1.0 200 OK\n";
     81   }
     82 
     83   std::string raw_headers;
     84   base::ReadFileToString(header_file, &raw_headers);
     85   return raw_headers;
     86 }
     87 
     88 }  // namespace
     89 
     90 // static
     91 void URLRequestMockHTTPJob::AddUrlHandler(
     92     const base::FilePath& base_path,
     93     const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
     94   // Add kMockHostname to net::URLRequestFilter.
     95   net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
     96   filter->AddHostnameInterceptor(
     97       "http", kMockHostname, CreateInterceptor(base_path, worker_pool));
     98 }
     99 
    100 // static
    101 void URLRequestMockHTTPJob::AddHostnameToFileHandler(
    102     const std::string& hostname,
    103     const base::FilePath& file,
    104     const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
    105   net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
    106   filter->AddHostnameInterceptor(
    107       "http", hostname, CreateInterceptorForSingleFile(file, worker_pool));
    108 }
    109 
    110 // static
    111 GURL URLRequestMockHTTPJob::GetMockUrl(const base::FilePath& path) {
    112   std::string url = "http://";
    113   url.append(kMockHostname);
    114   url.append("/");
    115   std::string path_str = path.MaybeAsASCII();
    116   DCHECK(!path_str.empty());  // We only expect ASCII paths in tests.
    117   url.append(path_str);
    118   return GURL(url);
    119 }
    120 
    121 // static
    122 scoped_ptr<net::URLRequestInterceptor> URLRequestMockHTTPJob::CreateInterceptor(
    123     const base::FilePath& base_path,
    124     const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
    125   return scoped_ptr<net::URLRequestInterceptor>(
    126       new MockJobInterceptor(base_path, false, worker_pool));
    127 }
    128 
    129 // static
    130 scoped_ptr<net::URLRequestInterceptor>
    131 URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
    132     const base::FilePath& file,
    133     const scoped_refptr<base::SequencedWorkerPool>& worker_pool) {
    134   return scoped_ptr<net::URLRequestInterceptor>(
    135       new MockJobInterceptor(file, true, worker_pool));
    136 }
    137 
    138 URLRequestMockHTTPJob::URLRequestMockHTTPJob(
    139     net::URLRequest* request,
    140     net::NetworkDelegate* network_delegate,
    141     const base::FilePath& file_path,
    142     const scoped_refptr<base::TaskRunner>& task_runner)
    143     : net::URLRequestFileJob(request, network_delegate, file_path, task_runner),
    144       task_runner_(task_runner),
    145       weak_ptr_factory_(this) {
    146 }
    147 
    148 URLRequestMockHTTPJob::~URLRequestMockHTTPJob() {
    149 }
    150 
    151 // Public virtual version.
    152 void URLRequestMockHTTPJob::GetResponseInfo(net::HttpResponseInfo* info) {
    153   // Forward to private const version.
    154   GetResponseInfoConst(info);
    155 }
    156 
    157 bool URLRequestMockHTTPJob::IsRedirectResponse(GURL* location,
    158                                                int* http_status_code) {
    159   // Override the net::URLRequestFileJob implementation to invoke the default
    160   // one based on HttpResponseInfo.
    161   return net::URLRequestJob::IsRedirectResponse(location, http_status_code);
    162 }
    163 
    164 // Public virtual version.
    165 void URLRequestMockHTTPJob::Start() {
    166   base::PostTaskAndReplyWithResult(
    167       task_runner_.get(),
    168       FROM_HERE,
    169       base::Bind(&DoFileIO, file_path_),
    170       base::Bind(&URLRequestMockHTTPJob::GetRawHeaders,
    171                  weak_ptr_factory_.GetWeakPtr()));
    172 }
    173 
    174 void URLRequestMockHTTPJob::GetRawHeaders(std::string raw_headers) {
    175   // Handle CRLF line-endings.
    176   ReplaceSubstringsAfterOffset(&raw_headers, 0, "\r\n", "\n");
    177   // ParseRawHeaders expects \0 to end each header line.
    178   ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1));
    179   raw_headers_ = raw_headers;
    180   URLRequestFileJob::Start();
    181 }
    182 
    183 // Private const version.
    184 void URLRequestMockHTTPJob::GetResponseInfoConst(
    185     net::HttpResponseInfo* info) const {
    186   info->headers = new net::HttpResponseHeaders(raw_headers_);
    187 }
    188 
    189 bool URLRequestMockHTTPJob::GetMimeType(std::string* mime_type) const {
    190   net::HttpResponseInfo info;
    191   GetResponseInfoConst(&info);
    192   return info.headers.get() && info.headers->GetMimeType(mime_type);
    193 }
    194 
    195 int URLRequestMockHTTPJob::GetResponseCode() const {
    196   net::HttpResponseInfo info;
    197   GetResponseInfoConst(&info);
    198   // If we have headers, get the response code from them.
    199   if (info.headers.get())
    200     return info.headers->response_code();
    201   return net::URLRequestJob::GetResponseCode();
    202 }
    203 
    204 bool URLRequestMockHTTPJob::GetCharset(std::string* charset) {
    205   net::HttpResponseInfo info;
    206   GetResponseInfo(&info);
    207   return info.headers.get() && info.headers->GetCharset(charset);
    208 }
    209 
    210 }  // namespace net
    211