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