Regression(r220817): We should only copy the original request headers for Ping loads
[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, HTTPHeaderMap&& originalRequestHeaders)
46     : m_parameters(WTFMove(parameters))
47     , m_originalRequestHeaders(WTFMove(originalRequestHeaders))
48     , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
49     , m_isSameOriginRequest(securityOrigin().canRequest(m_parameters.request.url()))
50 {
51     ASSERT(m_parameters.sourceOrigin);
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 void PingLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
95 {
96     RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - shouldFollowRedirects? %d", m_parameters.shouldFollowRedirects);
97     if (!m_parameters.shouldFollowRedirects) {
98         completionHandler({ });
99         return;
100     }
101
102     if (auto* contentSecurityPolicy = this->contentSecurityPolicy()) {
103         if (!contentSecurityPolicy->allowConnectToSource(request.url(), redirectResponse.isNull() ? ContentSecurityPolicy::RedirectResponseReceived::No : ContentSecurityPolicy::RedirectResponseReceived::Yes)) {
104             RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect was blocked by CSP");
105             completionHandler({ });
106             return;
107         }
108     }
109
110     // FIXME: We should ensure the number of redirects does not exceed 20.
111
112     if (isAllowedRedirect(request.url())) {
113         completionHandler(WTFMove(request));
114         return;
115     }
116     RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect requires a CORS preflight");
117
118     // Force any subsequent request to use these checks.
119     m_isSameOriginRequest = false;
120
121     // Use a unique origin for subsequent loads if needed.
122     // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10).
123     ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
124     if (!securityOrigin().canRequest(redirectResponse.url()) && !protocolHostAndPortAreEqual(redirectResponse.url(), request.url())) {
125         if (!m_origin || !m_origin->isUnique())
126             m_origin = SecurityOrigin::createUnique();
127     }
128
129     // Except in case where preflight is needed, loading should be able to continue on its own.
130     if (m_isSimpleRequest && isSimpleCrossOriginAccessRequest(request.httpMethod(), m_originalRequestHeaders)) {
131         completionHandler(WTFMove(request));
132         return;
133     }
134
135     m_parameters.allowStoredCredentials = DoNotAllowStoredCredentials;
136     m_redirectHandler = WTFMove(completionHandler);
137
138     // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm).
139     request.setHTTPHeaderFields(m_parameters.request.httpHeaderFields());
140
141     makeCrossOriginAccessRequest(WTFMove(request));
142 }
143
144 void PingLoad::didReceiveChallenge(const AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
145 {
146     RELEASE_LOG_IF_ALLOWED("didReceiveChallenge");
147     completionHandler(AuthenticationChallengeDisposition::Cancel, { });
148     delete this;
149 }
150
151 void PingLoad::didReceiveResponseNetworkSession(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
152 {
153     RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession - httpStatusCode: %d", response.httpStatusCode());
154     completionHandler(PolicyAction::PolicyIgnore);
155     delete this;
156 }
157
158 void PingLoad::didReceiveData(Ref<SharedBuffer>&&)
159 {
160     RELEASE_LOG_IF_ALLOWED("didReceiveData");
161     ASSERT_NOT_REACHED();
162 }
163
164 void PingLoad::didCompleteWithError(const ResourceError& error, const NetworkLoadMetrics&)
165 {
166     if (error.isNull())
167         RELEASE_LOG_IF_ALLOWED("didComplete");
168     else
169         RELEASE_LOG_IF_ALLOWED("didCompleteWithError, error_code: %d", error.errorCode());
170     delete this;
171 }
172
173 void PingLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
174 {
175 }
176
177 void PingLoad::wasBlocked()
178 {
179     RELEASE_LOG_IF_ALLOWED("wasBlocked");
180     delete this;
181 }
182
183 void PingLoad::cannotShowURL()
184 {
185     RELEASE_LOG_IF_ALLOWED("cannotShowURL");
186     delete this;
187 }
188
189 void PingLoad::timeoutTimerFired()
190 {
191     RELEASE_LOG_IF_ALLOWED("timeoutTimerFired");
192     delete this;
193 }
194
195 bool PingLoad::isAllowedRedirect(const URL& url) const
196 {
197     if (m_parameters.mode == FetchOptions::Mode::NoCors)
198         return true;
199
200     return m_isSameOriginRequest && securityOrigin().canRequest(url);
201 }
202
203 ContentSecurityPolicy* PingLoad::contentSecurityPolicy() const
204 {
205     if (!m_contentSecurityPolicy && m_parameters.cspResponseHeaders) {
206         m_contentSecurityPolicy = std::make_unique<ContentSecurityPolicy>(*m_parameters.sourceOrigin);
207         m_contentSecurityPolicy->didReceiveHeaders(*m_parameters.cspResponseHeaders, ContentSecurityPolicy::ReportParsingErrors::No);
208     }
209     return m_contentSecurityPolicy.get();
210 }
211
212 void PingLoad::makeCrossOriginAccessRequest(ResourceRequest&& request)
213 {
214     ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
215     RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest");
216
217     if (isSimpleCrossOriginAccessRequest(request.httpMethod(), m_originalRequestHeaders)) {
218         makeSimpleCrossOriginAccessRequest(WTFMove(request));
219         return;
220     }
221
222     m_isSimpleRequest = false;
223     if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), request.url(), m_parameters.allowStoredCredentials, request.httpMethod(), m_originalRequestHeaders)) {
224         RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest - preflight can be skipped thanks to cached result");
225         preflightSuccess(WTFMove(request));
226     } else
227         makeCrossOriginAccessRequestWithPreflight(WTFMove(request));
228 }
229
230 void PingLoad::makeSimpleCrossOriginAccessRequest(ResourceRequest&& request)
231 {
232     ASSERT(isSimpleCrossOriginAccessRequest(request.httpMethod(), m_originalRequestHeaders));
233     RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest");
234
235     if (!request.url().protocolIsInHTTPFamily()) {
236         RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest: Cross origin requests are only supported for HTTP.");
237         return;
238     }
239
240     updateRequestForAccessControl(request, securityOrigin(), m_parameters.allowStoredCredentials);
241     loadRequest(WTFMove(request));
242 }
243
244 void PingLoad::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
245 {
246     RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight");
247     ASSERT(!m_corsPreflightChecker);
248
249     NetworkCORSPreflightChecker::Parameters parameters = {
250         WTFMove(request),
251         securityOrigin(),
252         m_parameters.sessionID,
253         m_parameters.allowStoredCredentials
254     };
255     m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(WTFMove(parameters), [this](NetworkCORSPreflightChecker::Result result) {
256         RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight preflight complete, success: %d forRedirect? %d", result == NetworkCORSPreflightChecker::Result::Success, !!m_redirectHandler);
257         auto corsPreflightChecker = WTFMove(m_corsPreflightChecker);
258         if (result == NetworkCORSPreflightChecker::Result::Success)
259             preflightSuccess(ResourceRequest { corsPreflightChecker->originalRequest() });
260         else
261             delete this;
262     });
263     m_corsPreflightChecker->startPreflight();
264 }
265
266 void PingLoad::preflightSuccess(ResourceRequest&& request)
267 {
268     RELEASE_LOG_IF_ALLOWED("preflightSuccess");
269
270     ResourceRequest actualRequest = WTFMove(request);
271     updateRequestForAccessControl(actualRequest, securityOrigin(), m_parameters.allowStoredCredentials);
272
273     if (auto redirectHandler = std::exchange(m_redirectHandler, nullptr))
274         redirectHandler(WTFMove(actualRequest));
275     else
276         loadRequest(WTFMove(actualRequest));
277 }
278
279 } // namespace WebKit
280
281 #endif // USE(NETWORK_SESSION)