d54b12c92c8a1110faa4d50ed215b36c71e1f562
[WebKit-https.git] / Source / WebCore / platform / network / curl / CurlContext.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc.  All rights reserved.
3  * Copyright (C) 2017 Sony Interactive Entertainment Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "CurlContext.h"
29
30 #if USE(CURL)
31 #include "HTTPHeaderMap.h"
32 #include <NetworkLoadMetrics.h>
33 #include <wtf/MainThread.h>
34 #include <wtf/NeverDestroyed.h>
35 #include <wtf/text/CString.h>
36
37 #if OS(WINDOWS)
38 #include "WebCoreBundleWin.h"
39 #include <shlobj.h>
40 #include <shlwapi.h>
41 #endif
42
43 namespace WebCore {
44
45 static CString cookieJarPath()
46 {
47     char* cookieJarPath = getenv("CURL_COOKIE_JAR_PATH");
48     if (cookieJarPath)
49         return cookieJarPath;
50
51 #if OS(WINDOWS)
52     char executablePath[MAX_PATH];
53     char appDataDirectory[MAX_PATH];
54     char cookieJarFullPath[MAX_PATH];
55     char cookieJarDirectory[MAX_PATH];
56
57     if (FAILED(::SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, appDataDirectory))
58         || FAILED(::GetModuleFileNameA(0, executablePath, MAX_PATH)))
59         return "cookies.dat";
60
61     ::PathRemoveExtensionA(executablePath);
62     LPSTR executableName = ::PathFindFileNameA(executablePath);
63     sprintf_s(cookieJarDirectory, MAX_PATH, "%s/%s", appDataDirectory, executableName);
64     sprintf_s(cookieJarFullPath, MAX_PATH, "%s/cookies.dat", cookieJarDirectory);
65
66     if (::SHCreateDirectoryExA(0, cookieJarDirectory, 0) != ERROR_SUCCESS
67         && ::GetLastError() != ERROR_FILE_EXISTS
68         && ::GetLastError() != ERROR_ALREADY_EXISTS)
69         return "cookies.dat";
70
71     return cookieJarFullPath;
72 #else
73     return "cookies.dat";
74 #endif
75 }
76
77 // CurlContext -------------------------------------------------------------------
78
79 CurlContext& CurlContext::singleton()
80 {
81     static NeverDestroyed<CurlContext> sharedInstance;
82     return sharedInstance;
83 }
84
85 CurlContext::CurlContext()
86 : m_cookieJarFileName { cookieJarPath() }
87 , m_cookieJar { std::make_unique<CookieJarCurlFileSystem>() }
88 {
89     initCookieSession();
90
91 #ifndef NDEBUG
92     m_verbose = getenv("DEBUG_CURL");
93
94     char* logFile = getenv("CURL_LOG_FILE");
95     if (logFile)
96         m_logFile = fopen(logFile, "a");
97 #endif
98 }
99
100 CurlContext::~CurlContext()
101 {
102 #ifndef NDEBUG
103     if (m_logFile)
104         fclose(m_logFile);
105 #endif
106 }
107
108 // Cookie =======================
109
110 void CurlContext::initCookieSession()
111 {
112     // Curl saves both persistent cookies, and session cookies to the cookie file.
113     // The session cookies should be deleted before starting a new session.
114
115     CURL* curl = curl_easy_init();
116
117     if (!curl)
118         return;
119
120     curl_easy_setopt(curl, CURLOPT_SHARE, m_shareHandle.handle());
121
122     if (!m_cookieJarFileName.isNull()) {
123         curl_easy_setopt(curl, CURLOPT_COOKIEFILE, m_cookieJarFileName.data());
124         curl_easy_setopt(curl, CURLOPT_COOKIEJAR, m_cookieJarFileName.data());
125     }
126
127     curl_easy_setopt(curl, CURLOPT_COOKIESESSION, 1);
128
129     curl_easy_cleanup(curl);
130 }
131
132 // Proxy =======================
133
134 const String CurlContext::ProxyInfo::url() const
135 {
136     String userPass;
137     if (username.length() || password.length())
138         userPass = username + ":" + password + "@";
139
140     return String("http://") + userPass + host + ":" + String::number(port);
141 }
142
143 void CurlContext::setProxyInfo(const String& host,
144     unsigned long port,
145     CurlProxyType type,
146     const String& username,
147     const String& password)
148 {
149     ProxyInfo info;
150
151     info.host = host;
152     info.port = port;
153     info.type = type;
154     info.username = username;
155     info.password = password;
156
157     setProxyInfo(info);
158 }
159
160 bool CurlContext::isHttp2Enabled() const
161 {
162     curl_version_info_data* data = curl_version_info(CURLVERSION_NOW);
163     return data->features & CURL_VERSION_HTTP2;
164 }
165
166 // CurlShareHandle --------------------------------------------
167
168 CurlShareHandle::CurlShareHandle()
169 {
170     m_shareHandle = curl_share_init();
171     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
172     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
173     curl_share_setopt(m_shareHandle, CURLSHOPT_LOCKFUNC, lockCallback);
174     curl_share_setopt(m_shareHandle, CURLSHOPT_UNLOCKFUNC, unlockCallback);
175 }
176
177 CurlShareHandle::~CurlShareHandle()
178 {
179     if (m_shareHandle)
180         curl_share_cleanup(m_shareHandle);
181 }
182
183 void CurlShareHandle::lockCallback(CURL*, curl_lock_data data, curl_lock_access, void*)
184 {
185     if (auto* mutex = mutexFor(data))
186         mutex->lock();
187 }
188
189 void CurlShareHandle::unlockCallback(CURL*, curl_lock_data data, void*)
190 {
191     if (auto* mutex = mutexFor(data))
192         mutex->unlock();
193 }
194
195 StaticLock* CurlShareHandle::mutexFor(curl_lock_data data)
196 {
197     static StaticLock cookieMutex;
198     static StaticLock dnsMutex;
199     static StaticLock shareMutex;
200
201     switch (data) {
202     case CURL_LOCK_DATA_COOKIE:
203         return &cookieMutex;
204     case CURL_LOCK_DATA_DNS:
205         return &dnsMutex;
206     case CURL_LOCK_DATA_SHARE:
207         return &shareMutex;
208     default:
209         ASSERT_NOT_REACHED();
210         return nullptr;
211     }
212 }
213
214 // CurlMultiHandle --------------------------------------------
215
216 CurlMultiHandle::CurlMultiHandle()
217 {
218     m_multiHandle = curl_multi_init();
219 }
220
221 CurlMultiHandle::~CurlMultiHandle()
222 {
223     if (m_multiHandle)
224         curl_multi_cleanup(m_multiHandle);
225 }
226
227 CURLMcode CurlMultiHandle::addHandle(CURL* handle)
228 {
229     return curl_multi_add_handle(m_multiHandle, handle);
230 }
231
232 CURLMcode CurlMultiHandle::removeHandle(CURL* handle)
233 {
234     return curl_multi_remove_handle(m_multiHandle, handle);
235 }
236
237 CURLMcode CurlMultiHandle::getFdSet(fd_set& readFdSet, fd_set& writeFdSet, fd_set& excFdSet, int& maxFd)
238 {
239     FD_ZERO(&readFdSet);
240     FD_ZERO(&writeFdSet);
241     FD_ZERO(&excFdSet);
242     maxFd = 0;
243
244     return curl_multi_fdset(m_multiHandle, &readFdSet, &writeFdSet, &excFdSet, &maxFd);
245 }
246
247 CURLMcode CurlMultiHandle::perform(int& runningHandles)
248 {
249     return curl_multi_perform(m_multiHandle, &runningHandles);
250 }
251
252 CURLMsg* CurlMultiHandle::readInfo(int& messagesInQueue)
253 {
254     return curl_multi_info_read(m_multiHandle, &messagesInQueue);
255 }
256
257 // CurlHandle -------------------------------------------------
258
259 CurlHandle::CurlHandle()
260 {
261     m_handle = curl_easy_init();
262 }
263
264 CurlHandle::~CurlHandle()
265 {
266     if (m_handle)
267         curl_easy_cleanup(m_handle);
268 }
269
270 const String CurlHandle::errorDescription(CURLcode errorCode)
271 {
272     return String(curl_easy_strerror(errorCode));
273 }
274
275 void CurlHandle::initialize()
276 {
277     curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
278 }
279
280 CURLcode CurlHandle::perform()
281 {
282     return curl_easy_perform(m_handle);
283 }
284
285 CURLcode CurlHandle::pause(int bitmask)
286 {
287     return curl_easy_pause(m_handle, bitmask);
288 }
289
290 void CurlHandle::enableShareHandle()
291 {
292     curl_easy_setopt(m_handle, CURLOPT_SHARE, CurlContext::singleton().shareHandle().handle());
293 }
294
295 void CurlHandle::setUrl(const URL& url)
296 {
297     URL curlUrl = url;
298
299     // Remove any fragment part, otherwise curl will send it as part of the request.
300     curlUrl.removeFragmentIdentifier();
301
302     // Remove any query part sent to a local file.
303     if (curlUrl.isLocalFile()) {
304         // By setting the query to a null string it'll be removed.
305         if (!curlUrl.query().isEmpty())
306             curlUrl.setQuery(String());
307     }
308
309     // url is in ASCII so latin1() will only convert it to char* without character translation.
310     curl_easy_setopt(m_handle, CURLOPT_URL, curlUrl.string().latin1().data());
311 }
312
313 void CurlHandle::appendRequestHeaders(const HTTPHeaderMap& headers)
314 {
315     if (headers.size()) {
316         for (auto& entry : headers) {
317             auto& value = entry.value;
318             appendRequestHeader(entry.key, entry.value);
319         }
320     }
321 }
322
323 void CurlHandle::appendRequestHeader(const String& name, const String& value)
324 {
325     String header(name);
326
327     if (value.isEmpty()) {
328         // Insert the ; to tell curl that this header has an empty value.
329         header.append(";");
330     } else {
331         header.append(": ");
332         header.append(value);
333     }
334
335     appendRequestHeader(header);
336 }
337
338 void CurlHandle::removeRequestHeader(const String& name)
339 {
340     // Add a header with no content, the internally used header will get disabled. 
341     String header(name);
342     header.append(":");
343
344     appendRequestHeader(header);
345 }
346
347 void CurlHandle::appendRequestHeader(const String& header)
348 {
349     bool needToEnable = m_requestHeaders.isEmpty();
350
351     m_requestHeaders.append(header);
352
353     if (needToEnable)
354         enableRequestHeaders();
355 }
356
357 void CurlHandle::enableRequestHeaders()
358 {
359     if (m_requestHeaders.isEmpty())
360         return;
361
362     const struct curl_slist* headers = m_requestHeaders.head();
363     curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers);
364 }
365
366 void CurlHandle::enableHttp()
367 {
368     if (CurlContext::singleton().isHttp2Enabled()) {
369         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
370         curl_easy_setopt(m_handle, CURLOPT_PIPEWAIT, 1L);
371         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_ALPN, 1L);
372         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_NPN, 0L);
373     } else
374         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
375 }
376
377 void CurlHandle::enableHttpGetRequest()
378 {
379     enableHttp();
380     curl_easy_setopt(m_handle, CURLOPT_HTTPGET, 1L);
381 }
382
383 void CurlHandle::enableHttpHeadRequest()
384 {
385     enableHttp();
386     curl_easy_setopt(m_handle, CURLOPT_NOBODY, 1L);
387 }
388
389 void CurlHandle::enableHttpPostRequest()
390 {
391     enableHttp();
392     curl_easy_setopt(m_handle, CURLOPT_POST, 1L);
393     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, 0L);
394 }
395
396 void CurlHandle::setPostFields(const char* data, long size)
397 {
398     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDS, data);
399     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, size);
400 }
401
402 void CurlHandle::setPostFieldLarge(curl_off_t size)
403 {
404     if (expectedSizeOfCurlOffT() != sizeof(long long))
405         size = static_cast<int>(size);
406
407     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE_LARGE, size);
408 }
409
410 void CurlHandle::enableHttpPutRequest()
411 {
412     enableHttp();
413     curl_easy_setopt(m_handle, CURLOPT_UPLOAD, 1L);
414     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE, 0L);
415 }
416
417 void CurlHandle::setInFileSizeLarge(curl_off_t size)
418 {
419     if (expectedSizeOfCurlOffT() != sizeof(long long))
420         size = static_cast<int>(size);
421
422     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE_LARGE, size);
423 }
424
425 void CurlHandle::setHttpCustomRequest(const String& method)
426 {
427     enableHttp();
428     curl_easy_setopt(m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
429 }
430
431 void CurlHandle::enableAcceptEncoding()
432 {
433     // enable all supported built-in compressions (gzip and deflate) through Accept-Encoding:
434     curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
435 }
436
437 void CurlHandle::enableAllowedProtocols()
438 {
439     static const long allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
440
441     curl_easy_setopt(m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
442 }
443
444 void CurlHandle::enableHttpAuthentication(long option)
445 {
446     curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, option);
447 }
448
449 void CurlHandle::setHttpAuthUserPass(const String& user, const String& password)
450 {
451     curl_easy_setopt(m_handle, CURLOPT_USERNAME, user.utf8().data());
452     curl_easy_setopt(m_handle, CURLOPT_PASSWORD, password.utf8().data());
453 }
454
455 void CurlHandle::setCACertPath(const char* path)
456 {
457     if (path)
458         curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
459 }
460
461 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
462 {
463     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, static_cast<long>(verifyPeer));
464 }
465
466 void CurlHandle::setSslVerifyHost(VerifyHost verifyHost)
467 {
468     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, static_cast<long>(verifyHost));
469 }
470
471 void CurlHandle::setSslCert(const char* cert)
472 {
473     curl_easy_setopt(m_handle, CURLOPT_SSLCERT, cert);
474 }
475
476 void CurlHandle::setSslCertType(const char* type)
477 {
478     curl_easy_setopt(m_handle, CURLOPT_SSLCERTTYPE, type);
479 }
480
481 void CurlHandle::setSslKeyPassword(const char* password)
482 {
483     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
484 }
485
486 void CurlHandle::enableCookieJarIfExists()
487 {
488     const char* cookieJar = CurlContext::singleton().getCookieJarFileName();
489     if (cookieJar)
490         curl_easy_setopt(m_handle, CURLOPT_COOKIEJAR, cookieJar);
491 }
492
493 void CurlHandle::setCookieList(const char* cookieList)
494 {
495     if (!cookieList)
496         return;
497
498     curl_easy_setopt(m_handle, CURLOPT_COOKIELIST, cookieList);
499 }
500
501 void CurlHandle::fetchCookieList(CurlSList &cookies) const
502 {
503     curl_easy_getinfo(m_handle, CURLINFO_COOKIELIST, static_cast<struct curl_slist**>(cookies));
504 }
505
506 void CurlHandle::enableProxyIfExists()
507 {
508     auto& proxy = CurlContext::singleton().proxyInfo();
509
510     if (proxy.type != CurlProxyType::Invalid) {
511         curl_easy_setopt(m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
512         curl_easy_setopt(m_handle, CURLOPT_PROXYTYPE, proxy.type);
513     }
514 }
515
516 void CurlHandle::enableTimeout()
517 {
518     static const long dnsCacheTimeout = 5 * 60; // [sec.]
519
520     curl_easy_setopt(m_handle, CURLOPT_DNS_CACHE_TIMEOUT, dnsCacheTimeout);
521 }
522
523 void CurlHandle::setTimeout(long timeoutMilliseconds)
524 {
525     curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, timeoutMilliseconds);
526 }
527
528 void CurlHandle::setHeaderCallbackFunction(curl_write_callback callbackFunc, void* userData)
529 {
530     curl_easy_setopt(m_handle, CURLOPT_HEADERFUNCTION, callbackFunc);
531     curl_easy_setopt(m_handle, CURLOPT_HEADERDATA, userData);
532 }
533
534 void CurlHandle::setWriteCallbackFunction(curl_write_callback callbackFunc, void* userData)
535 {
536     curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, callbackFunc);
537     curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, userData);
538 }
539
540 void CurlHandle::setReadCallbackFunction(curl_read_callback callbackFunc, void* userData)
541 {
542     curl_easy_setopt(m_handle, CURLOPT_READFUNCTION, callbackFunc);
543     curl_easy_setopt(m_handle, CURLOPT_READDATA, userData);
544 }
545
546 void CurlHandle::setSslCtxCallbackFunction(curl_ssl_ctx_callback callbackFunc, void* userData)
547 {
548     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_DATA, userData);
549     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
550 }
551
552 std::optional<uint16_t> CurlHandle::getPrimaryPort()
553 {
554     if (!m_handle)
555         return std::nullopt;
556
557     long port;
558     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_PORT, &port);
559     if (errorCode != CURLE_OK)
560         return std::nullopt;
561
562     /*
563      * https://github.com/curl/curl/blob/master/lib/connect.c#L612-L660
564      * confirmed that `port` is originally unsigned short.
565      */
566     return static_cast<uint16_t>(port);
567 }
568
569 std::optional<long> CurlHandle::getResponseCode()
570 {
571     if (!m_handle)
572         return std::nullopt;
573
574     long responseCode;
575     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &responseCode);
576     if (errorCode != CURLE_OK)
577         return std::nullopt;
578
579     return responseCode;
580 }
581
582 std::optional<long> CurlHandle::getHttpConnectCode()
583 {
584     if (!m_handle)
585         return std::nullopt;
586
587     long httpConnectCode;
588     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_CONNECTCODE, &httpConnectCode);
589     if (errorCode != CURLE_OK)
590         return std::nullopt;
591
592     return httpConnectCode;
593 }
594
595 std::optional<long long> CurlHandle::getContentLength()
596 {
597     if (!m_handle)
598         return std::nullopt;
599
600     double contentLength;
601
602     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
603     if (errorCode != CURLE_OK)
604         return std::nullopt;
605
606     return static_cast<long long>(contentLength);
607 }
608
609 std::optional<long> CurlHandle::getHttpAuthAvail()
610 {
611     if (!m_handle)
612         return std::nullopt;
613
614     long httpAuthAvailable;
615     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTPAUTH_AVAIL, &httpAuthAvailable);
616     if (errorCode != CURLE_OK)
617         return std::nullopt;
618
619     return httpAuthAvailable;
620 }
621
622 std::optional<long> CurlHandle::getHttpVersion()
623 {
624     if (!m_handle)
625         return std::nullopt;
626
627     long version;
628     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
629     if (errorCode != CURLE_OK)
630         return std::nullopt;
631
632     return version;
633 }
634
635 std::optional<NetworkLoadMetrics> CurlHandle::getNetworkLoadMetrics()
636 {
637     double nameLookup = 0.0;
638     double connect = 0.0;
639     double appConnect = 0.0;
640     double startTransfer = 0.0;
641
642     if (!m_handle)
643         return std::nullopt;
644
645     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_NAMELOOKUP_TIME, &nameLookup);
646     if (errorCode != CURLE_OK)
647         return std::nullopt;
648
649     errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONNECT_TIME, &connect);
650     if (errorCode != CURLE_OK)
651         return std::nullopt;
652
653     errorCode = curl_easy_getinfo(m_handle, CURLINFO_APPCONNECT_TIME, &appConnect);
654     if (errorCode != CURLE_OK)
655         return std::nullopt;
656
657     errorCode = curl_easy_getinfo(m_handle, CURLINFO_STARTTRANSFER_TIME, &startTransfer);
658     if (errorCode != CURLE_OK)
659         return std::nullopt;
660
661     NetworkLoadMetrics networkLoadMetrics;
662
663     networkLoadMetrics.domainLookupStart = Seconds(0);
664     networkLoadMetrics.domainLookupEnd = Seconds(nameLookup);
665     networkLoadMetrics.connectStart = Seconds(nameLookup);
666     networkLoadMetrics.connectEnd = Seconds(connect);
667
668     if (appConnect > 0.0) {
669         networkLoadMetrics.secureConnectionStart = Seconds(connect);
670         networkLoadMetrics.connectEnd = Seconds(appConnect);
671     }
672
673     networkLoadMetrics.requestStart = networkLoadMetrics.connectEnd;
674     networkLoadMetrics.responseStart = Seconds(startTransfer);
675
676     return networkLoadMetrics;
677 }
678
679 long long CurlHandle::maxCurlOffT()
680 {
681     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT() * 8 - 1)) - 1;
682
683     return maxCurlOffT;
684 }
685
686 int CurlHandle::expectedSizeOfCurlOffT()
687 {
688     // The size of a curl_off_t could be different in WebKit and in cURL depending on
689     // compilation flags of both.
690     static int expectedSizeOfCurlOffT = 0;
691     if (!expectedSizeOfCurlOffT) {
692         curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
693         if (infoData->features & CURL_VERSION_LARGEFILE)
694             expectedSizeOfCurlOffT = sizeof(long long);
695         else
696             expectedSizeOfCurlOffT = sizeof(int);
697     }
698
699     return expectedSizeOfCurlOffT;
700 }
701
702 #ifndef NDEBUG
703
704 void CurlHandle::enableVerboseIfUsed()
705 {
706     if (CurlContext::singleton().isVerbose())
707         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
708 }
709
710 void CurlHandle::enableStdErrIfUsed()
711 {
712     if (CurlContext::singleton().getLogFile())
713         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
714 }
715
716 #endif
717
718 }
719
720 #endif