1/*
2 * Copyright (C) 2012 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NetworkResourceLoader.h"
28
29#if ENABLE(NETWORK_PROCESS)
30
31#include "AsynchronousNetworkLoaderClient.h"
32#include "AuthenticationManager.h"
33#include "DataReference.h"
34#include "Logging.h"
35#include "NetworkBlobRegistry.h"
36#include "NetworkConnectionToWebProcess.h"
37#include "NetworkProcess.h"
38#include "NetworkProcessConnectionMessages.h"
39#include "NetworkResourceLoadParameters.h"
40#include "RemoteNetworkingContext.h"
41#include "ShareableResource.h"
42#include "SharedMemory.h"
43#include "SynchronousNetworkLoaderClient.h"
44#include "WebCoreArgumentCoders.h"
45#include "WebErrors.h"
46#include "WebResourceLoaderMessages.h"
47#include <WebCore/BlobDataFileReference.h>
48#include <WebCore/NotImplemented.h>
49#include <WebCore/ResourceBuffer.h>
50#include <WebCore/ResourceHandle.h>
51#include <wtf/MainThread.h>
52
53using namespace WebCore;
54
55namespace WebKit {
56
57NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> reply)
58    : m_bytesReceived(0)
59    , m_handleConvertedToDownload(false)
60    , m_identifier(parameters.identifier)
61    , m_webPageID(parameters.webPageID)
62    , m_webFrameID(parameters.webFrameID)
63    , m_sessionID(parameters.sessionID)
64    , m_request(parameters.request)
65    , m_priority(parameters.priority)
66    , m_contentSniffingPolicy(parameters.contentSniffingPolicy)
67    , m_allowStoredCredentials(parameters.allowStoredCredentials)
68    , m_clientCredentialPolicy(parameters.clientCredentialPolicy)
69    , m_shouldClearReferrerOnHTTPSToHTTPRedirect(parameters.shouldClearReferrerOnHTTPSToHTTPRedirect)
70    , m_isLoadingMainResource(parameters.isMainResource)
71    , m_defersLoading(parameters.defersLoading)
72    , m_sandboxExtensionsAreConsumed(false)
73    , m_connection(connection)
74{
75    // Either this loader has both a webPageID and webFrameID, or it is not allowed to ask the client for authentication credentials.
76    // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
77    //        Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
78    ASSERT((m_webPageID && m_webFrameID) || m_clientCredentialPolicy == DoNotAskClientForAnyCredentials);
79
80    for (size_t i = 0, count = parameters.requestBodySandboxExtensions.size(); i < count; ++i) {
81        if (RefPtr<SandboxExtension> extension = SandboxExtension::create(parameters.requestBodySandboxExtensions[i]))
82            m_requestBodySandboxExtensions.append(extension);
83    }
84
85    if (m_request.httpBody()) {
86        for (const FormDataElement& element : m_request.httpBody()->elements()) {
87            if (element.m_type == FormDataElement::Type::EncodedBlob)
88                m_fileReferences.appendVector(NetworkBlobRegistry::shared().filesInBlob(connection, element.m_url));
89        }
90    }
91
92    if (m_request.url().protocolIs("blob")) {
93        ASSERT(!SandboxExtension::create(parameters.resourceSandboxExtension));
94        m_fileReferences.appendVector(NetworkBlobRegistry::shared().filesInBlob(connection, m_request.url()));
95    } else
96
97    if (RefPtr<SandboxExtension> resourceSandboxExtension = SandboxExtension::create(parameters.resourceSandboxExtension))
98        m_resourceSandboxExtensions.append(resourceSandboxExtension);
99
100    ASSERT(RunLoop::isMain());
101
102    if (reply || parameters.shouldBufferResource)
103        m_bufferedData = WebCore::SharedBuffer::create();
104
105    if (reply)
106        m_networkLoaderClient = std::make_unique<SynchronousNetworkLoaderClient>(m_request, reply);
107    else
108        m_networkLoaderClient = std::make_unique<AsynchronousNetworkLoaderClient>();
109}
110
111NetworkResourceLoader::~NetworkResourceLoader()
112{
113    ASSERT(RunLoop::isMain());
114    ASSERT(!m_handle);
115    ASSERT(!m_hostRecord);
116}
117
118bool NetworkResourceLoader::isSynchronous() const
119{
120    return m_networkLoaderClient->isSynchronous();
121}
122
123void NetworkResourceLoader::start()
124{
125    ASSERT(RunLoop::isMain());
126
127    if (m_defersLoading) {
128        m_deferredRequest = m_request;
129        return;
130    }
131
132    // Explicit ref() balanced by a deref() in NetworkResourceLoader::cleanup()
133    ref();
134
135    // FIXME (NetworkProcess): Set platform specific settings.
136    m_networkingContext = RemoteNetworkingContext::create(m_sessionID, m_shouldClearReferrerOnHTTPSToHTTPRedirect);
137
138    consumeSandboxExtensions();
139
140    // FIXME (NetworkProcess): Pass an actual value for defersLoading
141    m_handle = ResourceHandle::create(m_networkingContext.get(), m_request, this, false /* defersLoading */, m_contentSniffingPolicy == SniffContent);
142}
143
144void NetworkResourceLoader::setDefersLoading(bool defers)
145{
146    m_defersLoading = defers;
147    if (m_handle)
148        m_handle->setDefersLoading(defers);
149    if (!defers && !m_deferredRequest.isNull()) {
150        m_request = m_deferredRequest;
151        m_deferredRequest = ResourceRequest();
152        start();
153    }
154}
155
156void NetworkResourceLoader::cleanup()
157{
158    ASSERT(RunLoop::isMain());
159
160    invalidateSandboxExtensions();
161
162    // Tell the scheduler about this finished loader soon so it can start more network requests.
163    NetworkProcess::shared().networkResourceLoadScheduler().scheduleRemoveLoader(this);
164
165    if (m_handle) {
166        // Explicit deref() balanced by a ref() in NetworkResourceLoader::start()
167        // This might cause the NetworkResourceLoader to be destroyed and therefore we do it last.
168        m_handle = 0;
169        deref();
170    }
171}
172
173void NetworkResourceLoader::didConvertHandleToDownload()
174{
175    ASSERT(m_handle);
176    m_handleConvertedToDownload = true;
177}
178
179void NetworkResourceLoader::abort()
180{
181    ASSERT(RunLoop::isMain());
182
183    if (m_handle && !m_handleConvertedToDownload)
184        m_handle->cancel();
185
186    cleanup();
187}
188
189void NetworkResourceLoader::didReceiveResponseAsync(ResourceHandle* handle, const ResourceResponse& response)
190{
191    ASSERT_UNUSED(handle, handle == m_handle);
192
193    m_networkLoaderClient->didReceiveResponse(this, response);
194
195    // m_handle will be 0 if the request got aborted above.
196    if (!m_handle)
197        return;
198
199    if (!m_isLoadingMainResource) {
200        // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message.
201        m_handle->continueDidReceiveResponse();
202    }
203}
204
205void NetworkResourceLoader::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int /* encodedDataLength */)
206{
207    // The NetworkProcess should never get a didReceiveData callback.
208    // We should always be using didReceiveBuffer.
209    ASSERT_NOT_REACHED();
210}
211
212void NetworkResourceLoader::didReceiveBuffer(ResourceHandle* handle, PassRefPtr<SharedBuffer> buffer, int encodedDataLength)
213{
214    ASSERT_UNUSED(handle, handle == m_handle);
215
216    // FIXME (NetworkProcess): For the memory cache we'll also need to cache the response data here.
217    // Such buffering will need to be thread safe, as this callback is happening on a background thread.
218
219    m_bytesReceived += buffer->size();
220    if (m_bufferedData)
221        m_bufferedData->append(buffer.get());
222    else
223        m_networkLoaderClient->didReceiveBuffer(this, buffer.get(), encodedDataLength);
224}
225
226void NetworkResourceLoader::didFinishLoading(ResourceHandle* handle, double finishTime)
227{
228    ASSERT_UNUSED(handle, handle == m_handle);
229
230    // Send the full resource data if we were buffering it.
231    if (m_bufferedData && m_bufferedData->size())
232        m_networkLoaderClient->didReceiveBuffer(this, m_bufferedData.get(), m_bufferedData->size());
233
234    m_networkLoaderClient->didFinishLoading(this, finishTime);
235
236    cleanup();
237}
238
239void NetworkResourceLoader::didFail(ResourceHandle* handle, const ResourceError& error)
240{
241    ASSERT_UNUSED(handle, handle == m_handle);
242
243    m_networkLoaderClient->didFail(this, error);
244
245    cleanup();
246}
247
248void NetworkResourceLoader::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& redirectResponse)
249{
250    ASSERT_UNUSED(handle, handle == m_handle);
251
252    // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect.
253    ASSERT(!redirectResponse.isNull());
254    ASSERT(RunLoop::isMain());
255
256    ResourceRequest proposedRequest = request;
257    m_suggestedRequestForWillSendRequest = request;
258
259    m_networkLoaderClient->willSendRequest(this, proposedRequest, redirectResponse);
260}
261
262void NetworkResourceLoader::continueWillSendRequest(const ResourceRequest& newRequest)
263{
264#if PLATFORM(COCOA)
265    m_suggestedRequestForWillSendRequest.updateFromDelegatePreservingOldProperties(newRequest.nsURLRequest(DoNotUpdateHTTPBody));
266#elif USE(SOUP)
267    // FIXME: Implement ResourceRequest::updateFromDelegatePreservingOldProperties. See https://bugs.webkit.org/show_bug.cgi?id=126127.
268    m_suggestedRequestForWillSendRequest.updateFromDelegatePreservingOldProperties(newRequest);
269#endif
270
271    RunLoop::main().dispatch(bind(&NetworkResourceLoadScheduler::receivedRedirect, &NetworkProcess::shared().networkResourceLoadScheduler(), this, m_suggestedRequestForWillSendRequest.url()));
272
273    m_request = m_suggestedRequestForWillSendRequest;
274    m_suggestedRequestForWillSendRequest = ResourceRequest();
275
276    m_handle->continueWillSendRequest(m_request);
277
278    if (m_request.isNull()) {
279        m_handle->cancel();
280        didFail(m_handle.get(), cancelledError(m_request));
281    }
282}
283
284void NetworkResourceLoader::continueDidReceiveResponse()
285{
286    // FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly.
287    // Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse.
288    if (!m_handle)
289        return;
290
291    m_handle->continueDidReceiveResponse();
292}
293
294void NetworkResourceLoader::didSendData(ResourceHandle* handle, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
295{
296    ASSERT_UNUSED(handle, handle == m_handle);
297
298    m_networkLoaderClient->didSendData(this, bytesSent, totalBytesToBeSent);
299}
300
301void NetworkResourceLoader::wasBlocked(ResourceHandle* handle)
302{
303    ASSERT_UNUSED(handle, handle == m_handle);
304
305    didFail(handle, WebKit::blockedError(request()));
306}
307
308void NetworkResourceLoader::cannotShowURL(ResourceHandle* handle)
309{
310    ASSERT_UNUSED(handle, handle == m_handle);
311
312    didFail(handle, WebKit::cannotShowURLError(request()));
313}
314
315bool NetworkResourceLoader::shouldUseCredentialStorage(ResourceHandle* handle)
316{
317    ASSERT_UNUSED(handle, handle == m_handle || !m_handle); // m_handle will be 0 if called from ResourceHandle::start().
318
319    // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked.
320    // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer.
321
322    // We still need this sync version, because ResourceHandle itself uses it internally, even when the delegate uses an async one.
323
324    return m_allowStoredCredentials == AllowStoredCredentials;
325}
326
327void NetworkResourceLoader::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge)
328{
329    ASSERT_UNUSED(handle, handle == m_handle);
330
331    // FIXME (http://webkit.org/b/115291): Since we go straight to the UI process for authentication we don't get WebCore's
332    // cross-origin check before asking the client for credentials.
333    // Therefore we are too permissive in the case where the ClientCredentialPolicy is DoNotAskClientForCrossOriginCredentials.
334    if (m_clientCredentialPolicy == DoNotAskClientForAnyCredentials) {
335        challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge);
336        return;
337    }
338
339    NetworkProcess::shared().authenticationManager().didReceiveAuthenticationChallenge(m_webPageID, m_webFrameID, challenge);
340}
341
342void NetworkResourceLoader::didCancelAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge&)
343{
344    ASSERT_UNUSED(handle, handle == m_handle);
345
346    // This function is probably not needed (see <rdar://problem/8960124>).
347    notImplemented();
348}
349
350void NetworkResourceLoader::receivedCancellation(ResourceHandle* handle, const AuthenticationChallenge&)
351{
352    ASSERT_UNUSED(handle, handle == m_handle);
353
354    m_handle->cancel();
355    didFail(m_handle.get(), cancelledError(m_request));
356}
357
358IPC::Connection* NetworkResourceLoader::messageSenderConnection()
359{
360    return connectionToWebProcess()->connection();
361}
362
363void NetworkResourceLoader::consumeSandboxExtensions()
364{
365    for (RefPtr<SandboxExtension>& extension : m_requestBodySandboxExtensions)
366        extension->consume();
367
368    for (RefPtr<SandboxExtension>& extension : m_resourceSandboxExtensions)
369        extension->consume();
370
371    for (RefPtr<BlobDataFileReference>& fileReference : m_fileReferences)
372        fileReference->prepareForFileAccess();
373
374    m_sandboxExtensionsAreConsumed = true;
375}
376
377void NetworkResourceLoader::invalidateSandboxExtensions()
378{
379    if (m_sandboxExtensionsAreConsumed) {
380        for (RefPtr<SandboxExtension>& extension : m_requestBodySandboxExtensions)
381            extension->revoke();
382        for (RefPtr<SandboxExtension>& extension : m_resourceSandboxExtensions)
383            extension->revoke();
384        for (RefPtr<BlobDataFileReference>& fileReference : m_fileReferences)
385            fileReference->revokeFileAccess();
386    }
387
388    m_requestBodySandboxExtensions.clear();
389    m_resourceSandboxExtensions.clear();
390    m_fileReferences.clear();
391
392    m_sandboxExtensionsAreConsumed = false;
393}
394
395#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
396void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle* handle, const ProtectionSpace& protectionSpace)
397{
398    ASSERT(RunLoop::isMain());
399    ASSERT_UNUSED(handle, handle == m_handle);
400
401    m_networkLoaderClient->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
402}
403
404void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
405{
406    m_handle->continueCanAuthenticateAgainstProtectionSpace(result);
407}
408
409#endif
410
411#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
412bool NetworkResourceLoader::supportsDataArray()
413{
414    notImplemented();
415    return false;
416}
417
418void NetworkResourceLoader::didReceiveDataArray(ResourceHandle*, CFArrayRef)
419{
420    ASSERT_NOT_REACHED();
421    notImplemented();
422}
423#endif
424
425} // namespace WebKit
426
427#endif // ENABLE(NETWORK_PROCESS)
428