Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / android_webview / browser / net / android_stream_reader_url_request_job.cc
blob5af452eb8039d3eed29df834332fd0da4e8ec886
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.
5 #include "android_webview/browser/net/android_stream_reader_url_request_job.h"
7 #include <string>
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"
33 using android_webview::InputStream;
34 using android_webview::InputStreamReader;
35 using base::android::AttachCurrentThread;
36 using base::PostTaskAndReplyWithResult;
37 using content::BrowserThread;
39 namespace {
41 const int kHTTPOk = 200;
42 const int kHTTPNotFound = 404;
44 const char kResponseHeaderViaShouldInterceptRequest[] =
45 "Client-Via: shouldInterceptRequest";
46 const char kHTTPOkText[] = "OK";
47 const char kHTTPNotFoundText[] = "Not Found";
49 } // namespace
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_);
67 android_webview::InputStream* input_stream() {
68 return input_stream_.get();
71 int Seek(const net::HttpByteRange& byte_range) {
72 return input_stream_reader_->Seek(byte_range);
75 int ReadRawData(net::IOBuffer* buffer, int buffer_size) {
76 return input_stream_reader_->ReadRawData(buffer, buffer_size);
79 private:
80 friend class base::RefCountedThreadSafe<InputStreamReaderWrapper>;
81 ~InputStreamReaderWrapper() {}
83 scoped_ptr<android_webview::InputStream> input_stream_;
84 scoped_ptr<android_webview::InputStreamReader> input_stream_reader_;
86 DISALLOW_COPY_AND_ASSIGN(InputStreamReaderWrapper);
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_);
99 AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() {
102 namespace {
104 typedef base::Callback<
105 void(scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>,
106 scoped_ptr<InputStream>)> OnInputStreamOpenedCallback;
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) {
115 JNIEnv* env = AttachCurrentThread();
116 DCHECK(env);
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())));
125 } // namespace
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));
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())));
151 void AndroidStreamReaderURLRequestJob::Kill() {
152 DCHECK(thread_checker_.CalledOnValidThread());
153 weak_factory_.InvalidateWeakPtrs();
154 URLRequestJob::Kill();
157 scoped_ptr<InputStreamReader>
158 AndroidStreamReaderURLRequestJob::CreateStreamReader(InputStream* stream) {
159 return make_scoped_ptr(new InputStreamReader(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();
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);
179 return;
182 scoped_ptr<InputStreamReader> input_stream_reader(
183 CreateStreamReader(input_stream.get()));
184 DCHECK(input_stream_reader);
186 DCHECK(!input_stream_reader_wrapper_.get());
187 input_stream_reader_wrapper_ = new InputStreamReaderWrapper(
188 input_stream.Pass(), input_stream_reader.Pass());
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()));
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));
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());
230 NotifyReadComplete(result);
233 base::TaskRunner* AndroidStreamReaderURLRequestJob::GetWorkerThreadRunner() {
234 return static_cast<base::TaskRunner*>(BrowserThread::GetBlockingPool());
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;
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()));
259 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING,
260 net::ERR_IO_PENDING));
261 return false;
264 bool AndroidStreamReaderURLRequestJob::GetMimeType(
265 std::string* mime_type) const {
266 DCHECK(thread_checker_.CalledOnValidThread());
267 JNIEnv* env = AttachCurrentThread();
268 DCHECK(env);
270 if (!input_stream_reader_wrapper_.get())
271 return false;
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());
277 return delegate_->GetMimeType(
278 env, request(), input_stream_reader_wrapper_->input_stream(), mime_type);
281 bool AndroidStreamReaderURLRequestJob::GetCharset(std::string* charset) {
282 DCHECK(thread_checker_.CalledOnValidThread());
283 JNIEnv* env = AttachCurrentThread();
284 DCHECK(env);
286 if (!input_stream_reader_wrapper_.get())
287 return false;
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());
293 return delegate_->GetCharset(
294 env, request(), input_stream_reader_wrapper_->input_stream(), charset);
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);
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);
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);
327 JNIEnv* env = AttachCurrentThread();
328 DCHECK(env);
329 delegate_->AppendResponseHeaders(env, headers);
331 // Indicate that the response had been obtained via shouldInterceptRequest.
332 headers->AddHeader(kResponseHeaderViaShouldInterceptRequest);
334 response_info_.reset(new net::HttpResponseInfo());
335 response_info_->headers = headers;
337 NotifyHeadersComplete();
340 int AndroidStreamReaderURLRequestJob::GetResponseCode() const {
341 if (response_info_)
342 return response_info_->headers->response_code();
343 return URLRequestJob::GetResponseCode();
346 void AndroidStreamReaderURLRequestJob::GetResponseInfo(
347 net::HttpResponseInfo* info) {
348 if (response_info_)
349 *info = *response_info_;
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));