Home | History | Annotate | Download | only in network
      1 // Copyright 2014 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 "mojo/services/network/url_loader_impl.h"
      6 
      7 #include "mojo/common/common_type_converters.h"
      8 #include "mojo/services/network/network_context.h"
      9 #include "net/base/io_buffer.h"
     10 #include "net/base/load_flags.h"
     11 #include "net/http/http_response_headers.h"
     12 
     13 namespace mojo {
     14 namespace {
     15 
     16 const uint32_t kMaxReadSize = 64 * 1024;
     17 
     18 // Generates an URLResponsePtr from the response state of a net::URLRequest.
     19 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
     20   URLResponsePtr response(URLResponse::New());
     21   response->url = url_request->url().spec();
     22 
     23   const net::HttpResponseHeaders* headers = url_request->response_headers();
     24   if (headers) {
     25     response->status_code = headers->response_code();
     26     response->status_line = headers->GetStatusLine();
     27 
     28     std::vector<String> header_lines;
     29     void* iter = NULL;
     30     std::string name, value;
     31     while (headers->EnumerateHeaderLines(&iter, &name, &value))
     32       header_lines.push_back(name + ": " + value);
     33     if (!header_lines.empty())
     34       response->headers.Swap(&header_lines);
     35   }
     36 
     37   return response.Pass();
     38 }
     39 
     40 }  // namespace
     41 
     42 // Keeps track of a pending two-phase write on a DataPipeProducerHandle.
     43 class URLLoaderImpl::PendingWriteToDataPipe :
     44     public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
     45  public:
     46   explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
     47       : handle_(handle.Pass()),
     48         buffer_(NULL) {
     49   }
     50 
     51   bool BeginWrite(uint32_t* num_bytes) {
     52     MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
     53                                           MOJO_WRITE_DATA_FLAG_NONE);
     54     if (*num_bytes > kMaxReadSize)
     55       *num_bytes = kMaxReadSize;
     56 
     57     return result == MOJO_RESULT_OK;
     58   }
     59 
     60   ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
     61     EndWriteDataRaw(handle_.get(), num_bytes);
     62     buffer_ = NULL;
     63     return handle_.Pass();
     64   }
     65 
     66   char* buffer() { return static_cast<char*>(buffer_); }
     67 
     68  private:
     69   friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
     70 
     71   ~PendingWriteToDataPipe() {
     72     if (handle_.is_valid())
     73       EndWriteDataRaw(handle_.get(), 0);
     74   }
     75 
     76   ScopedDataPipeProducerHandle handle_;
     77   void* buffer_;
     78 
     79   DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
     80 };
     81 
     82 // Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
     83 // and makes its buffer available as a net::IOBuffer.
     84 class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
     85  public:
     86   DependentIOBuffer(PendingWriteToDataPipe* pending_write)
     87       : net::WrappedIOBuffer(pending_write->buffer()),
     88         pending_write_(pending_write) {
     89   }
     90  private:
     91   virtual ~DependentIOBuffer() {}
     92   scoped_refptr<PendingWriteToDataPipe> pending_write_;
     93 };
     94 
     95 URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
     96     : context_(context),
     97       auto_follow_redirects_(true),
     98       weak_ptr_factory_(this) {
     99 }
    100 
    101 URLLoaderImpl::~URLLoaderImpl() {
    102 }
    103 
    104 void URLLoaderImpl::OnConnectionError() {
    105   delete this;
    106 }
    107 
    108 void URLLoaderImpl::Start(URLRequestPtr request,
    109                           ScopedDataPipeProducerHandle response_body_stream) {
    110   // Do not allow starting another request.
    111   if (url_request_) {
    112     SendError(net::ERR_UNEXPECTED);
    113     url_request_.reset();
    114     response_body_stream_.reset();
    115     return;
    116   }
    117 
    118   if (!request) {
    119     SendError(net::ERR_INVALID_ARGUMENT);
    120     return;
    121   }
    122 
    123   response_body_stream_ = response_body_stream.Pass();
    124 
    125   GURL url(request->url);
    126   url_request_.reset(
    127       new net::URLRequest(url,
    128                           net::DEFAULT_PRIORITY,
    129                           this,
    130                           context_->url_request_context()));
    131   url_request_->set_method(request->method);
    132   if (request->headers) {
    133     net::HttpRequestHeaders headers;
    134     for (size_t i = 0; i < request->headers.size(); ++i)
    135       headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
    136     url_request_->SetExtraRequestHeaders(headers);
    137   }
    138   if (request->bypass_cache)
    139     url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
    140   // TODO(darin): Handle request body.
    141 
    142   auto_follow_redirects_ = request->auto_follow_redirects;
    143 
    144   url_request_->Start();
    145 }
    146 
    147 void URLLoaderImpl::FollowRedirect() {
    148   if (auto_follow_redirects_) {
    149     DLOG(ERROR) << "Spurious call to FollowRedirect";
    150   } else {
    151     if (url_request_)
    152       url_request_->FollowDeferredRedirect();
    153   }
    154 }
    155 
    156 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
    157                                        const GURL& new_url,
    158                                        bool* defer_redirect) {
    159   DCHECK(url_request == url_request_.get());
    160   DCHECK(url_request->status().is_success());
    161 
    162   URLResponsePtr response = MakeURLResponse(url_request);
    163   std::string redirect_method =
    164       net::URLRequest::ComputeMethodForRedirect(url_request->method(),
    165                                                 response->status_code);
    166   client()->OnReceivedRedirect(
    167       response.Pass(), new_url.spec(), redirect_method);
    168 
    169   *defer_redirect = !auto_follow_redirects_;
    170 }
    171 
    172 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
    173   DCHECK(url_request == url_request_.get());
    174 
    175   if (!url_request->status().is_success()) {
    176     SendError(url_request->status().error());
    177     return;
    178   }
    179 
    180   client()->OnReceivedResponse(MakeURLResponse(url_request));
    181 
    182   // Start reading...
    183   ReadMore();
    184 }
    185 
    186 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
    187                                     int bytes_read) {
    188   if (url_request->status().is_success()) {
    189     DidRead(static_cast<uint32_t>(bytes_read), false);
    190   } else {
    191     pending_write_ = NULL;  // This closes the data pipe.
    192     // TODO(darin): Perhaps we should communicate this error to our client.
    193   }
    194 }
    195 
    196 void URLLoaderImpl::SendError(int error_code) {
    197   NetworkErrorPtr error(NetworkError::New());
    198   error->code = error_code;
    199   error->description = net::ErrorToString(error_code);
    200   client()->OnReceivedError(error.Pass());
    201 }
    202 
    203 void URLLoaderImpl::ReadMore() {
    204   DCHECK(!pending_write_);
    205 
    206   pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
    207 
    208   uint32_t num_bytes;
    209   if (!pending_write_->BeginWrite(&num_bytes))
    210     CHECK(false);  // Oops! TODO(darin): crbug/386877: The pipe might be full!
    211   if (num_bytes > static_cast<uint32_t>(std::numeric_limits<int>::max()))
    212     CHECK(false);  // Oops!
    213 
    214   scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_);
    215 
    216   int bytes_read;
    217   url_request_->Read(buf, static_cast<int>(num_bytes), &bytes_read);
    218 
    219   // Drop our reference to the buffer.
    220   buf = NULL;
    221 
    222   if (url_request_->status().is_io_pending()) {
    223     // Wait for OnReadCompleted.
    224   } else if (url_request_->status().is_success() && bytes_read > 0) {
    225     DidRead(static_cast<uint32_t>(bytes_read), true);
    226   } else {
    227     pending_write_->Complete(0);
    228     pending_write_ = NULL;  // This closes the data pipe.
    229     if (bytes_read == 0) {
    230       client()->OnReceivedEndOfResponseBody();
    231     } else {
    232       DCHECK(!url_request_->status().is_success());
    233       SendError(url_request_->status().error());
    234     }
    235   }
    236 }
    237 
    238 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
    239   DCHECK(url_request_->status().is_success());
    240 
    241   response_body_stream_ = pending_write_->Complete(num_bytes);
    242   pending_write_ = NULL;
    243 
    244   if (completed_synchronously) {
    245     base::MessageLoop::current()->PostTask(
    246         FROM_HERE,
    247         base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
    248   } else {
    249     ReadMore();
    250   }
    251 }
    252 
    253 }  // namespace mojo
    254