blob: 2d57aaa3e2ed5f6b7a6c278e7db40630b5cf7282 [file] [log] [blame]
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001/*
2 * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "AssociatedURLLoader.h"
33
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000034#include "WebDataSource.h"
35#include "WebFrameImpl.h"
Torne (Richard Coles)8abfc582013-09-12 12:10:38 +010036#include "core/fetch/CrossOriginAccessControl.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010037#include "core/loader/DocumentThreadableLoader.h"
38#include "core/loader/DocumentThreadableLoaderClient.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010039#include "core/xml/XMLHttpRequest.h"
Torne (Richard Coles)1e202182013-10-18 15:46:42 +010040#include "platform/Timer.h"
Torne (Richard Coles)19cde672013-11-06 12:28:04 +000041#include "platform/exported/WrappedResourceRequest.h"
42#include "platform/exported/WrappedResourceResponse.h"
Torne (Richard Coles)1e202182013-10-18 15:46:42 +010043#include "platform/network/HTTPParsers.h"
44#include "platform/network/ResourceError.h"
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010045#include "public/platform/WebHTTPHeaderVisitor.h"
46#include "public/platform/WebString.h"
47#include "public/platform/WebURLError.h"
48#include "public/platform/WebURLLoaderClient.h"
49#include "public/platform/WebURLRequest.h"
Ben Murdoch591b9582013-07-10 11:41:44 +010050#include "wtf/HashSet.h"
51#include "wtf/text/WTFString.h"
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000052
53using namespace WebCore;
54using namespace WTF;
55
Torne (Richard Coles)51b29062013-11-28 11:56:03 +000056namespace blink {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000057
58namespace {
59
60class HTTPRequestHeaderValidator : public WebHTTPHeaderVisitor {
61 WTF_MAKE_NONCOPYABLE(HTTPRequestHeaderValidator);
62public:
63 HTTPRequestHeaderValidator() : m_isSafe(true) { }
64
65 void visitHeader(const WebString& name, const WebString& value);
66 bool isSafe() const { return m_isSafe; }
67
68private:
69 bool m_isSafe;
70};
71
72typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet;
73
74void HTTPRequestHeaderValidator::visitHeader(const WebString& name, const WebString& value)
75{
76 m_isSafe = m_isSafe && isValidHTTPToken(name) && XMLHttpRequest::isAllowedHTTPHeader(name) && isValidHTTPHeaderValue(value);
77}
78
79// FIXME: Remove this and use WebCore code that does the same thing.
80class HTTPResponseHeaderValidator : public WebHTTPHeaderVisitor {
81 WTF_MAKE_NONCOPYABLE(HTTPResponseHeaderValidator);
82public:
83 HTTPResponseHeaderValidator(bool usingAccessControl) : m_usingAccessControl(usingAccessControl) { }
84
85 void visitHeader(const WebString& name, const WebString& value);
86 const HTTPHeaderSet& blockedHeaders();
87
88private:
89 HTTPHeaderSet m_exposedHeaders;
90 HTTPHeaderSet m_blockedHeaders;
91 bool m_usingAccessControl;
92};
93
94void HTTPResponseHeaderValidator::visitHeader(const WebString& name, const WebString& value)
95{
96 String headerName(name);
97 if (m_usingAccessControl) {
98 if (equalIgnoringCase(headerName, "access-control-expose-headers"))
99 parseAccessControlExposeHeadersAllowList(value, m_exposedHeaders);
100 else if (!isOnAccessControlResponseHeaderWhitelist(headerName))
101 m_blockedHeaders.add(name);
102 }
103}
104
105const HTTPHeaderSet& HTTPResponseHeaderValidator::blockedHeaders()
106{
107 // Remove exposed headers from the blocked set.
108 if (!m_exposedHeaders.isEmpty()) {
109 // Don't allow Set-Cookie headers to be exposed.
110 m_exposedHeaders.remove("set-cookie");
111 m_exposedHeaders.remove("set-cookie2");
112 // Block Access-Control-Expose-Header itself. It could be exposed later.
113 m_blockedHeaders.add("access-control-expose-headers");
114 HTTPHeaderSet::const_iterator end = m_exposedHeaders.end();
115 for (HTTPHeaderSet::const_iterator it = m_exposedHeaders.begin(); it != end; ++it)
116 m_blockedHeaders.remove(*it);
117 }
118
119 return m_blockedHeaders;
120}
121
122}
123
124// This class bridges the interface differences between WebCore and WebKit loader clients.
125// It forwards its ThreadableLoaderClient notifications to a WebURLLoaderClient.
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000126class AssociatedURLLoader::ClientAdapter FINAL : public DocumentThreadableLoaderClient {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000127 WTF_MAKE_NONCOPYABLE(ClientAdapter);
128public:
129 static PassOwnPtr<ClientAdapter> create(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&);
130
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000131 virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) OVERRIDE;
132 virtual void willSendRequest(ResourceRequest& /*newRequest*/, const ResourceResponse& /*redirectResponse*/) OVERRIDE;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000133
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000134 virtual void didReceiveResponse(unsigned long, const ResourceResponse&) OVERRIDE;
135 virtual void didDownloadData(int /*dataLength*/) OVERRIDE;
136 virtual void didReceiveData(const char*, int /*dataLength*/) OVERRIDE;
137 virtual void didReceiveCachedMetadata(const char*, int /*dataLength*/) OVERRIDE;
138 virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) OVERRIDE;
139 virtual void didFail(const ResourceError&) OVERRIDE;
140 virtual void didFailRedirectCheck() OVERRIDE;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000141
142 // Sets an error to be reported back to the client, asychronously.
143 void setDelayedError(const ResourceError&);
144
145 // Enables forwarding of error notifications to the WebURLLoaderClient. These must be
146 // deferred until after the call to AssociatedURLLoader::loadAsynchronously() completes.
147 void enableErrorNotifications();
148
149 // Stops loading and releases the DocumentThreadableLoader as early as possible.
Ben Murdoch02772c62013-07-26 10:21:05 +0100150 void clearClient() { m_client = 0; }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000151
152private:
153 ClientAdapter(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&);
154
155 void notifyError(Timer<ClientAdapter>*);
156
157 AssociatedURLLoader* m_loader;
158 WebURLLoaderClient* m_client;
159 WebURLLoaderOptions m_options;
160 WebURLError m_error;
161
162 Timer<ClientAdapter> m_errorTimer;
163 bool m_enableErrorNotifications;
164 bool m_didFail;
165};
166
167PassOwnPtr<AssociatedURLLoader::ClientAdapter> AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options)
168{
169 return adoptPtr(new ClientAdapter(loader, client, options));
170}
171
172AssociatedURLLoader::ClientAdapter::ClientAdapter(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options)
173 : m_loader(loader)
174 , m_client(client)
175 , m_options(options)
176 , m_errorTimer(this, &ClientAdapter::notifyError)
177 , m_enableErrorNotifications(false)
178 , m_didFail(false)
179{
180 ASSERT(m_loader);
181 ASSERT(m_client);
182}
183
184void AssociatedURLLoader::ClientAdapter::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
185{
186 if (!m_client)
187 return;
188
189 WrappedResourceRequest wrappedNewRequest(newRequest);
190 WrappedResourceResponse wrappedRedirectResponse(redirectResponse);
191 m_client->willSendRequest(m_loader, wrappedNewRequest, wrappedRedirectResponse);
192}
193
194void AssociatedURLLoader::ClientAdapter::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
195{
196 if (!m_client)
197 return;
198
199 m_client->didSendData(m_loader, bytesSent, totalBytesToBeSent);
200}
201
202void AssociatedURLLoader::ClientAdapter::didReceiveResponse(unsigned long, const ResourceResponse& response)
203{
Ben Murdoch07a852d2014-03-31 11:51:52 +0100204 if (!m_client)
205 return;
206
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000207 // Try to use the original ResourceResponse if possible.
208 WebURLResponse validatedResponse = WrappedResourceResponse(response);
209 HTTPResponseHeaderValidator validator(m_options.crossOriginRequestPolicy == WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl);
210 if (!m_options.exposeAllResponseHeaders)
211 validatedResponse.visitHTTPHeaderFields(&validator);
212
213 // If there are blocked headers, copy the response so we can remove them.
214 const HTTPHeaderSet& blockedHeaders = validator.blockedHeaders();
215 if (!blockedHeaders.isEmpty()) {
216 validatedResponse = WebURLResponse(validatedResponse);
217 HTTPHeaderSet::const_iterator end = blockedHeaders.end();
218 for (HTTPHeaderSet::const_iterator it = blockedHeaders.begin(); it != end; ++it)
219 validatedResponse.clearHTTPHeaderField(*it);
220 }
221 m_client->didReceiveResponse(m_loader, validatedResponse);
222}
223
224void AssociatedURLLoader::ClientAdapter::didDownloadData(int dataLength)
225{
226 if (!m_client)
227 return;
228
Torne (Richard Coles)c0e19a62013-08-30 15:15:11 +0100229 m_client->didDownloadData(m_loader, dataLength, -1);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000230}
231
232void AssociatedURLLoader::ClientAdapter::didReceiveData(const char* data, int dataLength)
233{
234 if (!m_client)
235 return;
236
237 m_client->didReceiveData(m_loader, data, dataLength, -1);
238}
239
240void AssociatedURLLoader::ClientAdapter::didReceiveCachedMetadata(const char* data, int dataLength)
241{
242 if (!m_client)
243 return;
244
245 m_client->didReceiveCachedMetadata(m_loader, data, dataLength);
246}
247
248void AssociatedURLLoader::ClientAdapter::didFinishLoading(unsigned long identifier, double finishTime)
249{
250 if (!m_client)
251 return;
252
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000253 m_client->didFinishLoading(m_loader, finishTime, WebURLLoaderClient::kUnknownEncodedDataLength);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000254}
255
256void AssociatedURLLoader::ClientAdapter::didFail(const ResourceError& error)
257{
258 if (!m_client)
259 return;
260
261 m_didFail = true;
262 m_error = WebURLError(error);
263 if (m_enableErrorNotifications)
264 notifyError(&m_errorTimer);
265}
266
267void AssociatedURLLoader::ClientAdapter::didFailRedirectCheck()
268{
269 m_loader->cancel();
270}
271
272void AssociatedURLLoader::ClientAdapter::setDelayedError(const ResourceError& error)
273{
274 didFail(error);
275}
276
277void AssociatedURLLoader::ClientAdapter::enableErrorNotifications()
278{
279 m_enableErrorNotifications = true;
280 // If an error has already been received, start a timer to report it to the client
281 // after AssociatedURLLoader::loadAsynchronously has returned to the caller.
282 if (m_didFail)
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000283 m_errorTimer.startOneShot(0, FROM_HERE);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000284}
285
286void AssociatedURLLoader::ClientAdapter::notifyError(Timer<ClientAdapter>* timer)
287{
288 ASSERT_UNUSED(timer, timer == &m_errorTimer);
289
290 m_client->didFail(m_loader, m_error);
291}
292
293AssociatedURLLoader::AssociatedURLLoader(PassRefPtr<WebFrameImpl> frameImpl, const WebURLLoaderOptions& options)
294 : m_frameImpl(frameImpl)
295 , m_options(options)
296 , m_client(0)
297{
298 ASSERT(m_frameImpl);
299}
300
301AssociatedURLLoader::~AssociatedURLLoader()
302{
303 cancel();
304}
305
306#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, webcore_name) \
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000307 COMPILE_ASSERT(static_cast<int>(blink::webkit_name) == static_cast<int>(WebCore::webcore_name), mismatching_enums)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000308
309COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyDeny, DenyCrossOriginRequests);
310COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl, UseAccessControl);
311COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyAllow, AllowCrossOriginRequests);
312
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000313COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ConsiderPreflight, ConsiderPreflight);
314COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ForcePreflight, ForcePreflight);
315COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::PreventPreflight, PreventPreflight);
316
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000317void AssociatedURLLoader::loadSynchronously(const WebURLRequest& request, WebURLResponse& response, WebURLError& error, WebData& data)
318{
319 ASSERT(0); // Synchronous loading is not supported.
320}
321
322void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebURLLoaderClient* client)
323{
324 ASSERT(!m_client);
325
326 m_client = client;
327 ASSERT(m_client);
328
329 bool allowLoad = true;
330 WebURLRequest newRequest(request);
331 if (m_options.untrustedHTTP) {
332 WebString method = newRequest.httpMethod();
333 allowLoad = isValidHTTPToken(method) && XMLHttpRequest::isAllowedHTTPMethod(method);
334 if (allowLoad) {
335 newRequest.setHTTPMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(method));
336 HTTPRequestHeaderValidator validator;
337 newRequest.visitHTTPHeaderFields(&validator);
338 allowLoad = validator.isSafe();
339 }
340 }
341
342 m_clientAdapter = ClientAdapter::create(this, m_client, m_options);
343
344 if (allowLoad) {
345 ThreadableLoaderOptions options;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000346 options.sniffContent = m_options.sniffContent ? SniffContent : DoNotSniffContent;
347 options.allowCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials;
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000348 options.preflightPolicy = static_cast<WebCore::PreflightPolicy>(m_options.preflightPolicy);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000349 options.crossOriginRequestPolicy = static_cast<WebCore::CrossOriginRequestPolicy>(m_options.crossOriginRequestPolicy);
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000350 options.dataBufferingPolicy = DoNotBufferData;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000351
352 const ResourceRequest& webcoreRequest = newRequest.toResourceRequest();
353 Document* webcoreDocument = m_frameImpl->frame()->document();
354 m_loader = DocumentThreadableLoader::create(webcoreDocument, m_clientAdapter.get(), webcoreRequest, options);
355 } else {
356 // FIXME: return meaningful error codes.
357 m_clientAdapter->setDelayedError(ResourceError());
358 }
359 m_clientAdapter->enableErrorNotifications();
360}
361
362void AssociatedURLLoader::cancel()
363{
364 if (m_clientAdapter)
365 m_clientAdapter->clearClient();
366 if (m_loader)
367 m_loader->cancel();
368}
369
370void AssociatedURLLoader::setDefersLoading(bool defersLoading)
371{
372 if (m_loader)
373 m_loader->setDefersLoading(defersLoading);
374}
375
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000376} // namespace blink