1/*
2 * Copyright (C) 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 "NetworkCORSPreflightChecker.h"
28
29#include "AuthenticationChallengeDisposition.h"
30#include "AuthenticationManager.h"
31#include "Logging.h"
32#include "NetworkLoadParameters.h"
33#include "NetworkProcess.h"
34#include <WebCore/CrossOriginAccessControl.h>
35#include <WebCore/SecurityOrigin.h>
36
37#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - NetworkCORSPreflightChecker::" fmt, this, ##__VA_ARGS__)
38
39namespace WebKit {
40
41using namespace WebCore;
42
43NetworkCORSPreflightChecker::NetworkCORSPreflightChecker(NetworkProcess& networkProcess, Parameters&& parameters, bool shouldCaptureExtraNetworkLoadMetrics, CompletionCallback&& completionCallback)
44 : m_parameters(WTFMove(parameters))
45 , m_networkProcess(networkProcess)
46 , m_completionCallback(WTFMove(completionCallback))
47 , m_shouldCaptureExtraNetworkLoadMetrics(shouldCaptureExtraNetworkLoadMetrics)
48{
49}
50
51NetworkCORSPreflightChecker::~NetworkCORSPreflightChecker()
52{
53 if (m_task) {
54 ASSERT(m_task->client() == this);
55 m_task->clearClient();
56 m_task->cancel();
57 }
58 if (m_completionCallback)
59 m_completionCallback(ResourceError { ResourceError::Type::Cancellation });
60}
61
62void NetworkCORSPreflightChecker::startPreflight()
63{
64 RELEASE_LOG_IF_ALLOWED("startPreflight");
65
66 NetworkLoadParameters loadParameters;
67 loadParameters.sessionID = m_parameters.sessionID;
68 loadParameters.request = createAccessControlPreflightRequest(m_parameters.originalRequest, m_parameters.sourceOrigin, m_parameters.referrer);
69 if (!m_parameters.userAgent.isNull())
70 loadParameters.request.setHTTPHeaderField(HTTPHeaderName::UserAgent, m_parameters.userAgent);
71
72 if (m_shouldCaptureExtraNetworkLoadMetrics)
73 m_loadInformation = NetworkTransactionInformation { NetworkTransactionInformation::Type::Preflight, loadParameters.request, { }, { } };
74
75 if (auto* networkSession = m_networkProcess->networkSession(loadParameters.sessionID)) {
76 m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters));
77 m_task->resume();
78 } else
79 ASSERT_NOT_REACHED();
80}
81
82void NetworkCORSPreflightChecker::willPerformHTTPRedirection(WebCore::ResourceResponse&& response, WebCore::ResourceRequest&&, RedirectCompletionHandler&& completionHandler)
83{
84 if (m_shouldCaptureExtraNetworkLoadMetrics)
85 m_loadInformation.response = WTFMove(response);
86
87 RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection");
88 completionHandler({ });
89 m_completionCallback(ResourceError { errorDomainWebKitInternal, 0, m_parameters.originalRequest.url(), "Preflight response is not successful"_s, ResourceError::Type::AccessControl });
90}
91
92void NetworkCORSPreflightChecker::didReceiveChallenge(WebCore::AuthenticationChallenge&& challenge, ChallengeCompletionHandler&& completionHandler)
93{
94 RELEASE_LOG_IF_ALLOWED("didReceiveChallenge, authentication scheme: %u", challenge.protectionSpace().authenticationScheme());
95
96 auto scheme = challenge.protectionSpace().authenticationScheme();
97 bool isTLSHandshake = scheme == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested
98 || scheme == ProtectionSpaceAuthenticationSchemeClientCertificateRequested;
99
100 if (!isTLSHandshake) {
101 completionHandler(AuthenticationChallengeDisposition::UseCredential, { });
102 return;
103 }
104
105 m_networkProcess->authenticationManager().didReceiveAuthenticationChallenge(m_parameters.pageID, m_parameters.frameID, challenge, WTFMove(completionHandler));
106}
107
108void NetworkCORSPreflightChecker::didReceiveResponse(WebCore::ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
109{
110 RELEASE_LOG_IF_ALLOWED("didReceiveResponse");
111
112 if (m_shouldCaptureExtraNetworkLoadMetrics)
113 m_loadInformation.response = response;
114
115 m_response = WTFMove(response);
116 completionHandler(PolicyAction::Use);
117}
118
119void NetworkCORSPreflightChecker::didReceiveData(Ref<WebCore::SharedBuffer>&&)
120{
121 RELEASE_LOG_IF_ALLOWED("didReceiveData");
122}
123
124void NetworkCORSPreflightChecker::didCompleteWithError(const WebCore::ResourceError& preflightError, const WebCore::NetworkLoadMetrics& metrics)
125{
126 if (m_shouldCaptureExtraNetworkLoadMetrics)
127 m_loadInformation.metrics = metrics;
128
129 if (!preflightError.isNull()) {
130 RELEASE_LOG_IF_ALLOWED("didCompleteWithError");
131 auto error = preflightError;
132 if (error.isNull() || error.isGeneral())
133 error.setType(ResourceError::Type::AccessControl);
134
135 m_completionCallback(WTFMove(error));
136 return;
137 }
138
139 RELEASE_LOG_IF_ALLOWED("didComplete http_status_code: %d", m_response.httpStatusCode());
140
141 String errorDescription;
142 if (!validatePreflightResponse(m_parameters.originalRequest, m_response, m_parameters.storedCredentialsPolicy, m_parameters.sourceOrigin, errorDescription)) {
143 RELEASE_LOG_IF_ALLOWED("didComplete, AccessControl error: %s", errorDescription.utf8().data());
144 m_completionCallback(ResourceError { errorDomainWebKitInternal, 0, m_parameters.originalRequest.url(), errorDescription, ResourceError::Type::AccessControl });
145 return;
146 }
147 m_completionCallback(ResourceError { });
148}
149
150void NetworkCORSPreflightChecker::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
151{
152}
153
154void NetworkCORSPreflightChecker::wasBlocked()
155{
156 RELEASE_LOG_IF_ALLOWED("wasBlocked");
157 m_completionCallback(ResourceError { errorDomainWebKitInternal, 0, m_parameters.originalRequest.url(), "CORS-preflight request was blocked"_s, ResourceError::Type::AccessControl });
158}
159
160void NetworkCORSPreflightChecker::cannotShowURL()
161{
162 RELEASE_LOG_IF_ALLOWED("cannotShowURL");
163 m_completionCallback(ResourceError { errorDomainWebKitInternal, 0, m_parameters.originalRequest.url(), "Preflight response was blocked"_s, ResourceError::Type::AccessControl });
164}
165
166void NetworkCORSPreflightChecker::wasBlockedByRestrictions()
167{
168 RELEASE_LOG_IF_ALLOWED("wasBlockedByRestrictions");
169 m_completionCallback(ResourceError { errorDomainWebKitInternal, 0, m_parameters.originalRequest.url(), "Preflight response was blocked"_s, ResourceError::Type::AccessControl });
170}
171
172NetworkTransactionInformation NetworkCORSPreflightChecker::takeInformation()
173{
174 ASSERT(m_shouldCaptureExtraNetworkLoadMetrics);
175 return WTFMove(m_loadInformation);
176}
177
178} // Namespace WebKit
179
180#undef RELEASE_LOG_IF_ALLOWED
181