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