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