• 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 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:
InputStreamReaderWrapper(scoped_ptr<InputStream> input_stream,scoped_ptr<InputStreamReader> input_stream_reader)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 
input_stream()65   android_webview::InputStream* input_stream() {
66     return input_stream_.get();
67   }
68 
Seek(const net::HttpByteRange & byte_range)69   int Seek(const net::HttpByteRange& byte_range) {
70     return input_stream_reader_->Seek(byte_range);
71   }
72 
ReadRawData(net::IOBuffer * buffer,int buffer_size)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>;
~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 
AndroidStreamReaderURLRequestJob(net::URLRequest * request,net::NetworkDelegate * network_delegate,scoped_ptr<Delegate> delegate)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 
~AndroidStreamReaderURLRequestJob()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
OpenInputStreamOnWorkerThread(scoped_refptr<base::MessageLoopProxy> job_thread_proxy,scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> delegate,const GURL & url,OnInputStreamOpenedCallback callback)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 
Start()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 
Kill()149 void AndroidStreamReaderURLRequestJob::Kill() {
150   DCHECK(thread_checker_.CalledOnValidThread());
151   weak_factory_.InvalidateWeakPtrs();
152   URLRequestJob::Kill();
153 }
154 
155 scoped_ptr<InputStreamReader>
CreateStreamReader(InputStream * stream)156 AndroidStreamReaderURLRequestJob::CreateStreamReader(InputStream* stream) {
157   return make_scoped_ptr(new InputStreamReader(stream));
158 }
159 
OnInputStreamOpened(scoped_ptr<Delegate> returned_delegate,scoped_ptr<android_webview::InputStream> input_stream)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 
OnReaderSeekCompleted(int result)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 
OnReaderReadCompleted(int result)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 
GetWorkerThreadRunner()231 base::TaskRunner* AndroidStreamReaderURLRequestJob::GetWorkerThreadRunner() {
232   return static_cast<base::TaskRunner*>(BrowserThread::GetBlockingPool());
233 }
234 
ReadRawData(net::IOBuffer * dest,int dest_size,int * bytes_read)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 
GetMimeType(std::string * mime_type) const262 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 
GetCharset(std::string * charset)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 
HeadersComplete(int status_code,const std::string & status_text)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 
GetResponseCode() const331 int AndroidStreamReaderURLRequestJob::GetResponseCode() const {
332   if (response_info_)
333     return response_info_->headers->response_code();
334   return URLRequestJob::GetResponseCode();
335 }
336 
GetResponseInfo(net::HttpResponseInfo * info)337 void AndroidStreamReaderURLRequestJob::GetResponseInfo(
338     net::HttpResponseInfo* info) {
339   if (response_info_)
340     *info = *response_info_;
341 }
342 
SetExtraRequestHeaders(const net::HttpRequestHeaders & headers)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