[WTF] Remove StaticLock
[WebKit.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 "CurlRequestScheduler.h"
32 #include "HTTPHeaderMap.h"
33 #include <NetworkLoadMetrics.h>
34 #include <mutex>
35 #include <wtf/MainThread.h>
36 #include <wtf/NeverDestroyed.h>
37 #include <wtf/text/CString.h>
38
39 #if OS(WINDOWS)
40 #include "WebCoreBundleWin.h"
41 #include <shlobj.h>
42 #include <shlwapi.h>
43 #endif
44
45 namespace WebCore {
46
47 class EnvironmentVariableReader {
48 public:
49     const char* read(const char* name) { return ::getenv(name); }
50     bool defined(const char* name) { return read(name) != nullptr; }
51
52     template<typename T> std::optional<T> readAs(const char* name)
53     {
54         if (const char* valueStr = read(name)) {
55             T value;
56             if (sscanf(valueStr, sscanTemplate<T>(), &value) == 1)
57                 return value;
58         }
59
60         return std::nullopt;
61     }
62
63 private:
64     template<typename T> const char* sscanTemplate()
65     {
66         ASSERT_NOT_REACHED();
67         return nullptr;
68     }
69
70     // define specialized member function for specific type.
71     template<> constexpr const char* sscanTemplate<signed>() { return "%d"; }
72     template<> constexpr const char* sscanTemplate<unsigned>() { return "%u"; }
73 };
74
75 // CurlContext -------------------------------------------------------------------
76
77 CurlContext& CurlContext::singleton()
78 {
79     static NeverDestroyed<CurlContext> sharedInstance;
80     return sharedInstance;
81 }
82
83 CurlContext::CurlContext()
84 {
85     initShareHandle();
86
87     EnvironmentVariableReader envVar;
88
89     if (auto value = envVar.readAs<unsigned>("WEBKIT_CURL_DNS_CACHE_TIMEOUT"))
90         m_dnsCacheTimeout = Seconds(*value);
91
92     if (auto value = envVar.readAs<unsigned>("WEBKIT_CURL_CONNECT_TIMEOUT"))
93         m_connectTimeout = Seconds(*value);
94
95     long maxConnects { CurlDefaultMaxConnects };
96     long maxTotalConnections { CurlDefaultMaxTotalConnections };
97     long maxHostConnections { CurlDefaultMaxHostConnections };
98
99     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAXCONNECTS"))
100         maxConnects = *value;
101
102     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAX_TOTAL_CONNECTIONS"))
103         maxTotalConnections = *value;
104
105     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAX_HOST_CONNECTIONS"))
106         maxHostConnections = *value;
107
108     m_scheduler = std::make_unique<CurlRequestScheduler>(maxConnects, maxTotalConnections, maxHostConnections);
109
110 #ifndef NDEBUG
111     m_verbose = envVar.defined("DEBUG_CURL");
112
113     auto logFile = envVar.read("CURL_LOG_FILE");
114     if (logFile)
115         m_logFile = fopen(logFile, "a");
116 #endif
117 }
118
119 CurlContext::~CurlContext()
120 {
121 #ifndef NDEBUG
122     if (m_logFile)
123         fclose(m_logFile);
124 #endif
125 }
126
127 void CurlContext::initShareHandle()
128 {
129     CURL* curl = curl_easy_init();
130
131     if (!curl)
132         return;
133
134     curl_easy_setopt(curl, CURLOPT_SHARE, m_shareHandle.handle());
135
136     curl_easy_cleanup(curl);
137 }
138
139 // Proxy =======================
140
141 const String CurlContext::ProxyInfo::url() const
142 {
143     String userPass;
144     if (username.length() || password.length())
145         userPass = username + ":" + password + "@";
146
147     return String("http://") + userPass + host + ":" + String::number(port);
148 }
149
150 void CurlContext::setProxyInfo(const String& host,
151     unsigned long port,
152     CurlProxyType type,
153     const String& username,
154     const String& password)
155 {
156     ProxyInfo info;
157
158     info.host = host;
159     info.port = port;
160     info.type = type;
161     info.username = username;
162     info.password = password;
163
164     setProxyInfo(info);
165 }
166
167 bool CurlContext::isHttp2Enabled() const
168 {
169     curl_version_info_data* data = curl_version_info(CURLVERSION_NOW);
170     return data->features & CURL_VERSION_HTTP2;
171 }
172
173 // CurlShareHandle --------------------------------------------
174
175 CurlShareHandle::CurlShareHandle()
176 {
177     m_shareHandle = curl_share_init();
178     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
179     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
180     curl_share_setopt(m_shareHandle, CURLSHOPT_LOCKFUNC, lockCallback);
181     curl_share_setopt(m_shareHandle, CURLSHOPT_UNLOCKFUNC, unlockCallback);
182 }
183
184 CurlShareHandle::~CurlShareHandle()
185 {
186     if (m_shareHandle)
187         curl_share_cleanup(m_shareHandle);
188 }
189
190 void CurlShareHandle::lockCallback(CURL*, curl_lock_data data, curl_lock_access, void*)
191 {
192     if (auto* mutex = mutexFor(data))
193         mutex->lock();
194 }
195
196 void CurlShareHandle::unlockCallback(CURL*, curl_lock_data data, void*)
197 {
198     if (auto* mutex = mutexFor(data))
199         mutex->unlock();
200 }
201
202 Lock* CurlShareHandle::mutexFor(curl_lock_data data)
203 {
204     static Lock cookieMutex;
205     static Lock dnsMutex;
206     static Lock shareMutex;
207
208     switch (data) {
209     case CURL_LOCK_DATA_COOKIE:
210         return &cookieMutex;
211     case CURL_LOCK_DATA_DNS:
212         return &dnsMutex;
213     case CURL_LOCK_DATA_SHARE:
214         return &shareMutex;
215     default:
216         ASSERT_NOT_REACHED();
217         return nullptr;
218     }
219 }
220
221 // CurlMultiHandle --------------------------------------------
222
223 CurlMultiHandle::CurlMultiHandle()
224 {
225     m_multiHandle = curl_multi_init();
226 }
227
228 CurlMultiHandle::~CurlMultiHandle()
229 {
230     if (m_multiHandle)
231         curl_multi_cleanup(m_multiHandle);
232 }
233
234 void CurlMultiHandle::setMaxConnects(long maxConnects)
235 {
236     if (maxConnects < 0)
237         return;
238
239     curl_multi_setopt(m_multiHandle, CURLMOPT_MAXCONNECTS, maxConnects);
240 }
241
242 void CurlMultiHandle::setMaxTotalConnections(long maxTotalConnections)
243 {
244     curl_multi_setopt(m_multiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, maxTotalConnections);
245 }
246
247 void CurlMultiHandle::setMaxHostConnections(long maxHostConnections)
248 {
249     curl_multi_setopt(m_multiHandle, CURLMOPT_MAX_HOST_CONNECTIONS, maxHostConnections);
250 }
251
252 CURLMcode CurlMultiHandle::addHandle(CURL* handle)
253 {
254     return curl_multi_add_handle(m_multiHandle, handle);
255 }
256
257 CURLMcode CurlMultiHandle::removeHandle(CURL* handle)
258 {
259     return curl_multi_remove_handle(m_multiHandle, handle);
260 }
261
262 CURLMcode CurlMultiHandle::getFdSet(fd_set& readFdSet, fd_set& writeFdSet, fd_set& excFdSet, int& maxFd)
263 {
264     FD_ZERO(&readFdSet);
265     FD_ZERO(&writeFdSet);
266     FD_ZERO(&excFdSet);
267     maxFd = 0;
268
269     return curl_multi_fdset(m_multiHandle, &readFdSet, &writeFdSet, &excFdSet, &maxFd);
270 }
271
272 CURLMcode CurlMultiHandle::perform(int& runningHandles)
273 {
274     return curl_multi_perform(m_multiHandle, &runningHandles);
275 }
276
277 CURLMsg* CurlMultiHandle::readInfo(int& messagesInQueue)
278 {
279     return curl_multi_info_read(m_multiHandle, &messagesInQueue);
280 }
281
282 // CurlHandle -------------------------------------------------
283
284 CurlHandle::CurlHandle()
285 {
286     m_handle = curl_easy_init();
287 }
288
289 CurlHandle::~CurlHandle()
290 {
291     if (m_handle)
292         curl_easy_cleanup(m_handle);
293 }
294
295 const String CurlHandle::errorDescription(CURLcode errorCode)
296 {
297     return String(curl_easy_strerror(errorCode));
298 }
299
300 void CurlHandle::initialize()
301 {
302     curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
303 }
304
305 CURLcode CurlHandle::perform()
306 {
307     return curl_easy_perform(m_handle);
308 }
309
310 CURLcode CurlHandle::pause(int bitmask)
311 {
312     return curl_easy_pause(m_handle, bitmask);
313 }
314
315 void CurlHandle::enableShareHandle()
316 {
317     curl_easy_setopt(m_handle, CURLOPT_SHARE, CurlContext::singleton().shareHandle().handle());
318 }
319
320 void CurlHandle::setUrl(const URL& url)
321 {
322     URL curlUrl = url;
323
324     // Remove any fragment part, otherwise curl will send it as part of the request.
325     curlUrl.removeFragmentIdentifier();
326
327     // Remove any query part sent to a local file.
328     if (curlUrl.isLocalFile()) {
329         // By setting the query to a null string it'll be removed.
330         if (!curlUrl.query().isEmpty())
331             curlUrl.setQuery(String());
332     }
333
334     // url is in ASCII so latin1() will only convert it to char* without character translation.
335     curl_easy_setopt(m_handle, CURLOPT_URL, curlUrl.string().latin1().data());
336 }
337
338 void CurlHandle::appendRequestHeaders(const HTTPHeaderMap& headers)
339 {
340     if (headers.size()) {
341         for (auto& entry : headers) {
342             auto& value = entry.value;
343             appendRequestHeader(entry.key, entry.value);
344         }
345     }
346 }
347
348 void CurlHandle::appendRequestHeader(const String& name, const String& value)
349 {
350     String header(name);
351
352     if (value.isEmpty()) {
353         // Insert the ; to tell curl that this header has an empty value.
354         header.append(";");
355     } else {
356         header.append(": ");
357         header.append(value);
358     }
359
360     appendRequestHeader(header);
361 }
362
363 void CurlHandle::removeRequestHeader(const String& name)
364 {
365     // Add a header with no content, the internally used header will get disabled. 
366     String header(name);
367     header.append(":");
368
369     appendRequestHeader(header);
370 }
371
372 void CurlHandle::appendRequestHeader(const String& header)
373 {
374     bool needToEnable = m_requestHeaders.isEmpty();
375
376     m_requestHeaders.append(header);
377
378     if (needToEnable)
379         enableRequestHeaders();
380 }
381
382 void CurlHandle::enableRequestHeaders()
383 {
384     if (m_requestHeaders.isEmpty())
385         return;
386
387     const struct curl_slist* headers = m_requestHeaders.head();
388     curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers);
389 }
390
391 void CurlHandle::enableHttp()
392 {
393     if (CurlContext::singleton().isHttp2Enabled()) {
394         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
395         curl_easy_setopt(m_handle, CURLOPT_PIPEWAIT, 1L);
396         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_ALPN, 1L);
397         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_NPN, 0L);
398     } else
399         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
400 }
401
402 void CurlHandle::enableHttpGetRequest()
403 {
404     enableHttp();
405     curl_easy_setopt(m_handle, CURLOPT_HTTPGET, 1L);
406 }
407
408 void CurlHandle::enableHttpHeadRequest()
409 {
410     enableHttp();
411     curl_easy_setopt(m_handle, CURLOPT_NOBODY, 1L);
412 }
413
414 void CurlHandle::enableHttpPostRequest()
415 {
416     enableHttp();
417     curl_easy_setopt(m_handle, CURLOPT_POST, 1L);
418     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, 0L);
419 }
420
421 void CurlHandle::setPostFields(const char* data, long size)
422 {
423     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDS, data);
424     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, size);
425 }
426
427 void CurlHandle::setPostFieldLarge(curl_off_t size)
428 {
429     if (expectedSizeOfCurlOffT() != sizeof(long long))
430         size = static_cast<int>(size);
431
432     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE_LARGE, size);
433 }
434
435 void CurlHandle::enableHttpPutRequest()
436 {
437     enableHttp();
438     curl_easy_setopt(m_handle, CURLOPT_UPLOAD, 1L);
439     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE, 0L);
440 }
441
442 void CurlHandle::setInFileSizeLarge(curl_off_t size)
443 {
444     if (expectedSizeOfCurlOffT() != sizeof(long long))
445         size = static_cast<int>(size);
446
447     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE_LARGE, size);
448 }
449
450 void CurlHandle::setHttpCustomRequest(const String& method)
451 {
452     enableHttp();
453     curl_easy_setopt(m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
454 }
455
456 void CurlHandle::enableAcceptEncoding()
457 {
458     // enable all supported built-in compressions (gzip and deflate) through Accept-Encoding:
459     curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
460 }
461
462 void CurlHandle::enableAllowedProtocols()
463 {
464     static const long allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
465
466     curl_easy_setopt(m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
467 }
468
469 void CurlHandle::enableHttpAuthentication(long option)
470 {
471     curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, option);
472 }
473
474 void CurlHandle::setHttpAuthUserPass(const String& user, const String& password)
475 {
476     curl_easy_setopt(m_handle, CURLOPT_USERNAME, user.utf8().data());
477     curl_easy_setopt(m_handle, CURLOPT_PASSWORD, password.utf8().data());
478 }
479
480 void CurlHandle::setCACertPath(const char* path)
481 {
482     if (path)
483         curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
484 }
485
486 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
487 {
488     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, static_cast<long>(verifyPeer));
489 }
490
491 void CurlHandle::setSslVerifyHost(VerifyHost verifyHost)
492 {
493     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, static_cast<long>(verifyHost));
494 }
495
496 void CurlHandle::setSslCert(const char* cert)
497 {
498     curl_easy_setopt(m_handle, CURLOPT_SSLCERT, cert);
499 }
500
501 void CurlHandle::setSslCertType(const char* type)
502 {
503     curl_easy_setopt(m_handle, CURLOPT_SSLCERTTYPE, type);
504 }
505
506 void CurlHandle::setSslKeyPassword(const char* password)
507 {
508     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
509 }
510
511 void CurlHandle::enableProxyIfExists()
512 {
513     auto& proxy = CurlContext::singleton().proxyInfo();
514
515     if (proxy.type != CurlProxyType::Invalid) {
516         curl_easy_setopt(m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
517         curl_easy_setopt(m_handle, CURLOPT_PROXYTYPE, proxy.type);
518     }
519 }
520
521 static CURLoption safeTimeValue(double time)
522 {
523     auto value = static_cast<unsigned>(time >= 0.0 ? time : 0);
524     return static_cast<CURLoption>(value);
525 }
526
527 void CurlHandle::setDnsCacheTimeout(Seconds timeout)
528 {
529     curl_easy_setopt(m_handle, CURLOPT_DNS_CACHE_TIMEOUT, safeTimeValue(timeout.seconds()));
530 }
531
532 void CurlHandle::setConnectTimeout(Seconds timeout)
533 {
534     curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, safeTimeValue(timeout.seconds()));
535 }
536
537 void CurlHandle::setTimeout(Seconds timeout)
538 {
539     curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, safeTimeValue(timeout.milliseconds()));
540 }
541
542 void CurlHandle::setHeaderCallbackFunction(curl_write_callback callbackFunc, void* userData)
543 {
544     curl_easy_setopt(m_handle, CURLOPT_HEADERFUNCTION, callbackFunc);
545     curl_easy_setopt(m_handle, CURLOPT_HEADERDATA, userData);
546 }
547
548 void CurlHandle::setWriteCallbackFunction(curl_write_callback callbackFunc, void* userData)
549 {
550     curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, callbackFunc);
551     curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, userData);
552 }
553
554 void CurlHandle::setReadCallbackFunction(curl_read_callback callbackFunc, void* userData)
555 {
556     curl_easy_setopt(m_handle, CURLOPT_READFUNCTION, callbackFunc);
557     curl_easy_setopt(m_handle, CURLOPT_READDATA, userData);
558 }
559
560 void CurlHandle::setSslCtxCallbackFunction(curl_ssl_ctx_callback callbackFunc, void* userData)
561 {
562     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_DATA, userData);
563     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
564 }
565
566 std::optional<uint16_t> CurlHandle::getPrimaryPort()
567 {
568     if (!m_handle)
569         return std::nullopt;
570
571     long port;
572     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_PORT, &port);
573     if (errorCode != CURLE_OK)
574         return std::nullopt;
575
576     /*
577      * https://github.com/curl/curl/blob/master/lib/connect.c#L612-L660
578      * confirmed that `port` is originally unsigned short.
579      */
580     return static_cast<uint16_t>(port);
581 }
582
583 std::optional<long> CurlHandle::getResponseCode()
584 {
585     if (!m_handle)
586         return std::nullopt;
587
588     long responseCode;
589     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &responseCode);
590     if (errorCode != CURLE_OK)
591         return std::nullopt;
592
593     return responseCode;
594 }
595
596 std::optional<long> CurlHandle::getHttpConnectCode()
597 {
598     if (!m_handle)
599         return std::nullopt;
600
601     long httpConnectCode;
602     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_CONNECTCODE, &httpConnectCode);
603     if (errorCode != CURLE_OK)
604         return std::nullopt;
605
606     return httpConnectCode;
607 }
608
609 std::optional<long long> CurlHandle::getContentLength()
610 {
611     if (!m_handle)
612         return std::nullopt;
613
614     double contentLength;
615
616     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
617     if (errorCode != CURLE_OK)
618         return std::nullopt;
619
620     return static_cast<long long>(contentLength);
621 }
622
623 std::optional<long> CurlHandle::getHttpAuthAvail()
624 {
625     if (!m_handle)
626         return std::nullopt;
627
628     long httpAuthAvailable;
629     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTPAUTH_AVAIL, &httpAuthAvailable);
630     if (errorCode != CURLE_OK)
631         return std::nullopt;
632
633     return httpAuthAvailable;
634 }
635
636 std::optional<long> CurlHandle::getHttpVersion()
637 {
638     if (!m_handle)
639         return std::nullopt;
640
641     long version;
642     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
643     if (errorCode != CURLE_OK)
644         return std::nullopt;
645
646     return version;
647 }
648
649 std::optional<NetworkLoadMetrics> CurlHandle::getNetworkLoadMetrics()
650 {
651     double nameLookup = 0.0;
652     double connect = 0.0;
653     double appConnect = 0.0;
654     double startTransfer = 0.0;
655
656     if (!m_handle)
657         return std::nullopt;
658
659     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_NAMELOOKUP_TIME, &nameLookup);
660     if (errorCode != CURLE_OK)
661         return std::nullopt;
662
663     errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONNECT_TIME, &connect);
664     if (errorCode != CURLE_OK)
665         return std::nullopt;
666
667     errorCode = curl_easy_getinfo(m_handle, CURLINFO_APPCONNECT_TIME, &appConnect);
668     if (errorCode != CURLE_OK)
669         return std::nullopt;
670
671     errorCode = curl_easy_getinfo(m_handle, CURLINFO_STARTTRANSFER_TIME, &startTransfer);
672     if (errorCode != CURLE_OK)
673         return std::nullopt;
674
675     NetworkLoadMetrics networkLoadMetrics;
676
677     networkLoadMetrics.domainLookupStart = Seconds(0);
678     networkLoadMetrics.domainLookupEnd = Seconds(nameLookup);
679     networkLoadMetrics.connectStart = Seconds(nameLookup);
680     networkLoadMetrics.connectEnd = Seconds(connect);
681
682     if (appConnect > 0.0) {
683         networkLoadMetrics.secureConnectionStart = Seconds(connect);
684         networkLoadMetrics.connectEnd = Seconds(appConnect);
685     }
686
687     networkLoadMetrics.requestStart = networkLoadMetrics.connectEnd;
688     networkLoadMetrics.responseStart = Seconds(startTransfer);
689
690     return networkLoadMetrics;
691 }
692
693 long long CurlHandle::maxCurlOffT()
694 {
695     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT() * 8 - 1)) - 1;
696
697     return maxCurlOffT;
698 }
699
700 int CurlHandle::expectedSizeOfCurlOffT()
701 {
702     // The size of a curl_off_t could be different in WebKit and in cURL depending on
703     // compilation flags of both.
704     static int expectedSizeOfCurlOffT = 0;
705     if (!expectedSizeOfCurlOffT) {
706         curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
707         if (infoData->features & CURL_VERSION_LARGEFILE)
708             expectedSizeOfCurlOffT = sizeof(long long);
709         else
710             expectedSizeOfCurlOffT = sizeof(int);
711     }
712
713     return expectedSizeOfCurlOffT;
714 }
715
716 #ifndef NDEBUG
717
718 void CurlHandle::enableVerboseIfUsed()
719 {
720     if (CurlContext::singleton().isVerbose())
721         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
722 }
723
724 void CurlHandle::enableStdErrIfUsed()
725 {
726     if (CurlContext::singleton().getLogFile())
727         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
728 }
729
730 #endif
731
732 }
733
734 #endif