0d9cb86dd792c6df0047cb4ed0ce198e3a381226
[WebKit-https.git] / Source / WebKit / NetworkProcess / PingLoad.cpp
1 /*
2  * Copyright (C) 2016-2017 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 "PingLoad.h"
28
29 #if USE(NETWORK_SESSION)
30
31 #include "AuthenticationManager.h"
32 #include "Logging.h"
33 #include "NetworkCORSPreflightChecker.h"
34 #include "SessionTracker.h"
35 #include <WebCore/ContentSecurityPolicy.h>
36 #include <WebCore/CrossOriginAccessControl.h>
37 #include <WebCore/CrossOriginPreflightResultCache.h>
38
39 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - PingLoad::" fmt, this, ##__VA_ARGS__)
40
41 namespace WebKit {
42
43 using namespace WebCore;
44
45 PingLoad::PingLoad(NetworkResourceLoadParameters&& parameters)
46     : m_parameters(WTFMove(parameters))
47     , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
48     , m_isSameOriginRequest(securityOrigin().canRequest(m_parameters.request.url()))
49 {
50     ASSERT(m_parameters.sourceOrigin);
51     ASSERT(m_parameters.originalRequestHeaders);
52
53     // If the server never responds, this object will hang around forever.
54     // Set a very generous timeout, just in case.
55     m_timeoutTimer.startOneShot(60000_s);
56
57     if (m_isSameOriginRequest || m_parameters.mode == FetchOptions::Mode::NoCors) {
58         loadRequest(ResourceRequest { m_parameters.request });
59         return;
60     }
61
62     makeCrossOriginAccessRequest(ResourceRequest { m_parameters.request });
63 }
64
65 PingLoad::~PingLoad()
66 {
67     if (m_redirectHandler)
68         m_redirectHandler({ });
69
70     if (m_task) {
71         ASSERT(m_task->client() == this);
72         m_task->clearClient();
73         m_task->cancel();
74     }
75 }
76
77 void PingLoad::loadRequest(ResourceRequest&& request)
78 {
79     RELEASE_LOG_IF_ALLOWED("startNetworkLoad");
80     if (auto* networkSession = SessionTracker::networkSession(m_parameters.sessionID)) {
81         auto loadParameters = m_parameters;
82         loadParameters.request = WTFMove(request);
83         m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters));
84         m_task->resume();
85     } else
86         ASSERT_NOT_REACHED();
87 }
88
89 SecurityOrigin& PingLoad::securityOrigin() const
90 {
91     return m_origin ? *m_origin : *m_parameters.sourceOrigin;
92 }
93
94 const HTTPHeaderMap& PingLoad::originalRequestHeaders() const
95 {
96     return *m_parameters.originalRequestHeaders;
97 }
98
99 void PingLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
100 {
101     RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - shouldFollowRedirects? %d", m_parameters.shouldFollowRedirects);
102     if (!m_parameters.shouldFollowRedirects) {
103         completionHandler({ });
104         return;
105     }
106
107     if (auto* contentSecurityPolicy = this->contentSecurityPolicy()) {
108         if (!contentSecurityPolicy->allowConnectToSource(request.url(), redirectResponse.isNull() ? ContentSecurityPolicy::RedirectResponseReceived::No : ContentSecurityPolicy::RedirectResponseReceived::Yes)) {
109             RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect was blocked by CSP");
110             completionHandler({ });
111             return;
112         }
113     }
114
115     // FIXME: We should ensure the number of redirects does not exceed 20.
116
117     if (isAllowedRedirect(request.url())) {
118         completionHandler(WTFMove(request));
119         return;
120     }
121     RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect requires a CORS preflight");
122
123     // Force any subsequent request to use these checks.
124     m_isSameOriginRequest = false;
125
126     // Use a unique origin for subsequent loads if needed.
127     // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10).
128     ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
129     if (!securityOrigin().canRequest(redirectResponse.url()) && !protocolHostAndPortAreEqual(redirectResponse.url(), request.url())) {
130         if (!m_origin || !m_origin->isUnique())
131             m_origin = SecurityOrigin::createUnique();
132     }
133
134     // Except in case where preflight is needed, loading should be able to continue on its own.
135     if (m_isSimpleRequest && isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders())) {
136         completionHandler(WTFMove(request));
137         return;
138     }
139
140     m_parameters.allowStoredCredentials = DoNotAllowStoredCredentials;
141     m_redirectHandler = WTFMove(completionHandler);
142
143     // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm).
144     request.setHTTPHeaderFields(m_parameters.request.httpHeaderFields());
145
146     makeCrossOriginAccessRequest(WTFMove(request));
147 }
148
149 void PingLoad::didReceiveChallenge(const AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
150 {
151     RELEASE_LOG_IF_ALLOWED("didReceiveChallenge");
152     completionHandler(AuthenticationChallengeDisposition::Cancel, { });
153     delete this;
154 }
155
156 void PingLoad::didReceiveResponseNetworkSession(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
157 {
158     RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession - httpStatusCode: %d", response.httpStatusCode());
159     completionHandler(PolicyAction::PolicyIgnore);
160     delete this;
161 }
162
163 void PingLoad::didReceiveData(Ref<SharedBuffer>&&)
164 {
165     RELEASE_LOG_IF_ALLOWED("didReceiveData");
166     ASSERT_NOT_REACHED();
167 }
168
169 void PingLoad::didCompleteWithError(const ResourceError& error, const NetworkLoadMetrics&)
170 {
171     if (error.isNull())
172         RELEASE_LOG_IF_ALLOWED("didComplete");
173     else
174         RELEASE_LOG_IF_ALLOWED("didCompleteWithError, error_code: %d", error.errorCode());
175     delete this;
176 }
177
178 void PingLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
179 {
180 }
181
182 void PingLoad::wasBlocked()
183 {
184     RELEASE_LOG_IF_ALLOWED("wasBlocked");
185     delete this;
186 }
187
188 void PingLoad::cannotShowURL()
189 {
190     RELEASE_LOG_IF_ALLOWED("cannotShowURL");
191     delete this;
192 }
193
194 void PingLoad::timeoutTimerFired()
195 {
196     RELEASE_LOG_IF_ALLOWED("timeoutTimerFired");
197     delete this;
198 }
199
200 bool PingLoad::isAllowedRedirect(const URL& url) const
201 {
202     if (m_parameters.mode == FetchOptions::Mode::NoCors)
203         return true;
204
205     return m_isSameOriginRequest && securityOrigin().canRequest(url);
206 }
207
208 ContentSecurityPolicy* PingLoad::contentSecurityPolicy() const
209 {
210     if (!m_contentSecurityPolicy && m_parameters.cspResponseHeaders) {
211         m_contentSecurityPolicy = std::make_unique<ContentSecurityPolicy>(*m_parameters.sourceOrigin);
212         m_contentSecurityPolicy->didReceiveHeaders(*m_parameters.cspResponseHeaders, ContentSecurityPolicy::ReportParsingErrors::No);
213     }
214     return m_contentSecurityPolicy.get();
215 }
216
217 void PingLoad::makeCrossOriginAccessRequest(ResourceRequest&& request)
218 {
219     ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
220     RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest");
221
222     if (isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders())) {
223         makeSimpleCrossOriginAccessRequest(WTFMove(request));
224         return;
225     }
226
227     m_isSimpleRequest = false;
228     if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), request.url(), m_parameters.allowStoredCredentials, request.httpMethod(), originalRequestHeaders())) {
229         RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest - preflight can be skipped thanks to cached result");
230         preflightSuccess(WTFMove(request));
231     } else
232         makeCrossOriginAccessRequestWithPreflight(WTFMove(request));
233 }
234
235 void PingLoad::makeSimpleCrossOriginAccessRequest(ResourceRequest&& request)
236 {
237     ASSERT(isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders()));
238     RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest");
239
240     if (!request.url().protocolIsInHTTPFamily()) {
241         RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest: Cross origin requests are only supported for HTTP.");
242         return;
243     }
244
245     updateRequestForAccessControl(request, securityOrigin(), m_parameters.allowStoredCredentials);
246     loadRequest(WTFMove(request));
247 }
248
249 void PingLoad::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
250 {
251     RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight");
252     ASSERT(!m_corsPreflightChecker);
253
254     NetworkCORSPreflightChecker::Parameters parameters = {
255         WTFMove(request),
256         securityOrigin(),
257         m_parameters.sessionID,
258         m_parameters.allowStoredCredentials
259     };
260     m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(WTFMove(parameters), [this](NetworkCORSPreflightChecker::Result result) {
261         RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight preflight complete, success: %d forRedirect? %d", result == NetworkCORSPreflightChecker::Result::Success, !!m_redirectHandler);
262         auto corsPreflightChecker = WTFMove(m_corsPreflightChecker);
263         if (result == NetworkCORSPreflightChecker::Result::Success)
264             preflightSuccess(ResourceRequest { corsPreflightChecker->originalRequest() });
265         else
266             delete this;
267     });
268     m_corsPreflightChecker->startPreflight();
269 }
270
271 void PingLoad::preflightSuccess(ResourceRequest&& request)
272 {
273     RELEASE_LOG_IF_ALLOWED("preflightSuccess");
274
275     ResourceRequest actualRequest = WTFMove(request);
276     updateRequestForAccessControl(actualRequest, securityOrigin(), m_parameters.allowStoredCredentials);
277
278     if (auto redirectHandler = std::exchange(m_redirectHandler, nullptr))
279         redirectHandler(WTFMove(actualRequest));
280     else
281         loadRequest(WTFMove(actualRequest));
282 }
283
284 } // namespace WebKit
285
286 #endif // USE(NETWORK_SESSION)