- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / appcache / appcache_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 "webkit/browser/appcache/appcache_url_request_job.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/compiler_specific.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/net_log.h"
19 #include "net/http/http_request_headers.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_util.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_status.h"
24 #include "webkit/browser/appcache/appcache.h"
25 #include "webkit/browser/appcache/appcache_group.h"
26 #include "webkit/browser/appcache/appcache_histograms.h"
27 #include "webkit/browser/appcache/appcache_host.h"
28 #include "webkit/browser/appcache/appcache_service.h"
29
30 namespace appcache {
31
32 AppCacheURLRequestJob::AppCacheURLRequestJob(
33     net::URLRequest* request,
34     net::NetworkDelegate* network_delegate,
35     AppCacheStorage* storage,
36     AppCacheHost* host)
37     : net::URLRequestJob(request, network_delegate),
38       host_(host),
39       storage_(storage),
40       has_been_started_(false), has_been_killed_(false),
41       delivery_type_(AWAITING_DELIVERY_ORDERS),
42       group_id_(0), cache_id_(kNoCacheId), is_fallback_(false),
43       cache_entry_not_found_(false),
44       weak_factory_(this) {
45   DCHECK(storage_);
46 }
47
48 void AppCacheURLRequestJob::DeliverAppCachedResponse(
49     const GURL& manifest_url, int64 group_id, int64 cache_id,
50     const AppCacheEntry& entry, bool is_fallback) {
51   DCHECK(!has_delivery_orders());
52   DCHECK(entry.has_response_id());
53   delivery_type_ = APPCACHED_DELIVERY;
54   manifest_url_ = manifest_url;
55   group_id_ = group_id;
56   cache_id_ = cache_id;
57   entry_ = entry;
58   is_fallback_ = is_fallback;
59   MaybeBeginDelivery();
60 }
61
62 void AppCacheURLRequestJob::DeliverNetworkResponse() {
63   DCHECK(!has_delivery_orders());
64   delivery_type_ = NETWORK_DELIVERY;
65   storage_ = NULL;  // not needed
66   MaybeBeginDelivery();
67 }
68
69 void AppCacheURLRequestJob::DeliverErrorResponse() {
70   DCHECK(!has_delivery_orders());
71   delivery_type_ = ERROR_DELIVERY;
72   storage_ = NULL;  // not needed
73   MaybeBeginDelivery();
74 }
75
76 void AppCacheURLRequestJob::MaybeBeginDelivery() {
77   if (has_been_started() && has_delivery_orders()) {
78     // Start asynchronously so that all error reporting and data
79     // callbacks happen as they would for network requests.
80     base::MessageLoop::current()->PostTask(
81         FROM_HERE,
82         base::Bind(&AppCacheURLRequestJob::BeginDelivery,
83                    weak_factory_.GetWeakPtr()));
84   }
85 }
86
87 void AppCacheURLRequestJob::BeginDelivery() {
88   DCHECK(has_delivery_orders() && has_been_started());
89
90   if (has_been_killed())
91     return;
92
93   switch (delivery_type_) {
94     case NETWORK_DELIVERY:
95       AppCacheHistograms::AddNetworkJobStartDelaySample(
96           base::TimeTicks::Now() - start_time_tick_);
97       // To fallthru to the network, we restart the request which will
98       // cause a new job to be created to retrieve the resource from the
99       // network. Our caller is responsible for arranging to not re-intercept
100       // the same request.
101       NotifyRestartRequired();
102       break;
103
104     case ERROR_DELIVERY:
105       AppCacheHistograms::AddErrorJobStartDelaySample(
106           base::TimeTicks::Now() - start_time_tick_);
107       request()->net_log().AddEvent(
108           net::NetLog::TYPE_APPCACHE_DELIVERING_ERROR_RESPONSE);
109       NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
110                                              net::ERR_FAILED));
111       break;
112
113     case APPCACHED_DELIVERY:
114       if (entry_.IsExecutable()) {
115         BeginExecutableHandlerDelivery();
116         return;
117       }
118       AppCacheHistograms::AddAppCacheJobStartDelaySample(
119           base::TimeTicks::Now() - start_time_tick_);
120       request()->net_log().AddEvent(
121           is_fallback_ ?
122               net::NetLog::TYPE_APPCACHE_DELIVERING_FALLBACK_RESPONSE :
123               net::NetLog::TYPE_APPCACHE_DELIVERING_CACHED_RESPONSE);
124       storage_->LoadResponseInfo(
125           manifest_url_, group_id_, entry_.response_id(), this);
126       break;
127
128     default:
129       NOTREACHED();
130       break;
131   }
132 }
133
134 void AppCacheURLRequestJob::BeginExecutableHandlerDelivery() {
135   DCHECK(CommandLine::ForCurrentProcess()->
136             HasSwitch(kEnableExecutableHandlers));
137   if (!storage_->service()->handler_factory()) {
138     BeginErrorDelivery("missing handler factory");
139     return;
140   }
141
142   request()->net_log().AddEvent(
143       net::NetLog::TYPE_APPCACHE_DELIVERING_EXECUTABLE_RESPONSE);
144
145   // We defer job delivery until the executable handler is spun up and
146   // provides a response. The sequence goes like this...
147   //
148   // 1. First we load the cache.
149   // 2. Then if the handler is not spun up, we load the script resource which
150   //    is needed to spin it up.
151   // 3. Then we ask then we ask the handler to compute a response.
152   // 4. Finally we deilver that response to the caller.
153   storage_->LoadCache(cache_id_, this);
154 }
155
156 void AppCacheURLRequestJob::OnCacheLoaded(AppCache* cache, int64 cache_id) {
157   DCHECK_EQ(cache_id_, cache_id);
158   DCHECK(!has_been_killed());
159
160   if (!cache) {
161     BeginErrorDelivery("cache load failed");
162     return;
163   }
164
165   // Keep references to ensure they don't go out of scope until job completion.
166   cache_ = cache;
167   group_ = cache->owning_group();
168
169   // If the handler is spun up, ask it to compute a response.
170   AppCacheExecutableHandler* handler =
171       cache->GetExecutableHandler(entry_.response_id());
172   if (handler) {
173     InvokeExecutableHandler(handler);
174     return;
175   }
176
177   // Handler is not spun up yet, load the script resource to do that.
178   // NOTE: This is not ideal since multiple jobs may be doing this,
179   // concurrently but close enough for now, the first to load the script
180   // will win.
181
182   // Read the script data, truncating if its too large.
183   // NOTE: we just issue one read and don't bother chaining if the resource
184   // is very (very) large, close enough for now.
185   const int64 kLimit = 500 * 1000;
186   handler_source_buffer_ = new net::GrowableIOBuffer();
187   handler_source_buffer_->SetCapacity(kLimit);
188   handler_source_reader_.reset(storage_->CreateResponseReader(
189       manifest_url_, group_id_, entry_.response_id()));
190   handler_source_reader_->ReadData(
191       handler_source_buffer_.get(),
192       kLimit,
193       base::Bind(&AppCacheURLRequestJob::OnExecutableSourceLoaded,
194                  base::Unretained(this)));
195 }
196
197 void AppCacheURLRequestJob::OnExecutableSourceLoaded(int result) {
198   DCHECK(!has_been_killed());
199   handler_source_reader_.reset();
200   if (result < 0) {
201     BeginErrorDelivery("script source load failed");
202     return;
203   }
204
205   handler_source_buffer_->SetCapacity(result);  // Free up some memory.
206
207   AppCacheExecutableHandler* handler = cache_->GetOrCreateExecutableHandler(
208       entry_.response_id(), handler_source_buffer_.get());
209   handler_source_buffer_ = NULL;  // not needed anymore
210   if (handler) {
211     InvokeExecutableHandler(handler);
212     return;
213   }
214
215   BeginErrorDelivery("factory failed to produce a handler");
216 }
217
218 void AppCacheURLRequestJob::InvokeExecutableHandler(
219     AppCacheExecutableHandler* handler) {
220   handler->HandleRequest(
221       request(),
222       base::Bind(&AppCacheURLRequestJob::OnExecutableResponseCallback,
223                  weak_factory_.GetWeakPtr()));
224 }
225
226 void AppCacheURLRequestJob::OnExecutableResponseCallback(
227     const AppCacheExecutableHandler::Response& response) {
228   DCHECK(!has_been_killed());
229   if (response.use_network) {
230     delivery_type_ = NETWORK_DELIVERY;
231     storage_ = NULL;
232     BeginDelivery();
233     return;
234   }
235
236   if (!response.cached_resource_url.is_empty()) {
237     AppCacheEntry* entry_ptr = cache_->GetEntry(response.cached_resource_url);
238     if (entry_ptr && !entry_ptr->IsExecutable()) {
239       entry_ = *entry_ptr;
240       BeginDelivery();
241       return;
242     }
243   }
244
245   if (!response.redirect_url.is_empty()) {
246     // TODO(michaeln): playback a redirect
247     // response_headers_(new HttpResponseHeaders(response_headers)),
248     // fallthru for now to deliver an error
249   }
250
251   // Otherwise, return an error.
252   BeginErrorDelivery("handler returned an invalid response");
253 }
254
255 void AppCacheURLRequestJob::BeginErrorDelivery(const char* message) {
256   if (host_)
257     host_->frontend()->OnLogMessage(host_->host_id(), LOG_ERROR, message);
258   delivery_type_ = ERROR_DELIVERY;
259   storage_ = NULL;
260   BeginDelivery();
261 }
262
263 AppCacheURLRequestJob::~AppCacheURLRequestJob() {
264   if (storage_)
265     storage_->CancelDelegateCallbacks(this);
266 }
267
268 void AppCacheURLRequestJob::OnResponseInfoLoaded(
269       AppCacheResponseInfo* response_info, int64 response_id) {
270   DCHECK(is_delivering_appcache_response());
271   scoped_refptr<AppCacheURLRequestJob> protect(this);
272   if (response_info) {
273     info_ = response_info;
274     reader_.reset(storage_->CreateResponseReader(
275         manifest_url_, group_id_, entry_.response_id()));
276
277     if (is_range_request())
278       SetupRangeResponse();
279
280     NotifyHeadersComplete();
281   } else {
282     if (storage_->service()->storage() == storage_) {
283       // A resource that is expected to be in the appcache is missing.
284       // See http://code.google.com/p/chromium/issues/detail?id=50657
285       // Instead of failing the request, we restart the request. The retry
286       // attempt will fallthru to the network instead of trying to load
287       // from the appcache.
288       storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
289                                                  entry_.response_id());
290     }
291     cache_entry_not_found_ = true;
292     NotifyRestartRequired();
293   }
294 }
295
296 const net::HttpResponseInfo* AppCacheURLRequestJob::http_info() const {
297   if (!info_.get())
298     return NULL;
299   if (range_response_info_)
300     return range_response_info_.get();
301   return info_->http_response_info();
302 }
303
304 void AppCacheURLRequestJob::SetupRangeResponse() {
305   DCHECK(is_range_request() && info_.get() && reader_.get() &&
306          is_delivering_appcache_response());
307   int resource_size = static_cast<int>(info_->response_data_size());
308   if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
309     range_requested_ = net::HttpByteRange();
310     return;
311   }
312
313   DCHECK(range_requested_.HasFirstBytePosition() &&
314          range_requested_.HasLastBytePosition());
315   int offset = static_cast<int>(range_requested_.first_byte_position());
316   int length = static_cast<int>(range_requested_.last_byte_position() -
317                                 range_requested_.first_byte_position() + 1);
318
319   // Tell the reader about the range to read.
320   reader_->SetReadRange(offset, length);
321
322   // Make a copy of the full response headers and fix them up
323   // for the range we'll be returning.
324   const char kLengthHeader[] = "Content-Length";
325   const char kRangeHeader[] = "Content-Range";
326   const char kPartialStatusLine[] = "HTTP/1.1 206 Partial Content";
327   range_response_info_.reset(
328       new net::HttpResponseInfo(*info_->http_response_info()));
329   net::HttpResponseHeaders* headers = range_response_info_->headers.get();
330   headers->RemoveHeader(kLengthHeader);
331   headers->RemoveHeader(kRangeHeader);
332   headers->ReplaceStatusLine(kPartialStatusLine);
333   headers->AddHeader(
334       base::StringPrintf("%s: %d", kLengthHeader, length));
335   headers->AddHeader(
336       base::StringPrintf("%s: bytes %d-%d/%d",
337                          kRangeHeader,
338                          offset,
339                          offset + length - 1,
340                          resource_size));
341 }
342
343 void AppCacheURLRequestJob::OnReadComplete(int result) {
344   DCHECK(is_delivering_appcache_response());
345   if (result == 0) {
346     NotifyDone(net::URLRequestStatus());
347   } else if (result < 0) {
348     if (storage_->service()->storage() == storage_) {
349       storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
350                                                  entry_.response_id());
351     }
352     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
353   } else {
354     SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
355   }
356   NotifyReadComplete(result);
357 }
358
359 // net::URLRequestJob overrides ------------------------------------------------
360
361 void AppCacheURLRequestJob::Start() {
362   DCHECK(!has_been_started());
363   has_been_started_ = true;
364   start_time_tick_ = base::TimeTicks::Now();
365   MaybeBeginDelivery();
366 }
367
368 void AppCacheURLRequestJob::Kill() {
369   if (!has_been_killed_) {
370     has_been_killed_ = true;
371     reader_.reset();
372     handler_source_reader_.reset();
373     if (storage_) {
374       storage_->CancelDelegateCallbacks(this);
375       storage_ = NULL;
376     }
377     host_ = NULL;
378     info_ = NULL;
379     cache_ = NULL;
380     group_ = NULL;
381     range_response_info_.reset();
382     net::URLRequestJob::Kill();
383     weak_factory_.InvalidateWeakPtrs();
384   }
385 }
386
387 net::LoadState AppCacheURLRequestJob::GetLoadState() const {
388   if (!has_been_started())
389     return net::LOAD_STATE_IDLE;
390   if (!has_delivery_orders())
391     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
392   if (delivery_type_ != APPCACHED_DELIVERY)
393     return net::LOAD_STATE_IDLE;
394   if (!info_.get())
395     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
396   if (reader_.get() && reader_->IsReadPending())
397     return net::LOAD_STATE_READING_RESPONSE;
398   return net::LOAD_STATE_IDLE;
399 }
400
401 bool AppCacheURLRequestJob::GetMimeType(std::string* mime_type) const {
402   if (!http_info())
403     return false;
404   return http_info()->headers->GetMimeType(mime_type);
405 }
406
407 bool AppCacheURLRequestJob::GetCharset(std::string* charset) {
408   if (!http_info())
409     return false;
410   return http_info()->headers->GetCharset(charset);
411 }
412
413 void AppCacheURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
414   if (!http_info())
415     return;
416   *info = *http_info();
417 }
418
419 int AppCacheURLRequestJob::GetResponseCode() const {
420   if (!http_info())
421     return -1;
422   return http_info()->headers->response_code();
423 }
424
425 bool AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size,
426                                         int *bytes_read) {
427   DCHECK(is_delivering_appcache_response());
428   DCHECK_NE(buf_size, 0);
429   DCHECK(bytes_read);
430   DCHECK(!reader_->IsReadPending());
431   reader_->ReadData(
432       buf, buf_size, base::Bind(&AppCacheURLRequestJob::OnReadComplete,
433                                 base::Unretained(this)));
434   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
435   return false;
436 }
437
438 void AppCacheURLRequestJob::SetExtraRequestHeaders(
439     const net::HttpRequestHeaders& headers) {
440   std::string value;
441   std::vector<net::HttpByteRange> ranges;
442   if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
443       !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
444     return;
445   }
446
447   // If multiple ranges are requested, we play dumb and
448   // return the entire response with 200 OK.
449   if (ranges.size() == 1U)
450     range_requested_ = ranges[0];
451 }
452
453 }  // namespace appcache