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