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