Home | History | Annotate | Download | only in net
      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 "android_webview/browser/net/android_stream_reader_url_request_job.h"
      6 
      7 #include <string>
      8 
      9 #include "android_webview/browser/input_stream.h"
     10 #include "android_webview/browser/net/input_stream_reader.h"
     11 #include "base/android/jni_android.h"
     12 #include "base/android/jni_string.h"
     13 #include "base/bind.h"
     14 #include "base/bind_helpers.h"
     15 #include "base/lazy_instance.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/message_loop/message_loop_proxy.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/task_runner.h"
     20 #include "base/threading/sequenced_worker_pool.h"
     21 #include "base/threading/thread.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "net/base/io_buffer.h"
     24 #include "net/base/mime_util.h"
     25 #include "net/base/net_errors.h"
     26 #include "net/base/net_util.h"
     27 #include "net/http/http_response_headers.h"
     28 #include "net/http/http_response_info.h"
     29 #include "net/http/http_util.h"
     30 #include "net/url_request/url_request.h"
     31 #include "net/url_request/url_request_job_manager.h"
     32 
     33 using android_webview::InputStream;
     34 using android_webview::InputStreamReader;
     35 using base::android::AttachCurrentThread;
     36 using base::PostTaskAndReplyWithResult;
     37 using content::BrowserThread;
     38 
     39 namespace {
     40 
     41 const int kHTTPOk = 200;
     42 const int kHTTPNotFound = 404;
     43 
     44 const char kHTTPOkText[] = "OK";
     45 const char kHTTPNotFoundText[] = "Not Found";
     46 
     47 } // namespace
     48 
     49 // The requests posted to the worker thread might outlive the job.  Thread-safe
     50 // ref counting is used to ensure that the InputStream and InputStreamReader
     51 // members of this class are still there when the closure is run on the worker
     52 // thread.
     53 class InputStreamReaderWrapper :
     54     public base::RefCountedThreadSafe<InputStreamReaderWrapper> {
     55  public:
     56   InputStreamReaderWrapper(
     57       scoped_ptr<InputStream> input_stream,
     58       scoped_ptr<InputStreamReader> input_stream_reader)
     59       : input_stream_(input_stream.Pass()),
     60         input_stream_reader_(input_stream_reader.Pass()) {
     61     DCHECK(input_stream_);
     62     DCHECK(input_stream_reader_);
     63   }
     64 
     65   android_webview::InputStream* input_stream() {
     66     return input_stream_.get();
     67   }
     68 
     69   int Seek(const net::HttpByteRange& byte_range) {
     70     return input_stream_reader_->Seek(byte_range);
     71   }
     72 
     73   int ReadRawData(net::IOBuffer* buffer, int buffer_size) {
     74     return input_stream_reader_->ReadRawData(buffer, buffer_size);
     75   }
     76 
     77  private:
     78   friend class base::RefCountedThreadSafe<InputStreamReaderWrapper>;
     79   ~InputStreamReaderWrapper() {}
     80 
     81   scoped_ptr<android_webview::InputStream> input_stream_;
     82   scoped_ptr<android_webview::InputStreamReader> input_stream_reader_;
     83 
     84   DISALLOW_COPY_AND_ASSIGN(InputStreamReaderWrapper);
     85 };
     86 
     87 AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob(
     88     net::URLRequest* request,
     89     net::NetworkDelegate* network_delegate,
     90     scoped_ptr<Delegate> delegate)
     91     : URLRequestJob(request, network_delegate),
     92       delegate_(delegate.Pass()),
     93       weak_factory_(this) {
     94   DCHECK(delegate_);
     95 }
     96 
     97 AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() {
     98 }
     99 
    100 namespace {
    101 
    102 typedef base::Callback<
    103     void(scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>,
    104          scoped_ptr<InputStream>)> OnInputStreamOpenedCallback;
    105 
    106 // static
    107 void OpenInputStreamOnWorkerThread(
    108     scoped_refptr<base::MessageLoopProxy> job_thread_proxy,
    109     scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> delegate,
    110     const GURL& url,
    111     OnInputStreamOpenedCallback callback) {
    112 
    113   JNIEnv* env = AttachCurrentThread();
    114   DCHECK(env);
    115 
    116   scoped_ptr<InputStream> input_stream = delegate->OpenInputStream(env, url);
    117   job_thread_proxy->PostTask(FROM_HERE,
    118                              base::Bind(callback,
    119                                         base::Passed(delegate.Pass()),
    120                                         base::Passed(input_stream.Pass())));
    121 }
    122 
    123 } // namespace
    124 
    125 void AndroidStreamReaderURLRequestJob::Start() {
    126   DCHECK(thread_checker_.CalledOnValidThread());
    127   // Start reading asynchronously so that all error reporting and data
    128   // callbacks happen as they would for network requests.
    129   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING,
    130                                   net::ERR_IO_PENDING));
    131 
    132   // This could be done in the InputStreamReader but would force more
    133   // complex synchronization in the delegate.
    134   GetWorkerThreadRunner()->PostTask(
    135       FROM_HERE,
    136       base::Bind(
    137           &OpenInputStreamOnWorkerThread,
    138           base::MessageLoop::current()->message_loop_proxy(),
    139           // This is intentional - the job could be deleted while the callback
    140           // is executing on the background thread.
    141           // The delegate will be "returned" to the job once the InputStream
    142           // open attempt is completed.
    143           base::Passed(&delegate_),
    144           request()->url(),
    145           base::Bind(&AndroidStreamReaderURLRequestJob::OnInputStreamOpened,
    146                      weak_factory_.GetWeakPtr())));
    147 }
    148 
    149 void AndroidStreamReaderURLRequestJob::Kill() {
    150   DCHECK(thread_checker_.CalledOnValidThread());
    151   weak_factory_.InvalidateWeakPtrs();
    152   URLRequestJob::Kill();
    153 }
    154 
    155 scoped_ptr<InputStreamReader>
    156 AndroidStreamReaderURLRequestJob::CreateStreamReader(InputStream* stream) {
    157   return make_scoped_ptr(new InputStreamReader(stream));
    158 }
    159 
    160 void AndroidStreamReaderURLRequestJob::OnInputStreamOpened(
    161       scoped_ptr<Delegate> returned_delegate,
    162       scoped_ptr<android_webview::InputStream> input_stream) {
    163   DCHECK(thread_checker_.CalledOnValidThread());
    164   DCHECK(returned_delegate);
    165   delegate_ = returned_delegate.Pass();
    166 
    167   if (!input_stream) {
    168     bool restart_required = false;
    169     delegate_->OnInputStreamOpenFailed(request(), &restart_required);
    170     if (restart_required) {
    171       NotifyRestartRequired();
    172     } else {
    173       // Clear the IO_PENDING status set in Start().
    174       SetStatus(net::URLRequestStatus());
    175       HeadersComplete(kHTTPNotFound, kHTTPNotFoundText);
    176     }
    177     return;
    178   }
    179 
    180   scoped_ptr<InputStreamReader> input_stream_reader(
    181       CreateStreamReader(input_stream.get()));
    182   DCHECK(input_stream_reader);
    183 
    184   DCHECK(!input_stream_reader_wrapper_.get());
    185   input_stream_reader_wrapper_ = new InputStreamReaderWrapper(
    186       input_stream.Pass(), input_stream_reader.Pass());
    187 
    188   PostTaskAndReplyWithResult(
    189       GetWorkerThreadRunner(),
    190       FROM_HERE,
    191       base::Bind(&InputStreamReaderWrapper::Seek,
    192                  input_stream_reader_wrapper_,
    193                  byte_range_),
    194       base::Bind(&AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted,
    195                  weak_factory_.GetWeakPtr()));
    196 }
    197 
    198 void AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted(int result) {
    199   DCHECK(thread_checker_.CalledOnValidThread());
    200   // Clear the IO_PENDING status set in Start().
    201   SetStatus(net::URLRequestStatus());
    202   if (result >= 0) {
    203     set_expected_content_size(result);
    204     HeadersComplete(kHTTPOk, kHTTPOkText);
    205   } else {
    206     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
    207   }
    208 }
    209 
    210 void AndroidStreamReaderURLRequestJob::OnReaderReadCompleted(int result) {
    211   DCHECK(thread_checker_.CalledOnValidThread());
    212   // The URLRequest API contract requires that:
    213   // * NotifyDone be called once, to set the status code, indicate the job is
    214   //   finished (there will be no further IO),
    215   // * NotifyReadComplete be called if false is returned from ReadRawData to
    216   //   indicate that the IOBuffer will not be used by the job anymore.
    217   // There might be multiple calls to ReadRawData (and thus multiple calls to
    218   // NotifyReadComplete), which is why NotifyDone is called only on errors
    219   // (result < 0) and end of data (result == 0).
    220   if (result == 0) {
    221     NotifyDone(net::URLRequestStatus());
    222   } else if (result < 0) {
    223     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
    224   } else {
    225     // Clear the IO_PENDING status.
    226     SetStatus(net::URLRequestStatus());
    227   }
    228   NotifyReadComplete(result);
    229 }
    230 
    231 base::TaskRunner* AndroidStreamReaderURLRequestJob::GetWorkerThreadRunner() {
    232   return static_cast<base::TaskRunner*>(BrowserThread::GetBlockingPool());
    233 }
    234 
    235 bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest,
    236                                                    int dest_size,
    237                                                    int* bytes_read) {
    238   DCHECK(thread_checker_.CalledOnValidThread());
    239   if (!input_stream_reader_wrapper_.get()) {
    240     // This will happen if opening the InputStream fails in which case the
    241     // error is communicated by setting the HTTP response status header rather
    242     // than failing the request during the header fetch phase.
    243     *bytes_read = 0;
    244     return true;
    245   }
    246 
    247   PostTaskAndReplyWithResult(
    248       GetWorkerThreadRunner(),
    249       FROM_HERE,
    250       base::Bind(&InputStreamReaderWrapper::ReadRawData,
    251                  input_stream_reader_wrapper_,
    252                  make_scoped_refptr(dest),
    253                  dest_size),
    254       base::Bind(&AndroidStreamReaderURLRequestJob::OnReaderReadCompleted,
    255                  weak_factory_.GetWeakPtr()));
    256 
    257   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING,
    258                                   net::ERR_IO_PENDING));
    259   return false;
    260 }
    261 
    262 bool AndroidStreamReaderURLRequestJob::GetMimeType(
    263     std::string* mime_type) const {
    264   DCHECK(thread_checker_.CalledOnValidThread());
    265   JNIEnv* env = AttachCurrentThread();
    266   DCHECK(env);
    267 
    268   if (!input_stream_reader_wrapper_.get())
    269     return false;
    270 
    271   // Since it's possible for this call to alter the InputStream a
    272   // Seek or ReadRawData operation running in the background is not permitted.
    273   DCHECK(!request_->status().is_io_pending());
    274 
    275   return delegate_->GetMimeType(
    276       env, request(), input_stream_reader_wrapper_->input_stream(), mime_type);
    277 }
    278 
    279 bool AndroidStreamReaderURLRequestJob::GetCharset(std::string* charset) {
    280   DCHECK(thread_checker_.CalledOnValidThread());
    281   JNIEnv* env = AttachCurrentThread();
    282   DCHECK(env);
    283 
    284   if (!input_stream_reader_wrapper_.get())
    285     return false;
    286 
    287   // Since it's possible for this call to alter the InputStream a
    288   // Seek or ReadRawData operation running in the background is not permitted.
    289   DCHECK(!request_->status().is_io_pending());
    290 
    291   return delegate_->GetCharset(
    292       env, request(), input_stream_reader_wrapper_->input_stream(), charset);
    293 }
    294 
    295 void AndroidStreamReaderURLRequestJob::HeadersComplete(
    296     int status_code,
    297     const std::string& status_text) {
    298   std::string status("HTTP/1.1 ");
    299   status.append(base::IntToString(status_code));
    300   status.append(" ");
    301   status.append(status_text);
    302   // HttpResponseHeaders expects its input string to be terminated by two NULs.
    303   status.append("\0\0", 2);
    304   net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
    305 
    306   if (status_code == kHTTPOk) {
    307     if (expected_content_size() != -1) {
    308       std::string content_length_header(
    309           net::HttpRequestHeaders::kContentLength);
    310       content_length_header.append(": ");
    311       content_length_header.append(
    312           base::Int64ToString(expected_content_size()));
    313       headers->AddHeader(content_length_header);
    314     }
    315 
    316     std::string mime_type;
    317     if (GetMimeType(&mime_type) && !mime_type.empty()) {
    318       std::string content_type_header(net::HttpRequestHeaders::kContentType);
    319       content_type_header.append(": ");
    320       content_type_header.append(mime_type);
    321       headers->AddHeader(content_type_header);
    322     }
    323   }
    324 
    325   response_info_.reset(new net::HttpResponseInfo());
    326   response_info_->headers = headers;
    327 
    328   NotifyHeadersComplete();
    329 }
    330 
    331 int AndroidStreamReaderURLRequestJob::GetResponseCode() const {
    332   if (response_info_)
    333     return response_info_->headers->response_code();
    334   return URLRequestJob::GetResponseCode();
    335 }
    336 
    337 void AndroidStreamReaderURLRequestJob::GetResponseInfo(
    338     net::HttpResponseInfo* info) {
    339   if (response_info_)
    340     *info = *response_info_;
    341 }
    342 
    343 void AndroidStreamReaderURLRequestJob::SetExtraRequestHeaders(
    344     const net::HttpRequestHeaders& headers) {
    345   std::string range_header;
    346   if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
    347     // We only extract the "Range" header so that we know how many bytes in the
    348     // stream to skip and how many to read after that.
    349     std::vector<net::HttpByteRange> ranges;
    350     if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
    351       if (ranges.size() == 1) {
    352         byte_range_ = ranges[0];
    353       } else {
    354         // We don't support multiple range requests in one single URL request,
    355         // because we need to do multipart encoding here.
    356         NotifyDone(net::URLRequestStatus(
    357             net::URLRequestStatus::FAILED,
    358             net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
    359       }
    360     }
    361   }
    362 }
    363