1/*
2 * Copyright (C) 2012-2018 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 "WebResourceLoader.h"
28
29#include "DataReference.h"
30#include "Logging.h"
31#include "NetworkProcessConnection.h"
32#include "NetworkResourceLoaderMessages.h"
33#include "WebCoreArgumentCoders.h"
34#include "WebErrors.h"
35#include "WebProcess.h"
36#include <WebCore/ApplicationCacheHost.h>
37#include <WebCore/CertificateInfo.h>
38#include <WebCore/DiagnosticLoggingClient.h>
39#include <WebCore/DiagnosticLoggingKeys.h>
40#include <WebCore/DocumentLoader.h>
41#include <WebCore/Frame.h>
42#include <WebCore/Page.h>
43#include <WebCore/ResourceError.h>
44#include <WebCore/ResourceLoader.h>
45#include <WebCore/SubresourceLoader.h>
46#include <wtf/CompletionHandler.h>
47
48#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - WebResourceLoader::" fmt, this, ##__VA_ARGS__)
49
50namespace WebKit {
51using namespace WebCore;
52
53Ref<WebResourceLoader> WebResourceLoader::create(Ref<ResourceLoader>&& coreLoader, const TrackingParameters& trackingParameters)
54{
55 return adoptRef(*new WebResourceLoader(WTFMove(coreLoader), trackingParameters));
56}
57
58WebResourceLoader::WebResourceLoader(Ref<WebCore::ResourceLoader>&& coreLoader, const TrackingParameters& trackingParameters)
59 : m_coreLoader(WTFMove(coreLoader))
60 , m_trackingParameters(trackingParameters)
61{
62}
63
64WebResourceLoader::~WebResourceLoader()
65{
66}
67
68IPC::Connection* WebResourceLoader::messageSenderConnection() const
69{
70 return &WebProcess::singleton().ensureNetworkProcessConnection().connection();
71}
72
73uint64_t WebResourceLoader::messageSenderDestinationID() const
74{
75 RELEASE_ASSERT(RunLoop::isMain());
76 RELEASE_ASSERT(m_coreLoader->identifier());
77 return m_coreLoader->identifier();
78}
79
80void WebResourceLoader::detachFromCoreLoader()
81{
82 RELEASE_ASSERT(RunLoop::isMain());
83 m_coreLoader = nullptr;
84}
85
86void WebResourceLoader::willSendRequest(ResourceRequest&& proposedRequest, ResourceResponse&& redirectResponse)
87{
88 Ref<WebResourceLoader> protectedThis(*this);
89
90 LOG(Network, "(WebProcess) WebResourceLoader::willSendRequest to '%s'", proposedRequest.url().string().latin1().data());
91 RELEASE_LOG_IF_ALLOWED("willSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
92
93 if (m_coreLoader->documentLoader()->applicationCacheHost().maybeLoadFallbackForRedirect(m_coreLoader.get(), proposedRequest, redirectResponse)) {
94 RELEASE_LOG_IF_ALLOWED("willSendRequest: exiting early because maybeLoadFallbackForRedirect returned false (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
95 return;
96 }
97
98 m_coreLoader->willSendRequest(WTFMove(proposedRequest), redirectResponse, [this, protectedThis = WTFMove(protectedThis)](ResourceRequest&& request) {
99 if (!m_coreLoader || !m_coreLoader->identifier()) {
100 RELEASE_LOG_IF_ALLOWED("willSendRequest: exiting early because no coreloader or identifier (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
101 return;
102 }
103
104 send(Messages::NetworkResourceLoader::ContinueWillSendRequest(request, m_coreLoader->isAllowedToAskUserForCredentials()));
105 });
106}
107
108void WebResourceLoader::didSendData(uint64_t bytesSent, uint64_t totalBytesToBeSent)
109{
110 m_coreLoader->didSendData(bytesSent, totalBytesToBeSent);
111}
112
113void WebResourceLoader::didReceiveResponse(const ResourceResponse& response, bool needsContinueDidReceiveResponseMessage)
114{
115 LOG(Network, "(WebProcess) WebResourceLoader::didReceiveResponse for '%s'. Status %d.", m_coreLoader->url().string().latin1().data(), response.httpStatusCode());
116 RELEASE_LOG_IF_ALLOWED("didReceiveResponse: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", status = %d)", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID, response.httpStatusCode());
117
118 Ref<WebResourceLoader> protectedThis(*this);
119
120 if (m_coreLoader->documentLoader()->applicationCacheHost().maybeLoadFallbackForResponse(m_coreLoader.get(), response)) {
121 RELEASE_LOG_IF_ALLOWED("didReceiveResponse: not continuing load because the content is already cached (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
122 return;
123 }
124
125 CompletionHandler<void()> policyDecisionCompletionHandler;
126 if (needsContinueDidReceiveResponseMessage) {
127#if !ASSERT_DISABLED
128 m_isProcessingNetworkResponse = true;
129#endif
130 policyDecisionCompletionHandler = [this, protectedThis = WTFMove(protectedThis)] {
131#if !ASSERT_DISABLED
132 m_isProcessingNetworkResponse = false;
133#endif
134 // If m_coreLoader becomes null as a result of the didReceiveResponse callback, we can't use the send function().
135 if (m_coreLoader && m_coreLoader->identifier())
136 send(Messages::NetworkResourceLoader::ContinueDidReceiveResponse());
137 else
138 RELEASE_LOG_IF_ALLOWED("didReceiveResponse: not continuing load because no coreLoader or no ID (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
139 };
140 }
141
142 m_coreLoader->didReceiveResponse(response, WTFMove(policyDecisionCompletionHandler));
143}
144
145void WebResourceLoader::didReceiveData(const IPC::DataReference& data, int64_t encodedDataLength)
146{
147 LOG(Network, "(WebProcess) WebResourceLoader::didReceiveData of size %lu for '%s'", data.size(), m_coreLoader->url().string().latin1().data());
148 ASSERT_WITH_MESSAGE(!m_isProcessingNetworkResponse, "Network process should not send data until we've validated the response");
149
150 if (!m_numBytesReceived) {
151 RELEASE_LOG_IF_ALLOWED("didReceiveData: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
152 }
153 m_numBytesReceived += data.size();
154
155 m_coreLoader->didReceiveData(reinterpret_cast<const char*>(data.data()), data.size(), encodedDataLength, DataPayloadBytes);
156}
157
158void WebResourceLoader::didFinishResourceLoad(const NetworkLoadMetrics& networkLoadMetrics)
159{
160 LOG(Network, "(WebProcess) WebResourceLoader::didFinishResourceLoad for '%s'", m_coreLoader->url().string().latin1().data());
161 RELEASE_LOG_IF_ALLOWED("didFinishResourceLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID, m_numBytesReceived);
162
163 ASSERT_WITH_MESSAGE(!m_isProcessingNetworkResponse, "Load should not be able to finish before we've validated the response");
164 m_coreLoader->didFinishLoading(networkLoadMetrics);
165}
166
167void WebResourceLoader::didFailResourceLoad(const ResourceError& error)
168{
169 LOG(Network, "(WebProcess) WebResourceLoader::didFailResourceLoad for '%s'", m_coreLoader->url().string().latin1().data());
170 RELEASE_LOG_IF_ALLOWED("didFailResourceLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
171
172 ASSERT_WITH_MESSAGE(!m_isProcessingNetworkResponse, "Load should not be able to finish before we've validated the response");
173
174 if (m_coreLoader->documentLoader()->applicationCacheHost().maybeLoadFallbackForError(m_coreLoader.get(), error))
175 return;
176 m_coreLoader->didFail(error);
177}
178
179void WebResourceLoader::didBlockAuthenticationChallenge()
180{
181 LOG(Network, "(WebProcess) WebResourceLoader::didBlockAuthenticationChallenge for '%s'", m_coreLoader->url().string().latin1().data());
182 RELEASE_LOG_IF_ALLOWED("didBlockAuthenticationChallenge: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
183
184 m_coreLoader->didBlockAuthenticationChallenge();
185}
186
187void WebResourceLoader::stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(const ResourceResponse& response)
188{
189 LOG(Network, "(WebProcess) WebResourceLoader::stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied for '%s'", m_coreLoader->url().string().latin1().data());
190 RELEASE_LOG_IF_ALLOWED("stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
191
192 m_coreLoader->documentLoader()->stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(m_coreLoader->identifier(), response);
193}
194
195#if ENABLE(SHAREABLE_RESOURCE)
196void WebResourceLoader::didReceiveResource(const ShareableResource::Handle& handle)
197{
198 LOG(Network, "(WebProcess) WebResourceLoader::didReceiveResource for '%s'", m_coreLoader->url().string().latin1().data());
199 RELEASE_LOG_IF_ALLOWED("didReceiveResource: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
200
201 RefPtr<SharedBuffer> buffer = handle.tryWrapInSharedBuffer();
202
203 if (!buffer) {
204 LOG_ERROR("Unable to create buffer from ShareableResource sent from the network process.");
205 RELEASE_LOG_IF_ALLOWED("didReceiveResource: Unable to create SharedBuffer (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_trackingParameters.pageID.toUInt64(), m_trackingParameters.frameID, m_trackingParameters.resourceID);
206 if (auto* frame = m_coreLoader->frame()) {
207 if (auto* page = frame->page())
208 page->diagnosticLoggingClient().logDiagnosticMessage(WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::createSharedBufferFailedKey(), WebCore::ShouldSample::No);
209 }
210 m_coreLoader->didFail(internalError(m_coreLoader->request().url()));
211 return;
212 }
213
214 Ref<WebResourceLoader> protect(*this);
215
216 // Only send data to the didReceiveData callback if it exists.
217 if (unsigned bufferSize = buffer->size())
218 m_coreLoader->didReceiveBuffer(buffer.releaseNonNull(), bufferSize, DataPayloadWholeResource);
219
220 if (!m_coreLoader)
221 return;
222
223 NetworkLoadMetrics emptyMetrics;
224 m_coreLoader->didFinishLoading(emptyMetrics);
225}
226#endif
227
228bool WebResourceLoader::isAlwaysOnLoggingAllowed() const
229{
230 return resourceLoader() && resourceLoader()->isAlwaysOnLoggingAllowed();
231}
232
233} // namespace WebKit
234
235#undef RELEASE_LOG_IF_ALLOWED
236