1 | /* |
2 | * Copyright (C) 2016-2019 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 | #include "AuthenticationChallengeDisposition.h" |
30 | #include "AuthenticationManager.h" |
31 | #include "Logging.h" |
32 | #include "NetworkConnectionToWebProcess.h" |
33 | #include "NetworkLoadChecker.h" |
34 | #include "NetworkProcess.h" |
35 | #include "WebErrors.h" |
36 | |
37 | #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - PingLoad::" fmt, this, ##__VA_ARGS__) |
38 | |
39 | namespace WebKit { |
40 | |
41 | using namespace WebCore; |
42 | |
43 | PingLoad::PingLoad(NetworkProcess& networkProcess, NetworkResourceLoadParameters&& parameters, CompletionHandler<void(const ResourceError&, const ResourceResponse&)>&& completionHandler) |
44 | : m_parameters(WTFMove(parameters)) |
45 | , m_completionHandler(WTFMove(completionHandler)) |
46 | , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired) |
47 | , m_networkLoadChecker(makeUniqueRef<NetworkLoadChecker>(networkProcess, FetchOptions { m_parameters.options}, m_parameters.sessionID, m_parameters.webPageID, m_parameters.webFrameID, WTFMove(m_parameters.originalRequestHeaders), URL { m_parameters.request.url() }, m_parameters.sourceOrigin.copyRef(), m_parameters.preflightPolicy, m_parameters.request.httpReferrer())) |
48 | { |
49 | initialize(networkProcess); |
50 | } |
51 | |
52 | PingLoad::PingLoad(NetworkConnectionToWebProcess& connection, NetworkProcess& networkProcess, NetworkResourceLoadParameters&& parameters, CompletionHandler<void(const ResourceError&, const ResourceResponse&)>&& completionHandler) |
53 | : m_parameters(WTFMove(parameters)) |
54 | , m_completionHandler(WTFMove(completionHandler)) |
55 | , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired) |
56 | , m_networkLoadChecker(makeUniqueRef<NetworkLoadChecker>(networkProcess, FetchOptions { m_parameters.options}, m_parameters.sessionID, m_parameters.webPageID, m_parameters.webFrameID, WTFMove(m_parameters.originalRequestHeaders), URL { m_parameters.request.url() }, m_parameters.sourceOrigin.copyRef(), m_parameters.preflightPolicy, m_parameters.request.httpReferrer())) |
57 | , m_blobFiles(connection.resolveBlobReferences(m_parameters)) |
58 | { |
59 | for (auto& file : m_blobFiles) { |
60 | if (file) |
61 | file->prepareForFileAccess(); |
62 | } |
63 | |
64 | initialize(networkProcess); |
65 | } |
66 | |
67 | void PingLoad::initialize(NetworkProcess& networkProcess) |
68 | { |
69 | m_networkLoadChecker->enableContentExtensionsCheck(); |
70 | if (m_parameters.cspResponseHeaders) |
71 | m_networkLoadChecker->setCSPResponseHeaders(WTFMove(m_parameters.cspResponseHeaders.value())); |
72 | #if ENABLE(CONTENT_EXTENSIONS) |
73 | m_networkLoadChecker->setContentExtensionController(WTFMove(m_parameters.mainDocumentURL), m_parameters.userContentControllerIdentifier); |
74 | #endif |
75 | |
76 | // If the server never responds, this object will hang around forever. |
77 | // Set a very generous timeout, just in case. |
78 | m_timeoutTimer.startOneShot(60000_s); |
79 | |
80 | m_networkLoadChecker->check(ResourceRequest { m_parameters.request }, nullptr, [this, weakThis = makeWeakPtr(*this), networkProcess = makeRef(networkProcess)] (auto&& result) { |
81 | if (!weakThis) |
82 | return; |
83 | WTF::switchOn(result, |
84 | [this] (ResourceError& error) { |
85 | this->didFinish(error); |
86 | }, |
87 | [] (NetworkLoadChecker::RedirectionTriplet& triplet) { |
88 | // We should never send a synthetic redirect for PingLoads. |
89 | ASSERT_NOT_REACHED(); |
90 | }, |
91 | [&] (ResourceRequest& request) { |
92 | this->loadRequest(networkProcess, WTFMove(request)); |
93 | } |
94 | ); |
95 | }); |
96 | } |
97 | |
98 | PingLoad::~PingLoad() |
99 | { |
100 | if (m_task) { |
101 | ASSERT(m_task->client() == this); |
102 | m_task->clearClient(); |
103 | m_task->cancel(); |
104 | } |
105 | for (auto& file : m_blobFiles) { |
106 | if (file) |
107 | file->revokeFileAccess(); |
108 | } |
109 | } |
110 | |
111 | void PingLoad::didFinish(const ResourceError& error, const ResourceResponse& response) |
112 | { |
113 | m_completionHandler(error, response); |
114 | delete this; |
115 | } |
116 | |
117 | void PingLoad::loadRequest(NetworkProcess& networkProcess, ResourceRequest&& request) |
118 | { |
119 | RELEASE_LOG_IF_ALLOWED("startNetworkLoad" ); |
120 | if (auto* networkSession = networkProcess.networkSession(m_parameters.sessionID)) { |
121 | auto loadParameters = m_parameters; |
122 | loadParameters.request = WTFMove(request); |
123 | m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters)); |
124 | m_task->resume(); |
125 | } else |
126 | ASSERT_NOT_REACHED(); |
127 | } |
128 | |
129 | void PingLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler) |
130 | { |
131 | m_networkLoadChecker->checkRedirection(ResourceRequest { }, WTFMove(request), WTFMove(redirectResponse), nullptr, [this, completionHandler = WTFMove(completionHandler)] (auto&& result) mutable { |
132 | if (!result.has_value()) { |
133 | completionHandler({ }); |
134 | this->didFinish(result.error()); |
135 | return; |
136 | } |
137 | auto request = WTFMove(result->redirectRequest); |
138 | if (!request.url().protocolIsInHTTPFamily()) { |
139 | this->didFinish(ResourceError { String { }, 0, request.url(), "Redirection to URL with a scheme that is not HTTP(S)"_s , ResourceError::Type::AccessControl }); |
140 | return; |
141 | } |
142 | |
143 | completionHandler(WTFMove(request)); |
144 | }); |
145 | } |
146 | |
147 | void PingLoad::didReceiveChallenge(AuthenticationChallenge&& challenge, ChallengeCompletionHandler&& completionHandler) |
148 | { |
149 | RELEASE_LOG_IF_ALLOWED("didReceiveChallenge" ); |
150 | if (challenge.protectionSpace().authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) { |
151 | completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, { }); |
152 | return; |
153 | } |
154 | auto weakThis = makeWeakPtr(*this); |
155 | completionHandler(AuthenticationChallengeDisposition::Cancel, { }); |
156 | if (!weakThis) |
157 | return; |
158 | didFinish(ResourceError { String(), 0, currentURL(), "Failed HTTP authentication"_s , ResourceError::Type::AccessControl }); |
159 | } |
160 | |
161 | void PingLoad::didReceiveResponse(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler) |
162 | { |
163 | RELEASE_LOG_IF_ALLOWED("didReceiveResponse - httpStatusCode: %d" , response.httpStatusCode()); |
164 | auto weakThis = makeWeakPtr(*this); |
165 | completionHandler(PolicyAction::Ignore); |
166 | if (!weakThis) |
167 | return; |
168 | didFinish({ }, response); |
169 | } |
170 | |
171 | void PingLoad::didReceiveData(Ref<SharedBuffer>&&) |
172 | { |
173 | RELEASE_LOG_IF_ALLOWED("didReceiveData" ); |
174 | ASSERT_NOT_REACHED(); |
175 | } |
176 | |
177 | void PingLoad::didCompleteWithError(const ResourceError& error, const NetworkLoadMetrics&) |
178 | { |
179 | if (error.isNull()) |
180 | RELEASE_LOG_IF_ALLOWED("didComplete" ); |
181 | else |
182 | RELEASE_LOG_IF_ALLOWED("didCompleteWithError, error_code: %d" , error.errorCode()); |
183 | |
184 | didFinish(error); |
185 | } |
186 | |
187 | void PingLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) |
188 | { |
189 | } |
190 | |
191 | void PingLoad::wasBlocked() |
192 | { |
193 | RELEASE_LOG_IF_ALLOWED("wasBlocked" ); |
194 | didFinish(blockedError(ResourceRequest { currentURL() })); |
195 | } |
196 | |
197 | void PingLoad::cannotShowURL() |
198 | { |
199 | RELEASE_LOG_IF_ALLOWED("cannotShowURL" ); |
200 | didFinish(cannotShowURLError(ResourceRequest { currentURL() })); |
201 | } |
202 | |
203 | void PingLoad::wasBlockedByRestrictions() |
204 | { |
205 | RELEASE_LOG_IF_ALLOWED("wasBlockedByRestrictions" ); |
206 | didFinish(wasBlockedByRestrictionsError(ResourceRequest { currentURL() })); |
207 | } |
208 | |
209 | void PingLoad::timeoutTimerFired() |
210 | { |
211 | RELEASE_LOG_IF_ALLOWED("timeoutTimerFired" ); |
212 | didFinish(ResourceError { String(), 0, currentURL(), "Load timed out"_s , ResourceError::Type::Timeout }); |
213 | } |
214 | |
215 | const URL& PingLoad::currentURL() const |
216 | { |
217 | return m_networkLoadChecker->url(); |
218 | } |
219 | |
220 | } // namespace WebKit |
221 | |
222 | #undef RELEASE_LOG_IF_ALLOWED |
223 | |