Use asynchronous ResourceHandleClient calls for WebKit1
[WebKit-https.git] / Source / WebCore / platform / network / curl / ResourceHandleCurl.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Inc.  All rights reserved.
3  * Copyright (C) 2005, 2006 Michael Emmel mike.emmel@gmail.com
4  * Copyright (C) 2017 Sony Interactive Entertainment Inc.
5  * All rights reserved.
6  * Copyright (C) 2017 NAVER Corp.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "ResourceHandle.h"
32
33 #if USE(CURL)
34
35 #include "CredentialStorage.h"
36 #include "CurlContext.h"
37 #include "FileSystem.h"
38 #include "Logging.h"
39 #include "ResourceHandleInternal.h"
40 #include "SynchronousLoaderClient.h"
41
42 namespace WebCore {
43
44 ResourceHandleInternal::~ResourceHandleInternal()
45 {
46     if (m_delegate)
47         m_delegate->releaseHandle();
48 }
49
50 ResourceHandle::~ResourceHandle()
51 {
52 }
53
54 bool ResourceHandle::start()
55 {
56     ASSERT(isMainThread());
57
58     CurlContext::singleton();
59
60     // The frame could be null if the ResourceHandle is not associated to any
61     // Frame, e.g. if we are downloading a file.
62     // If the frame is not null but the page is null this must be an attempted
63     // load from an unload handler, so let's just block it.
64     // If both the frame and the page are not null the context is valid.
65     if (d->m_context && !d->m_context->isValid())
66         return false;
67
68     // Only allow the POST and GET methods for non-HTTP requests.
69     const ResourceRequest& request = firstRequest();
70     if (!request.url().protocolIsInHTTPFamily() && request.httpMethod() != "GET" && request.httpMethod() != "POST") {
71         scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
72         return true;
73     }
74
75     d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(this));
76     return d->m_delegate->start();
77 }
78
79 void ResourceHandle::cancel()
80 {
81     if (d->m_delegate)
82         d->m_delegate->cancel();
83 }
84
85 #if OS(WINDOWS)
86
87 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
88 {
89     ASSERT(isMainThread());
90
91     CurlContext::singleton().sslHandle().setHostAllowsAnyHTTPSCertificate(host);
92 }
93
94 void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key)
95 {
96     ASSERT(isMainThread());
97
98     if (fileExists(certificate))
99         CurlContext::singleton().sslHandle().setClientCertificateInfo(host, certificate, key);
100     else
101         LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data());
102 }
103
104 #endif
105
106 #if OS(WINDOWS) && USE(CF)
107
108 void ResourceHandle::setClientCertificate(const String&, CFDataRef)
109 {
110 }
111
112 #endif
113
114 void ResourceHandle::platformSetDefersLoading(bool defers)
115 {
116     ASSERT(isMainThread());
117
118     if (d->m_delegate)
119         d->m_delegate->setDefersLoading(defers);
120 }
121
122 bool ResourceHandle::shouldUseCredentialStorage()
123 {
124     return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
125 }
126
127 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
128 {
129     ASSERT(isMainThread());
130
131     String partition = firstRequest().cachePartition();
132
133     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
134         Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone);
135
136         URL urlToStore;
137         if (challenge.failureResponse().httpStatusCode() == 401)
138             urlToStore = challenge.failureResponse().url();
139         CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
140         
141         if (d->m_delegate)
142             d->m_delegate->setAuthentication(credential.user(), credential.password());
143
144         d->m_user = String();
145         d->m_pass = String();
146         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
147         return;
148     }
149
150     if (shouldUseCredentialStorage()) {
151         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
152             // The stored credential wasn't accepted, stop using it.
153             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
154             // but the observable effect should be very minor, if any.
155             CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace());
156         }
157
158         if (!challenge.previousFailureCount()) {
159             Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace());
160             if (!credential.isEmpty() && credential != d->m_initialCredential) {
161                 ASSERT(credential.persistence() == CredentialPersistenceNone);
162                 if (challenge.failureResponse().httpStatusCode() == 401) {
163                     // Store the credential back, possibly adding it as a default for this directory.
164                     CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
165                 }
166
167                 if (d->m_delegate)
168                     d->m_delegate->setAuthentication(credential.user(), credential.password());
169                 return;
170             }
171         }
172     }
173
174     d->m_currentWebChallenge = challenge;
175
176     if (client()) {
177         auto protectedThis = makeRef(*this);
178         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
179     }
180 }
181
182 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
183 {
184     ASSERT(isMainThread());
185
186     if (challenge != d->m_currentWebChallenge)
187         return;
188
189     if (credential.isEmpty()) {
190         receivedRequestToContinueWithoutCredential(challenge);
191         return;
192     }
193
194     String partition = firstRequest().cachePartition();
195
196     if (shouldUseCredentialStorage()) {
197         if (challenge.failureResponse().httpStatusCode() == 401) {
198             URL urlToStore = challenge.failureResponse().url();
199             CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
200         }
201     }
202
203     if (d->m_delegate)
204         d->m_delegate->setAuthentication(credential.user(), credential.password());
205
206     clearAuthentication();
207 }
208
209 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
210 {
211     ASSERT(isMainThread());
212
213     if (challenge != d->m_currentWebChallenge)
214         return;
215
216     clearAuthentication();
217
218     auto protectedThis = makeRef(*this);
219     didReceiveResponse(ResourceResponse(d->m_response));
220 }
221
222 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
223 {
224     ASSERT(isMainThread());
225
226     if (challenge != d->m_currentWebChallenge)
227         return;
228
229     if (client()) {
230         auto protectedThis = makeRef(*this);
231         client()->receivedCancellation(this, challenge);
232     }
233 }
234
235 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
236 {
237     ASSERT_NOT_REACHED();
238 }
239
240 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&)
241 {
242     ASSERT_NOT_REACHED();
243 }
244
245 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
246 {
247     ASSERT(isMainThread());
248
249     SynchronousLoaderClient client;
250     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
251
252     handle->d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(handle.get()));
253     handle->d->m_delegate->dispatchSynchronousJob();
254
255     error = client.error();
256     data.swap(client.mutableData());
257     response = client.response();
258 }
259
260 void ResourceHandle::continueDidReceiveResponse()
261 {
262     ASSERT(isMainThread());
263
264     if (d->m_delegate)
265         d->m_delegate->continueDidReceiveResponse();
266 }
267
268 void ResourceHandle::platformContinueSynchronousDidReceiveResponse()
269 {
270     ASSERT(isMainThread());
271
272     if (d->m_delegate)
273         d->m_delegate->platformContinueSynchronousDidReceiveResponse();
274 }
275
276 void ResourceHandle::continueWillSendRequest(ResourceRequest&& request)
277 {
278     ASSERT(isMainThread());
279
280     if (d->m_delegate)
281         d->m_delegate->continueWillSendRequest(WTFMove(request));
282 }
283
284 } // namespace WebCore
285
286 #endif