ecdbf11a5b22239c952aa600dfef67676e060f08
[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() = default;
51
52 bool ResourceHandle::start()
53 {
54     ASSERT(isMainThread());
55
56     CurlContext::singleton();
57
58     // The frame could be null if the ResourceHandle is not associated to any
59     // Frame, e.g. if we are downloading a file.
60     // If the frame is not null but the page is null this must be an attempted
61     // load from an unload handler, so let's just block it.
62     // If both the frame and the page are not null the context is valid.
63     if (d->m_context && !d->m_context->isValid())
64         return false;
65
66     // Only allow the POST and GET methods for non-HTTP requests.
67     const ResourceRequest& request = firstRequest();
68     if (!request.url().protocolIsInHTTPFamily() && request.httpMethod() != "GET" && request.httpMethod() != "POST") {
69         scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
70         return true;
71     }
72
73     d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(this));
74     return d->m_delegate->start();
75 }
76
77 void ResourceHandle::cancel()
78 {
79     if (d->m_delegate)
80         d->m_delegate->cancel();
81 }
82
83 #if OS(WINDOWS)
84
85 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
86 {
87     ASSERT(isMainThread());
88
89     CurlContext::singleton().sslHandle().setHostAllowsAnyHTTPSCertificate(host);
90 }
91
92 void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key)
93 {
94     ASSERT(isMainThread());
95
96     if (fileExists(certificate))
97         CurlContext::singleton().sslHandle().setClientCertificateInfo(host, certificate, key);
98     else
99         LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data());
100 }
101
102 #endif
103
104 #if OS(WINDOWS) && USE(CF)
105
106 void ResourceHandle::setClientCertificate(const String&, CFDataRef)
107 {
108 }
109
110 #endif
111
112 void ResourceHandle::platformSetDefersLoading(bool defers)
113 {
114     ASSERT(isMainThread());
115
116     if (d->m_delegate)
117         d->m_delegate->setDefersLoading(defers);
118 }
119
120 bool ResourceHandle::shouldUseCredentialStorage()
121 {
122     return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
123 }
124
125 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
126 {
127     ASSERT(isMainThread());
128
129     String partition = firstRequest().cachePartition();
130
131     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
132         Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone);
133
134         URL urlToStore;
135         if (challenge.failureResponse().httpStatusCode() == 401)
136             urlToStore = challenge.failureResponse().url();
137         CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
138         
139         if (d->m_delegate)
140             d->m_delegate->setAuthentication(credential.user(), credential.password());
141
142         d->m_user = String();
143         d->m_pass = String();
144         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
145         return;
146     }
147
148     if (shouldUseCredentialStorage()) {
149         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
150             // The stored credential wasn't accepted, stop using it.
151             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
152             // but the observable effect should be very minor, if any.
153             CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace());
154         }
155
156         if (!challenge.previousFailureCount()) {
157             Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace());
158             if (!credential.isEmpty() && credential != d->m_initialCredential) {
159                 ASSERT(credential.persistence() == CredentialPersistenceNone);
160                 if (challenge.failureResponse().httpStatusCode() == 401) {
161                     // Store the credential back, possibly adding it as a default for this directory.
162                     CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
163                 }
164
165                 if (d->m_delegate)
166                     d->m_delegate->setAuthentication(credential.user(), credential.password());
167                 return;
168             }
169         }
170     }
171
172     d->m_currentWebChallenge = challenge;
173
174     if (client()) {
175         auto protectedThis = makeRef(*this);
176         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
177     }
178 }
179
180 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
181 {
182     ASSERT(isMainThread());
183
184     if (challenge != d->m_currentWebChallenge)
185         return;
186
187     if (credential.isEmpty()) {
188         receivedRequestToContinueWithoutCredential(challenge);
189         return;
190     }
191
192     String partition = firstRequest().cachePartition();
193
194     if (shouldUseCredentialStorage()) {
195         if (challenge.failureResponse().httpStatusCode() == 401) {
196             URL urlToStore = challenge.failureResponse().url();
197             CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
198         }
199     }
200
201     if (d->m_delegate)
202         d->m_delegate->setAuthentication(credential.user(), credential.password());
203
204     clearAuthentication();
205 }
206
207 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
208 {
209     ASSERT(isMainThread());
210
211     if (challenge != d->m_currentWebChallenge)
212         return;
213
214     clearAuthentication();
215
216     auto protectedThis = makeRef(*this);
217     didReceiveResponse(ResourceResponse(d->m_response));
218 }
219
220 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
221 {
222     ASSERT(isMainThread());
223
224     if (challenge != d->m_currentWebChallenge)
225         return;
226
227     if (client()) {
228         auto protectedThis = makeRef(*this);
229         client()->receivedCancellation(this, challenge);
230     }
231 }
232
233 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
234 {
235     ASSERT_NOT_REACHED();
236 }
237
238 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&)
239 {
240     ASSERT_NOT_REACHED();
241 }
242
243 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
244 {
245     ASSERT(isMainThread());
246
247     SynchronousLoaderClient client;
248     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
249
250     handle->d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(handle.get()));
251     handle->d->m_delegate->dispatchSynchronousJob();
252
253     error = client.error();
254     data.swap(client.mutableData());
255     response = client.response();
256 }
257
258 void ResourceHandle::continueDidReceiveResponse()
259 {
260     ASSERT(isMainThread());
261
262     if (d->m_delegate)
263         d->m_delegate->continueDidReceiveResponse();
264 }
265
266 void ResourceHandle::platformContinueSynchronousDidReceiveResponse()
267 {
268     ASSERT(isMainThread());
269
270     if (d->m_delegate)
271         d->m_delegate->platformContinueSynchronousDidReceiveResponse();
272 }
273
274 void ResourceHandle::continueWillSendRequest(ResourceRequest&& request)
275 {
276     ASSERT(isMainThread());
277     ASSERT(!client() || client()->usesAsyncCallbacks());
278
279     if (d->m_delegate)
280         d->m_delegate->continueWillSendRequest(WTFMove(request));
281 }
282
283 } // namespace WebCore
284
285 #endif