blob: b400b14011f4b9186e4799e0ffb5a5b2dfc552b0 [file] [log] [blame]
[email protected]f90bf0d92011-01-13 02:12:441// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]97e3edc2009-09-15 22:07:152// 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/appcache/appcache_request_handler.h"
6
7#include "net/url_request/url_request.h"
8#include "net/url_request/url_request_job.h"
9#include "webkit/appcache/appcache.h"
[email protected]0a608842011-09-08 10:55:1910#include "webkit/appcache/appcache_policy.h"
[email protected]3367fc1d2009-11-09 00:09:2111#include "webkit/appcache/appcache_url_request_job.h"
[email protected]97e3edc2009-09-15 22:07:1512
13namespace appcache {
14
[email protected]4252f602011-10-21 19:18:3615AppCacheRequestHandler::AppCacheRequestHandler(
16 AppCacheHost* host, ResourceType::Type resource_type)
[email protected]15f9cded2010-05-20 20:51:0617 : host_(host), resource_type_(resource_type),
[email protected]4252f602011-10-21 19:18:3618 is_waiting_for_cache_selection_(false), found_group_id_(0),
19 found_cache_id_(0), found_network_namespace_(false),
[email protected]17d647852012-09-08 17:45:5220 cache_entry_not_found_(false), maybe_load_resource_executed_(false) {
[email protected]7e8e3dd2009-09-18 01:05:0921 DCHECK(host_);
22 host_->AddObserver(this);
[email protected]97e3edc2009-09-15 22:07:1523}
24
[email protected]7e8e3dd2009-09-18 01:05:0925AppCacheRequestHandler::~AppCacheRequestHandler() {
[email protected]3367fc1d2009-11-09 00:09:2126 if (host_) {
27 storage()->CancelDelegateCallbacks(this);
[email protected]7e8e3dd2009-09-18 01:05:0928 host_->RemoveObserver(this);
[email protected]3367fc1d2009-11-09 00:09:2129 }
30}
31
[email protected]15f9cded2010-05-20 20:51:0632AppCacheStorage* AppCacheRequestHandler::storage() const {
[email protected]3367fc1d2009-11-09 00:09:2133 DCHECK(host_);
34 return host_->service()->storage();
[email protected]97e3edc2009-09-15 22:07:1535}
36
37void AppCacheRequestHandler::GetExtraResponseInfo(
38 int64* cache_id, GURL* manifest_url) {
[email protected]3367fc1d2009-11-09 00:09:2139 if (job_ && job_->is_delivering_appcache_response()) {
40 *cache_id = job_->cache_id();
41 *manifest_url = job_->manifest_url();
42 }
[email protected]97e3edc2009-09-15 22:07:1543}
44
[email protected]3367fc1d2009-11-09 00:09:2145AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource(
[email protected]9f170462012-08-24 01:06:5846 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
[email protected]17d647852012-09-08 17:45:5247 maybe_load_resource_executed_ = true;
[email protected]1e1f7d52010-08-25 21:38:2048 if (!host_ || !IsSchemeAndMethodSupported(request) || cache_entry_not_found_)
[email protected]97e3edc2009-09-15 22:07:1549 return NULL;
[email protected]3367fc1d2009-11-09 00:09:2150
51 // This method can get called multiple times over the life
52 // of a request. The case we detect here is having scheduled
53 // delivery of a "network response" using a job setup on an
54 // earlier call thru this method. To send the request thru
55 // to the network involves restarting the request altogether,
56 // which will call thru to our interception layer again.
57 // This time thru, we return NULL so the request hits the wire.
58 if (job_) {
[email protected]dc033982010-08-24 19:15:3259 DCHECK(job_->is_delivering_network_response() ||
60 job_->cache_entry_not_found());
[email protected]1e1f7d52010-08-25 21:38:2061 if (job_->cache_entry_not_found())
62 cache_entry_not_found_ = true;
[email protected]3367fc1d2009-11-09 00:09:2163 job_ = NULL;
[email protected]e6de0cc2011-09-06 21:06:4764 storage()->CancelDelegateCallbacks(this);
[email protected]3367fc1d2009-11-09 00:09:2165 return NULL;
66 }
67
68 // Clear out our 'found' fields since we're starting a request for a
69 // new resource, any values in those fields are no longer valid.
70 found_entry_ = AppCacheEntry();
71 found_fallback_entry_ = AppCacheEntry();
72 found_cache_id_ = kNoCacheId;
[email protected]810a52ef2010-01-08 01:22:1573 found_manifest_url_ = GURL();
[email protected]3367fc1d2009-11-09 00:09:2174 found_network_namespace_ = false;
75
[email protected]15f9cded2010-05-20 20:51:0676 if (is_main_resource())
[email protected]9f170462012-08-24 01:06:5877 MaybeLoadMainResource(request, network_delegate);
[email protected]3367fc1d2009-11-09 00:09:2178 else
[email protected]9f170462012-08-24 01:06:5879 MaybeLoadSubResource(request, network_delegate);
[email protected]3367fc1d2009-11-09 00:09:2180
81 // If its been setup to deliver a network response, we can just delete
82 // it now and return NULL instead to achieve that since it couldn't
83 // have been started yet.
84 if (job_ && job_->is_delivering_network_response()) {
85 DCHECK(!job_->has_been_started());
86 job_ = NULL;
87 }
88
89 return job_;
90}
91
92AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
[email protected]9f170462012-08-24 01:06:5893 net::URLRequest* request,
94 net::NetworkDelegate* network_delegate,
95 const GURL& location) {
[email protected]1e1f7d52010-08-25 21:38:2096 if (!host_ || !IsSchemeAndMethodSupported(request) || cache_entry_not_found_)
[email protected]3367fc1d2009-11-09 00:09:2197 return NULL;
[email protected]15f9cded2010-05-20 20:51:0698 if (is_main_resource())
[email protected]3367fc1d2009-11-09 00:09:2199 return NULL;
[email protected]17d647852012-09-08 17:45:52100 // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of
101 // it once a more general solution to crbug/121325 is in place.
102 if (!maybe_load_resource_executed_)
103 return NULL;
[email protected]3367fc1d2009-11-09 00:09:21104 if (request->url().GetOrigin() == location.GetOrigin())
105 return NULL;
106
107 DCHECK(!job_); // our jobs never generate redirects
108
109 if (found_fallback_entry_.has_response_id()) {
110 // 6.9.6, step 4: If this results in a redirect to another origin,
111 // get the resource of the fallback entry.
[email protected]9f170462012-08-24 01:06:58112 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]07b8eed2010-07-23 23:40:20113 DeliverAppCachedResponse(
[email protected]4252f602011-10-21 19:18:36114 found_fallback_entry_, found_cache_id_, found_group_id_,
[email protected]2abcade2012-01-05 00:19:40115 found_manifest_url_, true, found_namespace_entry_url_);
[email protected]3367fc1d2009-11-09 00:09:21116 } else if (!found_network_namespace_) {
117 // 6.9.6, step 6: Fail the resource load.
[email protected]9f170462012-08-24 01:06:58118 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]3367fc1d2009-11-09 00:09:21119 DeliverErrorResponse();
120 } else {
121 // 6.9.6 step 3 and 5: Fetch the resource normally.
122 }
123
124 return job_;
125}
126
127AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse(
[email protected]9f170462012-08-24 01:06:58128 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
[email protected]1e1f7d52010-08-25 21:38:20129 if (!host_ || !IsSchemeAndMethodSupported(request) || cache_entry_not_found_)
[email protected]3367fc1d2009-11-09 00:09:21130 return NULL;
131 if (!found_fallback_entry_.has_response_id())
132 return NULL;
133
[email protected]2756a8e2012-09-07 18:24:29134 if (request->status().status() == net::URLRequestStatus::CANCELED) {
[email protected]3367fc1d2009-11-09 00:09:21135 // 6.9.6, step 4: But not if the user canceled the download.
136 return NULL;
137 }
138
139 // We don't fallback for responses that we delivered.
140 if (job_) {
141 DCHECK(!job_->is_delivering_network_response());
142 return NULL;
143 }
144
145 if (request->status().is_success()) {
146 int code_major = request->GetResponseCode() / 100;
147 if (code_major !=4 && code_major != 5)
148 return NULL;
[email protected]cb0d6bd52011-05-10 01:27:48149
150 // Servers can override the fallback behavior with a response header.
151 const std::string kFallbackOverrideHeader(
152 "x-chromium-appcache-fallback-override");
153 const std::string kFallbackOverrideValue(
154 "disallow-fallback");
155 std::string header_value;
156 request->GetResponseHeaderByName(kFallbackOverrideHeader, &header_value);
157 if (header_value == kFallbackOverrideValue)
158 return NULL;
[email protected]3367fc1d2009-11-09 00:09:21159 }
160
161 // 6.9.6, step 4: If this results in a 4xx or 5xx status code
162 // or there were network errors, get the resource of the fallback entry.
[email protected]9f170462012-08-24 01:06:58163 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]07b8eed2010-07-23 23:40:20164 DeliverAppCachedResponse(
[email protected]4252f602011-10-21 19:18:36165 found_fallback_entry_, found_cache_id_, found_group_id_,
[email protected]2abcade2012-01-05 00:19:40166 found_manifest_url_, true, found_namespace_entry_url_);
[email protected]3367fc1d2009-11-09 00:09:21167 return job_;
168}
169
170void AppCacheRequestHandler::OnDestructionImminent(AppCacheHost* host) {
171 storage()->CancelDelegateCallbacks(this);
172 host_ = NULL; // no need to RemoveObserver, the host is being deleted
173
174 // Since the host is being deleted, we don't have to complete any job
175 // that is current running. It's destined for the bit bucket anyway.
176 if (job_) {
177 job_->Kill();
178 job_ = NULL;
179 }
180}
181
182void AppCacheRequestHandler::DeliverAppCachedResponse(
[email protected]4252f602011-10-21 19:18:36183 const AppCacheEntry& entry, int64 cache_id, int64 group_id,
[email protected]2abcade2012-01-05 00:19:40184 const GURL& manifest_url, bool is_fallback,
185 const GURL& namespace_entry_url) {
[email protected]6ed2a5a52010-11-03 02:29:14186 DCHECK(host_ && job_ && job_->is_waiting());
[email protected]3367fc1d2009-11-09 00:09:21187 DCHECK(entry.has_response_id());
[email protected]6ed2a5a52010-11-03 02:29:14188
[email protected]2abcade2012-01-05 00:19:40189 if (ResourceType::IsFrame(resource_type_) && !namespace_entry_url.is_empty())
190 host_->NotifyMainResourceIsNamespaceEntry(namespace_entry_url);
[email protected]6ed2a5a52010-11-03 02:29:14191
[email protected]4252f602011-10-21 19:18:36192 job_->DeliverAppCachedResponse(manifest_url, group_id, cache_id,
193 entry, is_fallback);
[email protected]3367fc1d2009-11-09 00:09:21194}
195
196void AppCacheRequestHandler::DeliverErrorResponse() {
197 DCHECK(job_ && job_->is_waiting());
198 job_->DeliverErrorResponse();
199}
200
201void AppCacheRequestHandler::DeliverNetworkResponse() {
202 DCHECK(job_ && job_->is_waiting());
203 job_->DeliverNetworkResponse();
204}
205
206// Main-resource handling ----------------------------------------------
207
[email protected]9f170462012-08-24 01:06:58208void AppCacheRequestHandler::MaybeLoadMainResource(
209 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
[email protected]3367fc1d2009-11-09 00:09:21210 DCHECK(!job_);
[email protected]ea2a7b92011-04-15 20:56:47211 DCHECK(host_);
212
213 const AppCacheHost* spawning_host =
214 ResourceType::IsSharedWorker(resource_type_) ?
215 host_ : host_->GetSpawningHost();
216 GURL preferred_manifest_url = spawning_host ?
217 spawning_host->preferred_manifest_url() : GURL();
[email protected]3367fc1d2009-11-09 00:09:21218
219 // We may have to wait for our storage query to complete, but
220 // this query can also complete syncrhonously.
[email protected]9f170462012-08-24 01:06:58221 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]ea2a7b92011-04-15 20:56:47222 storage()->FindResponseForMainRequest(
223 request->url(), preferred_manifest_url, this);
[email protected]3367fc1d2009-11-09 00:09:21224}
225
226void AppCacheRequestHandler::OnMainResponseFound(
227 const GURL& url, const AppCacheEntry& entry,
[email protected]2abcade2012-01-05 00:19:40228 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
[email protected]4252f602011-10-21 19:18:36229 int64 cache_id, int64 group_id, const GURL& manifest_url) {
[email protected]e6de0cc2011-09-06 21:06:47230 DCHECK(job_);
[email protected]3367fc1d2009-11-09 00:09:21231 DCHECK(host_);
[email protected]15f9cded2010-05-20 20:51:06232 DCHECK(is_main_resource());
[email protected]3367fc1d2009-11-09 00:09:21233 DCHECK(!entry.IsForeign());
234 DCHECK(!fallback_entry.IsForeign());
235 DCHECK(!(entry.has_response_id() && fallback_entry.has_response_id()));
236
[email protected]e6de0cc2011-09-06 21:06:47237 if (!job_)
238 return;
239
[email protected]0a608842011-09-08 10:55:19240 AppCachePolicy* policy = host_->service()->appcache_policy();
241 bool was_blocked_by_policy = !manifest_url.is_empty() && policy &&
242 !policy->CanLoadAppCache(manifest_url, host_->first_party_url());
[email protected]035545f2010-04-09 13:10:21243
[email protected]0a608842011-09-08 10:55:19244 if (was_blocked_by_policy) {
245 if (ResourceType::IsFrame(resource_type_)) {
246 host_->NotifyMainResourceBlocked(manifest_url);
247 } else {
248 DCHECK(ResourceType::IsSharedWorker(resource_type_));
[email protected]5c7d5982010-07-12 09:12:59249 host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
[email protected]0a608842011-09-08 10:55:19250 }
251 DeliverNetworkResponse();
252 return;
253 }
254
255 if (ResourceType::IsFrame(resource_type_) && cache_id != kNoCacheId) {
256 // AppCacheHost loads and holds a reference to the main resource cache
257 // for two reasons, firstly to preload the cache into the working set
258 // in advance of subresource loads happening, secondly to prevent the
259 // AppCache from falling out of the working set on frame navigations.
260 host_->LoadMainResourceCache(cache_id);
261 host_->set_preferred_manifest_url(manifest_url);
[email protected]3367fc1d2009-11-09 00:09:21262 }
263
264 // 6.11.1 Navigating across documents, steps 10 and 14.
265
266 found_entry_ = entry;
[email protected]2abcade2012-01-05 00:19:40267 found_namespace_entry_url_ = namespace_entry_url;
[email protected]3367fc1d2009-11-09 00:09:21268 found_fallback_entry_ = fallback_entry;
269 found_cache_id_ = cache_id;
[email protected]4252f602011-10-21 19:18:36270 found_group_id_ = group_id;
[email protected]3367fc1d2009-11-09 00:09:21271 found_manifest_url_ = manifest_url;
272 found_network_namespace_ = false; // not applicable to main requests
273
274 if (found_entry_.has_response_id()) {
[email protected]2abcade2012-01-05 00:19:40275 DCHECK(!found_fallback_entry_.has_response_id());
[email protected]07b8eed2010-07-23 23:40:20276 DeliverAppCachedResponse(
[email protected]4252f602011-10-21 19:18:36277 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
[email protected]2abcade2012-01-05 00:19:40278 false, found_namespace_entry_url_);
[email protected]3367fc1d2009-11-09 00:09:21279 } else {
280 DeliverNetworkResponse();
281 }
282}
283
284// Sub-resource handling ----------------------------------------------
285
286void AppCacheRequestHandler::MaybeLoadSubResource(
[email protected]9f170462012-08-24 01:06:58287 net::URLRequest* request, net::NetworkDelegate* network_delegate) {
[email protected]3367fc1d2009-11-09 00:09:21288 DCHECK(!job_);
289
290 if (host_->is_selection_pending()) {
291 // We have to wait until cache selection is complete and the
292 // selected cache is loaded.
293 is_waiting_for_cache_selection_ = true;
[email protected]9f170462012-08-24 01:06:58294 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]3367fc1d2009-11-09 00:09:21295 return;
296 }
297
298 if (!host_->associated_cache() ||
299 !host_->associated_cache()->is_complete()) {
300 return;
301 }
302
[email protected]9f170462012-08-24 01:06:58303 job_ = new AppCacheURLRequestJob(request, network_delegate, storage());
[email protected]3367fc1d2009-11-09 00:09:21304 ContinueMaybeLoadSubResource();
305}
306
307void AppCacheRequestHandler::ContinueMaybeLoadSubResource() {
308 // 6.9.6 Changes to the networking model
309 // If the resource is not to be fetched using the HTTP GET mechanism or
310 // equivalent ... then fetch the resource normally.
311 DCHECK(job_);
312 DCHECK(host_->associated_cache() &&
313 host_->associated_cache()->is_complete());
314
315 const GURL& url = job_->request()->url();
316 AppCache* cache = host_->associated_cache();
317 storage()->FindResponseForSubRequest(
318 host_->associated_cache(), url,
319 &found_entry_, &found_fallback_entry_, &found_network_namespace_);
320
321 if (found_entry_.has_response_id()) {
322 // Step 2: If there's an entry, get it instead.
323 DCHECK(!found_network_namespace_ &&
324 !found_fallback_entry_.has_response_id());
325 found_cache_id_ = cache->cache_id();
[email protected]4252f602011-10-21 19:18:36326 found_group_id_ = cache->owning_group()->group_id();
[email protected]3367fc1d2009-11-09 00:09:21327 found_manifest_url_ = cache->owning_group()->manifest_url();
328 DeliverAppCachedResponse(
[email protected]4252f602011-10-21 19:18:36329 found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
[email protected]6ed2a5a52010-11-03 02:29:14330 false, GURL());
[email protected]3367fc1d2009-11-09 00:09:21331 return;
332 }
333
334 if (found_fallback_entry_.has_response_id()) {
335 // Step 4: Fetch the resource normally, if this results
336 // in certain conditions, then use the fallback.
337 DCHECK(!found_network_namespace_ &&
338 !found_entry_.has_response_id());
339 found_cache_id_ = cache->cache_id();
340 found_manifest_url_ = cache->owning_group()->manifest_url();
341 DeliverNetworkResponse();
342 return;
343 }
344
345 if (found_network_namespace_) {
346 // Step 3 and 5: Fetch the resource normally.
347 DCHECK(!found_entry_.has_response_id() &&
348 !found_fallback_entry_.has_response_id());
349 DeliverNetworkResponse();
350 return;
351 }
352
353 // Step 6: Fail the resource load.
354 DeliverErrorResponse();
[email protected]97e3edc2009-09-15 22:07:15355}
356
[email protected]7e8e3dd2009-09-18 01:05:09357void AppCacheRequestHandler::OnCacheSelectionComplete(AppCacheHost* host) {
358 DCHECK(host == host_);
[email protected]15f9cded2010-05-20 20:51:06359 if (is_main_resource())
[email protected]3367fc1d2009-11-09 00:09:21360 return;
361 if (!is_waiting_for_cache_selection_)
362 return;
[email protected]7e8e3dd2009-09-18 01:05:09363
[email protected]3367fc1d2009-11-09 00:09:21364 is_waiting_for_cache_selection_ = false;
365
366 if (!host_->associated_cache() ||
367 !host_->associated_cache()->is_complete()) {
368 DeliverNetworkResponse();
369 return;
370 }
371
372 ContinueMaybeLoadSubResource();
[email protected]7e8e3dd2009-09-18 01:05:09373}
374
[email protected]97e3edc2009-09-15 22:07:15375} // namespace appcache