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 "base/memory/scoped_vector.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "mojo/common/common_type_converters.h"
     10 #include "mojo/services/network/network_context.h"
     11 #include "net/base/io_buffer.h"
     12 #include "net/base/load_flags.h"
     13 #include "net/base/upload_bytes_element_reader.h"
     14 #include "net/base/upload_data_stream.h"
     15 #include "net/http/http_response_headers.h"
     16 #include "net/url_request/redirect_info.h"
     17 #include "net/url_request/url_request_context.h"
     18 
     19 namespace mojo {
     20 namespace {
     21 
     22 const uint32_t kMaxReadSize = 64 * 1024;
     23 
     24 // Generates an URLResponsePtr from the response state of a net::URLRequest.
     25 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
     26   URLResponsePtr response(URLResponse::New());
     27   response->url = String::From(url_request->url());
     28 
     29   const net::HttpResponseHeaders* headers = url_request->response_headers();
     30   if (headers) {
     31     response->status_code = headers->response_code();
     32     response->status_line = headers->GetStatusLine();
     33 
     34     std::vector<String> header_lines;
     35     void* iter = NULL;
     36     std::string name, value;
     37     while (headers->EnumerateHeaderLines(&iter, &name, &value))
     38       header_lines.push_back(name + ": " + value);
     39     if (!header_lines.empty())
     40       response->headers.Swap(&header_lines);
     41   }
     42 
     43   std::string mime_type;
     44   url_request->GetMimeType(&mime_type);
     45   response->mime_type = mime_type;
     46 
     47   std::string charset;
     48   url_request->GetCharset(&charset);
     49   response->charset = charset;
     50 
     51   return response.Pass();
     52 }
     53 
     54 NetworkErrorPtr MakeNetworkError(int error_code) {
     55   NetworkErrorPtr error = NetworkError::New();
     56   error->code = error_code;
     57   error->description = net::ErrorToString(error_code);
     58   return error.Pass();
     59 }
     60 
     61 // Reads the request body upload data from a DataPipe.
     62 class UploadDataPipeElementReader : public net::UploadElementReader {
     63  public:
     64   UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
     65       : pipe_(pipe.Pass()), num_bytes_(0) {}
     66   virtual ~UploadDataPipeElementReader() {}
     67 
     68   // UploadElementReader overrides:
     69   virtual int Init(const net::CompletionCallback& callback) OVERRIDE {
     70     offset_ = 0;
     71     ReadDataRaw(pipe_.get(), NULL, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
     72     return net::OK;
     73   }
     74   virtual uint64 GetContentLength() const OVERRIDE {
     75     return num_bytes_;
     76   }
     77   virtual uint64 BytesRemaining() const OVERRIDE {
     78     return num_bytes_ - offset_;
     79   }
     80   virtual bool IsInMemory() const OVERRIDE {
     81     return false;
     82   }
     83   virtual int Read(net::IOBuffer* buf,
     84                    int buf_length,
     85                    const net::CompletionCallback& callback) OVERRIDE {
     86     uint32_t bytes_read =
     87         std::min(static_cast<uint32_t>(BytesRemaining()),
     88                  static_cast<uint32_t>(buf_length));
     89     if (bytes_read > 0) {
     90       ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
     91                   MOJO_READ_DATA_FLAG_NONE);
     92     }
     93 
     94     offset_ += bytes_read;
     95     return bytes_read;
     96   }
     97 
     98  private:
     99   ScopedDataPipeConsumerHandle pipe_;
    100   uint32_t num_bytes_;
    101   uint32_t offset_;
    102 
    103   DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
    104 };
    105 
    106 }  // namespace
    107 
    108 // Keeps track of a pending two-phase write on a DataPipeProducerHandle.
    109 class URLLoaderImpl::PendingWriteToDataPipe :
    110     public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
    111  public:
    112   explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
    113       : handle_(handle.Pass()),
    114         buffer_(NULL) {
    115   }
    116 
    117   MojoResult BeginWrite(uint32_t* num_bytes) {
    118     MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
    119                                           MOJO_WRITE_DATA_FLAG_NONE);
    120     if (*num_bytes > kMaxReadSize)
    121       *num_bytes = kMaxReadSize;
    122 
    123     return result;
    124   }
    125 
    126   ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
    127     EndWriteDataRaw(handle_.get(), num_bytes);
    128     buffer_ = NULL;
    129     return handle_.Pass();
    130   }
    131 
    132   char* buffer() { return static_cast<char*>(buffer_); }
    133 
    134  private:
    135   friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
    136 
    137   ~PendingWriteToDataPipe() {
    138     if (handle_.is_valid())
    139       EndWriteDataRaw(handle_.get(), 0);
    140   }
    141 
    142   ScopedDataPipeProducerHandle handle_;
    143   void* buffer_;
    144 
    145   DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
    146 };
    147 
    148 // Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
    149 // and makes its buffer available as a net::IOBuffer.
    150 class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
    151  public:
    152   DependentIOBuffer(PendingWriteToDataPipe* pending_write)
    153       : net::WrappedIOBuffer(pending_write->buffer()),
    154         pending_write_(pending_write) {
    155   }
    156  private:
    157   virtual ~DependentIOBuffer() {}
    158   scoped_refptr<PendingWriteToDataPipe> pending_write_;
    159 };
    160 
    161 URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
    162     : context_(context),
    163       response_body_buffer_size_(0),
    164       auto_follow_redirects_(true),
    165       weak_ptr_factory_(this) {
    166 }
    167 
    168 URLLoaderImpl::~URLLoaderImpl() {
    169 }
    170 
    171 void URLLoaderImpl::Start(URLRequestPtr request,
    172                           const Callback<void(URLResponsePtr)>& callback) {
    173   if (url_request_) {
    174     SendError(net::ERR_UNEXPECTED, callback);
    175     return;
    176   }
    177 
    178   if (!request) {
    179     SendError(net::ERR_INVALID_ARGUMENT, callback);
    180     return;
    181   }
    182 
    183   url_request_ = context_->url_request_context()->CreateRequest(
    184       GURL(request->url),
    185       net::DEFAULT_PRIORITY,
    186       this,
    187       NULL);
    188   url_request_->set_method(request->method);
    189   if (request->headers) {
    190     net::HttpRequestHeaders headers;
    191     for (size_t i = 0; i < request->headers.size(); ++i)
    192       headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
    193     url_request_->SetExtraRequestHeaders(headers);
    194   }
    195   if (request->body) {
    196     ScopedVector<net::UploadElementReader> element_readers;
    197     for (size_t i = 0; i < request->body.size(); ++i) {
    198       element_readers.push_back(
    199           new UploadDataPipeElementReader(request->body[i].Pass()));
    200     }
    201     url_request_->set_upload(make_scoped_ptr(
    202         new net::UploadDataStream(element_readers.Pass(), 0)));
    203   }
    204   if (request->bypass_cache)
    205     url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
    206 
    207   callback_ = callback;
    208   response_body_buffer_size_ = request->response_body_buffer_size;
    209   auto_follow_redirects_ = request->auto_follow_redirects;
    210 
    211   url_request_->Start();
    212 }
    213 
    214 void URLLoaderImpl::FollowRedirect(
    215     const Callback<void(URLResponsePtr)>& callback) {
    216   if (!url_request_) {
    217     SendError(net::ERR_UNEXPECTED, callback);
    218     return;
    219   }
    220 
    221   if (auto_follow_redirects_) {
    222     DLOG(ERROR) << "Spurious call to FollowRedirect";
    223     SendError(net::ERR_UNEXPECTED, callback);
    224     return;
    225   }
    226 
    227   // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
    228   url_request_->FollowDeferredRedirect();
    229 }
    230 
    231 void URLLoaderImpl::QueryStatus(
    232     const Callback<void(URLLoaderStatusPtr)>& callback) {
    233   URLLoaderStatusPtr status(URLLoaderStatus::New());
    234   if (url_request_) {
    235     status->is_loading = url_request_->is_pending();
    236     if (!url_request_->status().is_success())
    237       status->error = MakeNetworkError(url_request_->status().error());
    238   } else {
    239     status->is_loading = false;
    240   }
    241   // TODO(darin): Populate more status fields.
    242   callback.Run(status.Pass());
    243 }
    244 
    245 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
    246                                        const net::RedirectInfo& redirect_info,
    247                                        bool* defer_redirect) {
    248   DCHECK(url_request == url_request_.get());
    249   DCHECK(url_request->status().is_success());
    250 
    251   if (auto_follow_redirects_)
    252     return;
    253 
    254   // Send the redirect response to the client, allowing them to inspect it and
    255   // optionally follow the redirect.
    256   *defer_redirect = true;
    257 
    258   URLResponsePtr response = MakeURLResponse(url_request);
    259   response->redirect_method = redirect_info.new_method;
    260   response->redirect_url = String::From(redirect_info.new_url);
    261 
    262   SendResponse(response.Pass());
    263 }
    264 
    265 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
    266   DCHECK(url_request == url_request_.get());
    267 
    268   if (!url_request->status().is_success()) {
    269     SendError(url_request->status().error(), callback_);
    270     callback_ = Callback<void(URLResponsePtr)>();
    271     return;
    272   }
    273 
    274   // TODO(darin): Add support for optional MIME sniffing.
    275 
    276   DataPipe data_pipe;
    277   // TODO(darin): Honor given buffer size.
    278 
    279   URLResponsePtr response = MakeURLResponse(url_request);
    280   response->body = data_pipe.consumer_handle.Pass();
    281   response_body_stream_ = data_pipe.producer_handle.Pass();
    282 
    283   SendResponse(response.Pass());
    284 
    285   // Start reading...
    286   ReadMore();
    287 }
    288 
    289 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
    290                                     int bytes_read) {
    291   DCHECK(url_request == url_request_.get());
    292 
    293   if (url_request->status().is_success()) {
    294     DidRead(static_cast<uint32_t>(bytes_read), false);
    295   } else {
    296     pending_write_ = NULL;  // This closes the data pipe.
    297   }
    298 }
    299 
    300 void URLLoaderImpl::SendError(
    301     int error_code,
    302     const Callback<void(URLResponsePtr)>& callback) {
    303   URLResponsePtr response(URLResponse::New());
    304   if (url_request_)
    305     response->url = String::From(url_request_->url());
    306   response->error = MakeNetworkError(error_code);
    307   callback.Run(response.Pass());
    308 }
    309 
    310 void URLLoaderImpl::SendResponse(URLResponsePtr response) {
    311   Callback<void(URLResponsePtr)> callback;
    312   std::swap(callback_, callback);
    313   callback.Run(response.Pass());
    314 }
    315 
    316 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
    317   // TODO(darin): Handle a bad |result| value.
    318   ReadMore();
    319 }
    320 
    321 void URLLoaderImpl::WaitToReadMore() {
    322   handle_watcher_.Start(response_body_stream_.get(),
    323                         MOJO_HANDLE_SIGNAL_WRITABLE,
    324                         MOJO_DEADLINE_INDEFINITE,
    325                         base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
    326                                    weak_ptr_factory_.GetWeakPtr()));
    327 }
    328 
    329 void URLLoaderImpl::ReadMore() {
    330   DCHECK(!pending_write_.get());
    331 
    332   pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
    333 
    334   uint32_t num_bytes;
    335   MojoResult result = pending_write_->BeginWrite(&num_bytes);
    336   if (result == MOJO_RESULT_SHOULD_WAIT) {
    337     // The pipe is full. We need to wait for it to have more space.
    338     response_body_stream_ = pending_write_->Complete(num_bytes);
    339     pending_write_ = NULL;
    340     WaitToReadMore();
    341     return;
    342   }
    343   if (result != MOJO_RESULT_OK) {
    344     // The response body stream is in a bad state. Bail.
    345     // TODO(darin): How should this be communicated to our client?
    346     return;
    347   }
    348   CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
    349 
    350   scoped_refptr<net::IOBuffer> buf =
    351       new DependentIOBuffer(pending_write_.get());
    352 
    353   int bytes_read;
    354   url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read);
    355 
    356   // Drop our reference to the buffer.
    357   buf = NULL;
    358 
    359   if (url_request_->status().is_io_pending()) {
    360     // Wait for OnReadCompleted.
    361   } else if (url_request_->status().is_success() && bytes_read > 0) {
    362     DidRead(static_cast<uint32_t>(bytes_read), true);
    363   } else {
    364     pending_write_->Complete(0);
    365     pending_write_ = NULL;  // This closes the data pipe.
    366   }
    367 }
    368 
    369 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
    370   DCHECK(url_request_->status().is_success());
    371 
    372   response_body_stream_ = pending_write_->Complete(num_bytes);
    373   pending_write_ = NULL;
    374 
    375   if (completed_synchronously) {
    376     base::MessageLoop::current()->PostTask(
    377         FROM_HERE,
    378         base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
    379   } else {
    380     ReadMore();
    381   }
    382 }
    383 
    384 }  // namespace mojo
    385