1 /* 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 "web/AssociatedURLLoader.h" 33 34 #include "core/fetch/CrossOriginAccessControl.h" 35 #include "core/loader/DocumentThreadableLoader.h" 36 #include "core/loader/DocumentThreadableLoaderClient.h" 37 #include "core/xml/XMLHttpRequest.h" 38 #include "platform/Timer.h" 39 #include "platform/exported/WrappedResourceRequest.h" 40 #include "platform/exported/WrappedResourceResponse.h" 41 #include "platform/network/HTTPParsers.h" 42 #include "platform/network/ResourceError.h" 43 #include "public/platform/WebHTTPHeaderVisitor.h" 44 #include "public/platform/WebString.h" 45 #include "public/platform/WebURLError.h" 46 #include "public/platform/WebURLLoaderClient.h" 47 #include "public/platform/WebURLRequest.h" 48 #include "public/web/WebDataSource.h" 49 #include "web/WebLocalFrameImpl.h" 50 #include "wtf/HashSet.h" 51 #include "wtf/text/WTFString.h" 52 53 using namespace WebCore; 54 using namespace WTF; 55 56 namespace blink { 57 58 namespace { 59 60 class HTTPRequestHeaderValidator : public WebHTTPHeaderVisitor { 61 WTF_MAKE_NONCOPYABLE(HTTPRequestHeaderValidator); 62 public: 63 HTTPRequestHeaderValidator() : m_isSafe(true) { } 64 65 void visitHeader(const WebString& name, const WebString& value); 66 bool isSafe() const { return m_isSafe; } 67 68 private: 69 bool m_isSafe; 70 }; 71 72 typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet; 73 74 void 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. 80 class HTTPResponseHeaderValidator : public WebHTTPHeaderVisitor { 81 WTF_MAKE_NONCOPYABLE(HTTPResponseHeaderValidator); 82 public: 83 HTTPResponseHeaderValidator(bool usingAccessControl) : m_usingAccessControl(usingAccessControl) { } 84 85 void visitHeader(const WebString& name, const WebString& value); 86 const HTTPHeaderSet& blockedHeaders(); 87 88 private: 89 HTTPHeaderSet m_exposedHeaders; 90 HTTPHeaderSet m_blockedHeaders; 91 bool m_usingAccessControl; 92 }; 93 94 void 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 105 const 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 m_blockedHeaders.removeAll(m_exposedHeaders); 115 } 116 117 return m_blockedHeaders; 118 } 119 120 } 121 122 // This class bridges the interface differences between WebCore and WebKit loader clients. 123 // It forwards its ThreadableLoaderClient notifications to a WebURLLoaderClient. 124 class AssociatedURLLoader::ClientAdapter FINAL : public DocumentThreadableLoaderClient { 125 WTF_MAKE_NONCOPYABLE(ClientAdapter); 126 public: 127 static PassOwnPtr<ClientAdapter> create(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); 128 129 virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) OVERRIDE; 130 virtual void willSendRequest(ResourceRequest& /*newRequest*/, const ResourceResponse& /*redirectResponse*/) OVERRIDE; 131 132 virtual void didReceiveResponse(unsigned long, const ResourceResponse&) OVERRIDE; 133 virtual void didDownloadData(int /*dataLength*/) OVERRIDE; 134 virtual void didReceiveData(const char*, int /*dataLength*/) OVERRIDE; 135 virtual void didReceiveCachedMetadata(const char*, int /*dataLength*/) OVERRIDE; 136 virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) OVERRIDE; 137 virtual void didFail(const ResourceError&) OVERRIDE; 138 virtual void didFailRedirectCheck() OVERRIDE; 139 140 // Sets an error to be reported back to the client, asychronously. 141 void setDelayedError(const ResourceError&); 142 143 // Enables forwarding of error notifications to the WebURLLoaderClient. These must be 144 // deferred until after the call to AssociatedURLLoader::loadAsynchronously() completes. 145 void enableErrorNotifications(); 146 147 // Stops loading and releases the DocumentThreadableLoader as early as possible. 148 void clearClient() { m_client = 0; } 149 150 private: 151 ClientAdapter(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); 152 153 void notifyError(Timer<ClientAdapter>*); 154 155 AssociatedURLLoader* m_loader; 156 WebURLLoaderClient* m_client; 157 WebURLLoaderOptions m_options; 158 WebURLError m_error; 159 160 Timer<ClientAdapter> m_errorTimer; 161 bool m_enableErrorNotifications; 162 bool m_didFail; 163 }; 164 165 PassOwnPtr<AssociatedURLLoader::ClientAdapter> AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) 166 { 167 return adoptPtr(new ClientAdapter(loader, client, options)); 168 } 169 170 AssociatedURLLoader::ClientAdapter::ClientAdapter(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) 171 : m_loader(loader) 172 , m_client(client) 173 , m_options(options) 174 , m_errorTimer(this, &ClientAdapter::notifyError) 175 , m_enableErrorNotifications(false) 176 , m_didFail(false) 177 { 178 ASSERT(m_loader); 179 ASSERT(m_client); 180 } 181 182 void AssociatedURLLoader::ClientAdapter::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 183 { 184 if (!m_client) 185 return; 186 187 WrappedResourceRequest wrappedNewRequest(newRequest); 188 WrappedResourceResponse wrappedRedirectResponse(redirectResponse); 189 m_client->willSendRequest(m_loader, wrappedNewRequest, wrappedRedirectResponse); 190 } 191 192 void AssociatedURLLoader::ClientAdapter::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 193 { 194 if (!m_client) 195 return; 196 197 m_client->didSendData(m_loader, bytesSent, totalBytesToBeSent); 198 } 199 200 void AssociatedURLLoader::ClientAdapter::didReceiveResponse(unsigned long, const ResourceResponse& response) 201 { 202 if (!m_client) 203 return; 204 205 // Try to use the original ResourceResponse if possible. 206 WebURLResponse validatedResponse = WrappedResourceResponse(response); 207 HTTPResponseHeaderValidator validator(m_options.crossOriginRequestPolicy == WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl); 208 if (!m_options.exposeAllResponseHeaders) 209 validatedResponse.visitHTTPHeaderFields(&validator); 210 211 // If there are blocked headers, copy the response so we can remove them. 212 const HTTPHeaderSet& blockedHeaders = validator.blockedHeaders(); 213 if (!blockedHeaders.isEmpty()) { 214 validatedResponse = WebURLResponse(validatedResponse); 215 HTTPHeaderSet::const_iterator end = blockedHeaders.end(); 216 for (HTTPHeaderSet::const_iterator it = blockedHeaders.begin(); it != end; ++it) 217 validatedResponse.clearHTTPHeaderField(*it); 218 } 219 m_client->didReceiveResponse(m_loader, validatedResponse); 220 } 221 222 void AssociatedURLLoader::ClientAdapter::didDownloadData(int dataLength) 223 { 224 if (!m_client) 225 return; 226 227 m_client->didDownloadData(m_loader, dataLength, -1); 228 } 229 230 void AssociatedURLLoader::ClientAdapter::didReceiveData(const char* data, int dataLength) 231 { 232 if (!m_client) 233 return; 234 235 m_client->didReceiveData(m_loader, data, dataLength, -1); 236 } 237 238 void AssociatedURLLoader::ClientAdapter::didReceiveCachedMetadata(const char* data, int dataLength) 239 { 240 if (!m_client) 241 return; 242 243 m_client->didReceiveCachedMetadata(m_loader, data, dataLength); 244 } 245 246 void AssociatedURLLoader::ClientAdapter::didFinishLoading(unsigned long identifier, double finishTime) 247 { 248 if (!m_client) 249 return; 250 251 m_client->didFinishLoading(m_loader, finishTime, WebURLLoaderClient::kUnknownEncodedDataLength); 252 } 253 254 void AssociatedURLLoader::ClientAdapter::didFail(const ResourceError& error) 255 { 256 if (!m_client) 257 return; 258 259 m_didFail = true; 260 m_error = WebURLError(error); 261 if (m_enableErrorNotifications) 262 notifyError(&m_errorTimer); 263 } 264 265 void AssociatedURLLoader::ClientAdapter::didFailRedirectCheck() 266 { 267 m_loader->cancel(); 268 } 269 270 void AssociatedURLLoader::ClientAdapter::setDelayedError(const ResourceError& error) 271 { 272 didFail(error); 273 } 274 275 void AssociatedURLLoader::ClientAdapter::enableErrorNotifications() 276 { 277 m_enableErrorNotifications = true; 278 // If an error has already been received, start a timer to report it to the client 279 // after AssociatedURLLoader::loadAsynchronously has returned to the caller. 280 if (m_didFail) 281 m_errorTimer.startOneShot(0, FROM_HERE); 282 } 283 284 void AssociatedURLLoader::ClientAdapter::notifyError(Timer<ClientAdapter>* timer) 285 { 286 ASSERT_UNUSED(timer, timer == &m_errorTimer); 287 288 m_client->didFail(m_loader, m_error); 289 } 290 291 AssociatedURLLoader::AssociatedURLLoader(PassRefPtr<WebLocalFrameImpl> frameImpl, const WebURLLoaderOptions& options) 292 : m_frameImpl(frameImpl) 293 , m_options(options) 294 , m_client(0) 295 { 296 ASSERT(m_frameImpl); 297 } 298 299 AssociatedURLLoader::~AssociatedURLLoader() 300 { 301 cancel(); 302 } 303 304 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, webcore_name) \ 305 COMPILE_ASSERT(static_cast<int>(blink::webkit_name) == static_cast<int>(WebCore::webcore_name), mismatching_enums) 306 307 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyDeny, DenyCrossOriginRequests); 308 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl, UseAccessControl); 309 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyAllow, AllowCrossOriginRequests); 310 311 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ConsiderPreflight, ConsiderPreflight); 312 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ForcePreflight, ForcePreflight); 313 COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::PreventPreflight, PreventPreflight); 314 315 void AssociatedURLLoader::loadSynchronously(const WebURLRequest& request, WebURLResponse& response, WebURLError& error, WebData& data) 316 { 317 ASSERT(0); // Synchronous loading is not supported. 318 } 319 320 void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebURLLoaderClient* client) 321 { 322 ASSERT(!m_client); 323 324 m_client = client; 325 ASSERT(m_client); 326 327 bool allowLoad = true; 328 WebURLRequest newRequest(request); 329 if (m_options.untrustedHTTP) { 330 WebString method = newRequest.httpMethod(); 331 allowLoad = isValidHTTPToken(method) && XMLHttpRequest::isAllowedHTTPMethod(method); 332 if (allowLoad) { 333 newRequest.setHTTPMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(method)); 334 HTTPRequestHeaderValidator validator; 335 newRequest.visitHTTPHeaderFields(&validator); 336 allowLoad = validator.isSafe(); 337 } 338 } 339 340 m_clientAdapter = ClientAdapter::create(this, m_client, m_options); 341 342 if (allowLoad) { 343 ThreadableLoaderOptions options; 344 options.preflightPolicy = static_cast<WebCore::PreflightPolicy>(m_options.preflightPolicy); 345 options.crossOriginRequestPolicy = static_cast<WebCore::CrossOriginRequestPolicy>(m_options.crossOriginRequestPolicy); 346 347 ResourceLoaderOptions resourceLoaderOptions; 348 resourceLoaderOptions.sniffContent = m_options.sniffContent ? SniffContent : DoNotSniffContent; 349 resourceLoaderOptions.allowCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials; 350 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; 351 352 const ResourceRequest& webcoreRequest = newRequest.toResourceRequest(); 353 Document* webcoreDocument = m_frameImpl->frame()->document(); 354 ASSERT(webcoreDocument); 355 m_loader = DocumentThreadableLoader::create(*webcoreDocument, m_clientAdapter.get(), webcoreRequest, options, resourceLoaderOptions); 356 } else { 357 // FIXME: return meaningful error codes. 358 m_clientAdapter->setDelayedError(ResourceError()); 359 } 360 m_clientAdapter->enableErrorNotifications(); 361 } 362 363 void AssociatedURLLoader::cancel() 364 { 365 if (m_clientAdapter) 366 m_clientAdapter->clearClient(); 367 if (m_loader) 368 m_loader->cancel(); 369 } 370 371 void AssociatedURLLoader::setDefersLoading(bool defersLoading) 372 { 373 if (m_loader) 374 m_loader->setDefersLoading(defersLoading); 375 } 376 377 } // namespace blink 378