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