• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
InputStreamReaderWrapper(scoped_ptr<InputStream> input_stream,scoped_ptr<InputStreamReader> input_stream_reader)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 
input_stream()67   android_webview::InputStream* input_stream() {
68     return input_stream_.get();
69   }
70 
Seek(const net::HttpByteRange & byte_range)71   int Seek(const net::HttpByteRange& byte_range) {
72     return input_stream_reader_->Seek(byte_range);
73   }
74 
ReadRawData(net::IOBuffer * buffer,int buffer_size)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>;
~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 
AndroidStreamReaderURLRequestJob(net::URLRequest * request,net::NetworkDelegate * network_delegate,scoped_ptr<Delegate> delegate)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 
~AndroidStreamReaderURLRequestJob()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
OpenInputStreamOnWorkerThread(scoped_refptr<base::MessageLoopProxy> job_thread_proxy,scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> delegate,const GURL & url,OnInputStreamOpenedCallback callback)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 
Start()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 
Kill()151 void AndroidStreamReaderURLRequestJob::Kill() {
152   DCHECK(thread_checker_.CalledOnValidThread());
153   weak_factory_.InvalidateWeakPtrs();
154   URLRequestJob::Kill();
155 }
156 
157 scoped_ptr<InputStreamReader>
CreateStreamReader(InputStream * stream)158 AndroidStreamReaderURLRequestJob::CreateStreamReader(InputStream* stream) {
159   return make_scoped_ptr(new InputStreamReader(stream));
160 }
161 
OnInputStreamOpened(scoped_ptr<Delegate> returned_delegate,scoped_ptr<android_webview::InputStream> input_stream)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 
OnReaderSeekCompleted(int result)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 
OnReaderReadCompleted(int result)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 
GetWorkerThreadRunner()233 base::TaskRunner* AndroidStreamReaderURLRequestJob::GetWorkerThreadRunner() {
234   return static_cast<base::TaskRunner*>(BrowserThread::GetBlockingPool());
235 }
236 
ReadRawData(net::IOBuffer * dest,int dest_size,int * bytes_read)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 
GetMimeType(std::string * mime_type) const264 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 
GetCharset(std::string * charset)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 
HeadersComplete(int status_code,const std::string & status_text)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 
GetResponseCode() const340 int AndroidStreamReaderURLRequestJob::GetResponseCode() const {
341   if (response_info_)
342     return response_info_->headers->response_code();
343   return URLRequestJob::GetResponseCode();
344 }
345 
GetResponseInfo(net::HttpResponseInfo * info)346 void AndroidStreamReaderURLRequestJob::GetResponseInfo(
347     net::HttpResponseInfo* info) {
348   if (response_info_)
349     *info = *response_info_;
350 }
351 
SetExtraRequestHeaders(const net::HttpRequestHeaders & headers)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