[Curl] Surface additional NetworkLoadMetrics
[WebKit-https.git] / Source / WebCore / platform / network / curl / CurlContext.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc.  All rights reserved.
3  * Copyright (C) 2018 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 "CertificateInfo.h"
32 #include "CurlRequestScheduler.h"
33 #include "CurlSSLHandle.h"
34 #include "CurlSSLVerifier.h"
35 #include "HTTPHeaderMap.h"
36 #include <NetworkLoadMetrics.h>
37 #include <mutex>
38 #include <wtf/MainThread.h>
39 #include <wtf/NeverDestroyed.h>
40 #include <wtf/text/CString.h>
41
42 #if OS(WINDOWS)
43 #include "WebCoreBundleWin.h"
44 #include <shlobj.h>
45 #include <shlwapi.h>
46 #endif
47
48 namespace WebCore {
49
50 class EnvironmentVariableReader {
51 public:
52     const char* read(const char* name) { return ::getenv(name); }
53     bool defined(const char* name) { return read(name) != nullptr; }
54
55     template<typename T> std::optional<T> readAs(const char* name)
56     {
57         if (const char* valueStr = read(name)) {
58             T value;
59             if (sscanf(valueStr, sscanTemplate<T>(), &value) == 1)
60                 return value;
61         }
62
63         return std::nullopt;
64     }
65
66 private:
67     template<typename T> const char* sscanTemplate()
68     {
69         ASSERT_NOT_REACHED();
70         return nullptr;
71     }
72 };
73
74 template<>
75 constexpr const char* EnvironmentVariableReader::sscanTemplate<signed>() { return "%d"; }
76
77 template<>
78 constexpr const char* EnvironmentVariableReader::sscanTemplate<unsigned>() { return "%u"; }
79
80 // ALPN Protocol ID (RFC7301) https://tools.ietf.org/html/rfc7301
81 static const ASCIILiteral httpVersion10 { "http/1.0"_s };
82 static const ASCIILiteral httpVersion11 { "http/1.1"_s };
83 static const ASCIILiteral httpVersion2 { "h2"_s };
84
85 // CurlContext -------------------------------------------------------------------
86
87 CurlContext& CurlContext::singleton()
88 {
89     static NeverDestroyed<CurlContext> sharedInstance;
90     return sharedInstance;
91 }
92
93 CurlContext::CurlContext()
94 {
95     initShareHandle();
96
97     EnvironmentVariableReader envVar;
98
99     if (auto value = envVar.readAs<unsigned>("WEBKIT_CURL_DNS_CACHE_TIMEOUT"))
100         m_dnsCacheTimeout = Seconds(*value);
101
102     if (auto value = envVar.readAs<unsigned>("WEBKIT_CURL_CONNECT_TIMEOUT"))
103         m_connectTimeout = Seconds(*value);
104
105     long maxConnects { CurlDefaultMaxConnects };
106     long maxTotalConnections { CurlDefaultMaxTotalConnections };
107     long maxHostConnections { CurlDefaultMaxHostConnections };
108
109     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAXCONNECTS"))
110         maxConnects = *value;
111
112     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAX_TOTAL_CONNECTIONS"))
113         maxTotalConnections = *value;
114
115     if (auto value = envVar.readAs<signed>("WEBKIT_CURL_MAX_HOST_CONNECTIONS"))
116         maxHostConnections = *value;
117
118     m_scheduler = std::make_unique<CurlRequestScheduler>(maxConnects, maxTotalConnections, maxHostConnections);
119
120 #ifndef NDEBUG
121     m_verbose = envVar.defined("DEBUG_CURL");
122
123     auto logFile = envVar.read("CURL_LOG_FILE");
124     if (logFile)
125         m_logFile = fopen(logFile, "a");
126 #endif
127 }
128
129 CurlContext::~CurlContext()
130 {
131 #ifndef NDEBUG
132     if (m_logFile)
133         fclose(m_logFile);
134 #endif
135 }
136
137 void CurlContext::initShareHandle()
138 {
139     CURL* curl = curl_easy_init();
140
141     if (!curl)
142         return;
143
144     curl_easy_setopt(curl, CURLOPT_SHARE, m_shareHandle.handle());
145
146     curl_easy_cleanup(curl);
147 }
148
149 bool CurlContext::isHttp2Enabled() const
150 {
151     curl_version_info_data* data = curl_version_info(CURLVERSION_NOW);
152     return data->features & CURL_VERSION_HTTP2;
153 }
154
155 // CurlShareHandle --------------------------------------------
156
157 CurlShareHandle::CurlShareHandle()
158 {
159     m_shareHandle = curl_share_init();
160     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
161     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
162     curl_share_setopt(m_shareHandle, CURLSHOPT_LOCKFUNC, lockCallback);
163     curl_share_setopt(m_shareHandle, CURLSHOPT_UNLOCKFUNC, unlockCallback);
164 }
165
166 CurlShareHandle::~CurlShareHandle()
167 {
168     if (m_shareHandle)
169         curl_share_cleanup(m_shareHandle);
170 }
171
172 void CurlShareHandle::lockCallback(CURL*, curl_lock_data data, curl_lock_access, void*)
173 {
174     if (auto* mutex = mutexFor(data))
175         mutex->lock();
176 }
177
178 void CurlShareHandle::unlockCallback(CURL*, curl_lock_data data, void*)
179 {
180     if (auto* mutex = mutexFor(data))
181         mutex->unlock();
182 }
183
184 Lock* CurlShareHandle::mutexFor(curl_lock_data data)
185 {
186     static Lock cookieMutex;
187     static Lock dnsMutex;
188     static Lock shareMutex;
189
190     switch (data) {
191     case CURL_LOCK_DATA_COOKIE:
192         return &cookieMutex;
193     case CURL_LOCK_DATA_DNS:
194         return &dnsMutex;
195     case CURL_LOCK_DATA_SHARE:
196         return &shareMutex;
197     default:
198         ASSERT_NOT_REACHED();
199         return nullptr;
200     }
201 }
202
203 // CurlMultiHandle --------------------------------------------
204
205 CurlMultiHandle::CurlMultiHandle()
206 {
207     m_multiHandle = curl_multi_init();
208
209     if (CurlContext::singleton().isHttp2Enabled())
210         curl_multi_setopt(m_multiHandle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
211 }
212
213 CurlMultiHandle::~CurlMultiHandle()
214 {
215     if (m_multiHandle)
216         curl_multi_cleanup(m_multiHandle);
217 }
218
219 void CurlMultiHandle::setMaxConnects(long maxConnects)
220 {
221     if (maxConnects < 0)
222         return;
223
224     curl_multi_setopt(m_multiHandle, CURLMOPT_MAXCONNECTS, maxConnects);
225 }
226
227 void CurlMultiHandle::setMaxTotalConnections(long maxTotalConnections)
228 {
229     curl_multi_setopt(m_multiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, maxTotalConnections);
230 }
231
232 void CurlMultiHandle::setMaxHostConnections(long maxHostConnections)
233 {
234     curl_multi_setopt(m_multiHandle, CURLMOPT_MAX_HOST_CONNECTIONS, maxHostConnections);
235 }
236
237 CURLMcode CurlMultiHandle::addHandle(CURL* handle)
238 {
239     return curl_multi_add_handle(m_multiHandle, handle);
240 }
241
242 CURLMcode CurlMultiHandle::removeHandle(CURL* handle)
243 {
244     return curl_multi_remove_handle(m_multiHandle, handle);
245 }
246
247 CURLMcode CurlMultiHandle::getFdSet(fd_set& readFdSet, fd_set& writeFdSet, fd_set& excFdSet, int& maxFd)
248 {
249     FD_ZERO(&readFdSet);
250     FD_ZERO(&writeFdSet);
251     FD_ZERO(&excFdSet);
252     maxFd = 0;
253
254     return curl_multi_fdset(m_multiHandle, &readFdSet, &writeFdSet, &excFdSet, &maxFd);
255 }
256
257 CURLMcode CurlMultiHandle::perform(int& runningHandles)
258 {
259     return curl_multi_perform(m_multiHandle, &runningHandles);
260 }
261
262 CURLMsg* CurlMultiHandle::readInfo(int& messagesInQueue)
263 {
264     return curl_multi_info_read(m_multiHandle, &messagesInQueue);
265 }
266
267 // CurlHandle -------------------------------------------------
268
269 CurlHandle::CurlHandle()
270 {
271     m_handle = curl_easy_init();
272     curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
273
274     enableShareHandle();
275     enableAllowedProtocols();
276     enableAcceptEncoding();
277
278     setDnsCacheTimeout(CurlContext::singleton().dnsCacheTimeout());
279     setConnectTimeout(CurlContext::singleton().connectTimeout());
280
281     enableProxyIfExists();
282
283 #ifndef NDEBUG
284     enableVerboseIfUsed();
285     enableStdErrIfUsed();
286 #endif
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::enableSSLForHost(const String& host)
301 {
302     auto& sslHandle = CurlContext::singleton().sslHandle();
303     if (auto sslClientCertificate = sslHandle.getSSLClientCertificate(host)) {
304         setSslCert(sslClientCertificate->first.utf8().data());
305         setSslCertType("P12");
306         setSslKeyPassword(sslClientCertificate->second.utf8().data());
307     }
308
309     if (sslHandle.canIgnoreAnyHTTPSCertificatesForHost(host) || sslHandle.shouldIgnoreSSLErrors()) {
310         setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
311         setSslVerifyHost(CurlHandle::VerifyHost::LooseNameCheck);
312     } else {
313         setSslVerifyPeer(CurlHandle::VerifyPeer::Enable);
314         setSslVerifyHost(CurlHandle::VerifyHost::StrictNameCheck);
315     }
316
317     const auto& cipherList = sslHandle.getCipherList();
318     if (!cipherList.isEmpty())
319         setSslCipherList(cipherList.utf8().data());
320
321     setSslCtxCallbackFunction(willSetupSslCtxCallback, this);
322
323     if (auto* path = WTF::get_if<String>(sslHandle.getCACertInfo()))
324         setCACertPath(path->utf8().data());
325 }
326
327 CURLcode CurlHandle::willSetupSslCtx(void* sslCtx)
328 {
329     if (!sslCtx)
330         return CURLE_ABORTED_BY_CALLBACK;
331
332     if (!m_sslVerifier)
333         m_sslVerifier = std::make_unique<CurlSSLVerifier>(*this, sslCtx);
334
335     return CURLE_OK;
336 }
337
338 CURLcode CurlHandle::willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData)
339 {
340     return static_cast<CurlHandle*>(userData)->willSetupSslCtx(sslCtx);
341 }
342
343 int CurlHandle::sslErrors() const
344 {
345     return m_sslVerifier ? m_sslVerifier->sslErrors() : 0;
346 }
347
348 CURLcode CurlHandle::perform()
349 {
350     return curl_easy_perform(m_handle);
351 }
352
353 CURLcode CurlHandle::pause(int bitmask)
354 {
355     return curl_easy_pause(m_handle, bitmask);
356 }
357
358 void CurlHandle::enableShareHandle()
359 {
360     curl_easy_setopt(m_handle, CURLOPT_SHARE, CurlContext::singleton().shareHandle().handle());
361 }
362
363 void CurlHandle::setUrl(const URL& url)
364 {
365     m_url = url.isolatedCopy();
366
367     URL curlUrl = url;
368
369     // Remove any fragment part, otherwise curl will send it as part of the request.
370     curlUrl.removeFragmentIdentifier();
371
372     // Remove any query part sent to a local file.
373     if (curlUrl.isLocalFile()) {
374         // By setting the query to a null string it'll be removed.
375         if (!curlUrl.query().isEmpty())
376             curlUrl.setQuery(String());
377     }
378
379     // url is in ASCII so latin1() will only convert it to char* without character translation.
380     curl_easy_setopt(m_handle, CURLOPT_URL, curlUrl.string().latin1().data());
381
382     if (url.protocolIs("https"))
383         enableSSLForHost(m_url.host().toString());
384 }
385
386 void CurlHandle::appendRequestHeaders(const HTTPHeaderMap& headers)
387 {
388     if (headers.size()) {
389         for (auto& entry : headers) {
390             auto& value = entry.value;
391             appendRequestHeader(entry.key, entry.value);
392         }
393     }
394 }
395
396 void CurlHandle::appendRequestHeader(const String& name, const String& value)
397 {
398     String header(name);
399
400     if (value.isEmpty()) {
401         // Insert the ; to tell curl that this header has an empty value.
402         header.append(";");
403     } else {
404         header.append(": ");
405         header.append(value);
406     }
407
408     appendRequestHeader(header);
409 }
410
411 void CurlHandle::removeRequestHeader(const String& name)
412 {
413     // Add a header with no content, the internally used header will get disabled. 
414     String header(name);
415     header.append(":");
416
417     appendRequestHeader(header);
418 }
419
420 void CurlHandle::appendRequestHeader(const String& header)
421 {
422     bool needToEnable = m_requestHeaders.isEmpty();
423
424     m_requestHeaders.append(header);
425
426     if (needToEnable)
427         enableRequestHeaders();
428 }
429
430 void CurlHandle::enableRequestHeaders()
431 {
432     if (m_requestHeaders.isEmpty())
433         return;
434
435     const struct curl_slist* headers = m_requestHeaders.head();
436     curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers);
437 }
438
439 void CurlHandle::enableHttp()
440 {
441     if (m_url.protocolIs("https") && CurlContext::singleton().isHttp2Enabled()) {
442         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
443         curl_easy_setopt(m_handle, CURLOPT_PIPEWAIT, 1L);
444         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_ALPN, 1L);
445         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_NPN, 0L);
446     } else
447         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
448 }
449
450 void CurlHandle::enableHttpGetRequest()
451 {
452     enableHttp();
453     curl_easy_setopt(m_handle, CURLOPT_HTTPGET, 1L);
454 }
455
456 void CurlHandle::enableHttpHeadRequest()
457 {
458     enableHttp();
459     curl_easy_setopt(m_handle, CURLOPT_NOBODY, 1L);
460 }
461
462 void CurlHandle::enableHttpPostRequest()
463 {
464     enableHttp();
465     curl_easy_setopt(m_handle, CURLOPT_POST, 1L);
466     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, 0L);
467 }
468
469 void CurlHandle::setPostFields(const char* data, long size)
470 {
471     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDS, data);
472     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, size);
473 }
474
475 void CurlHandle::setPostFieldLarge(curl_off_t size)
476 {
477     if (expectedSizeOfCurlOffT() != sizeof(long long))
478         size = static_cast<int>(size);
479
480     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE_LARGE, size);
481 }
482
483 void CurlHandle::enableHttpPutRequest()
484 {
485     enableHttp();
486     curl_easy_setopt(m_handle, CURLOPT_UPLOAD, 1L);
487     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE, 0L);
488 }
489
490 void CurlHandle::setInFileSizeLarge(curl_off_t size)
491 {
492     if (expectedSizeOfCurlOffT() != sizeof(long long))
493         size = static_cast<int>(size);
494
495     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE_LARGE, size);
496 }
497
498 void CurlHandle::setHttpCustomRequest(const String& method)
499 {
500     enableHttp();
501     curl_easy_setopt(m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
502 }
503
504 void CurlHandle::enableAcceptEncoding()
505 {
506     // enable all supported built-in compressions (gzip and deflate) through Accept-Encoding:
507     curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
508 }
509
510 void CurlHandle::enableAllowedProtocols()
511 {
512     static const long allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
513
514     curl_easy_setopt(m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
515 }
516
517 void CurlHandle::enableHttpAuthentication(long option)
518 {
519     curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, option);
520 }
521
522 void CurlHandle::setHttpAuthUserPass(const String& user, const String& password)
523 {
524     curl_easy_setopt(m_handle, CURLOPT_USERNAME, user.utf8().data());
525     curl_easy_setopt(m_handle, CURLOPT_PASSWORD, password.utf8().data());
526 }
527
528 void CurlHandle::setCACertPath(const char* path)
529 {
530     if (path)
531         curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
532 }
533
534 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
535 {
536     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, static_cast<long>(verifyPeer));
537 }
538
539 void CurlHandle::setSslVerifyHost(VerifyHost verifyHost)
540 {
541     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, static_cast<long>(verifyHost));
542 }
543
544 void CurlHandle::setSslCert(const char* cert)
545 {
546     curl_easy_setopt(m_handle, CURLOPT_SSLCERT, cert);
547 }
548
549 void CurlHandle::setSslCertType(const char* type)
550 {
551     curl_easy_setopt(m_handle, CURLOPT_SSLCERTTYPE, type);
552 }
553
554 void CurlHandle::setSslKeyPassword(const char* password)
555 {
556     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
557 }
558
559 void CurlHandle::setSslCipherList(const char* cipherList)
560 {
561     curl_easy_setopt(m_handle, CURLOPT_SSL_CIPHER_LIST, cipherList);
562 }
563
564 void CurlHandle::enableProxyIfExists()
565 {
566     auto& proxy = CurlContext::singleton().proxySettings();
567
568     switch (proxy.mode()) {
569     case CurlProxySettings::Mode::Default :
570         // For the proxy set by environment variable
571         if (!proxy.user().isEmpty())
572             curl_easy_setopt(m_handle, CURLOPT_PROXYUSERNAME, proxy.user().utf8().data());
573         if (!proxy.password().isEmpty())
574             curl_easy_setopt(m_handle, CURLOPT_PROXYPASSWORD, proxy.password().utf8().data());
575         curl_easy_setopt(m_handle, CURLOPT_PROXYAUTH, proxy.authMethod());
576         break;
577     case CurlProxySettings::Mode::NoProxy :
578         // Disable the use of a proxy, even if there is an environment variable set for it.
579         curl_easy_setopt(m_handle, CURLOPT_PROXY, "");
580         break;
581     case CurlProxySettings::Mode::Custom :
582         curl_easy_setopt(m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
583         curl_easy_setopt(m_handle, CURLOPT_NOPROXY, proxy.ignoreHosts().utf8().data());
584         curl_easy_setopt(m_handle, CURLOPT_PROXYAUTH, proxy.authMethod());
585         break;
586     }
587 }
588
589 static CURLoption safeTimeValue(double time)
590 {
591     auto value = static_cast<unsigned>(time >= 0.0 ? time : 0);
592     return static_cast<CURLoption>(value);
593 }
594
595 void CurlHandle::setDnsCacheTimeout(Seconds timeout)
596 {
597     curl_easy_setopt(m_handle, CURLOPT_DNS_CACHE_TIMEOUT, safeTimeValue(timeout.seconds()));
598 }
599
600 void CurlHandle::setConnectTimeout(Seconds timeout)
601 {
602     curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, safeTimeValue(timeout.seconds()));
603 }
604
605 void CurlHandle::setTimeout(Seconds timeout)
606 {
607     curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, safeTimeValue(timeout.milliseconds()));
608 }
609
610 void CurlHandle::setHeaderCallbackFunction(curl_write_callback callbackFunc, void* userData)
611 {
612     curl_easy_setopt(m_handle, CURLOPT_HEADERFUNCTION, callbackFunc);
613     curl_easy_setopt(m_handle, CURLOPT_HEADERDATA, userData);
614 }
615
616 void CurlHandle::setWriteCallbackFunction(curl_write_callback callbackFunc, void* userData)
617 {
618     curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, callbackFunc);
619     curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, userData);
620 }
621
622 void CurlHandle::setReadCallbackFunction(curl_read_callback callbackFunc, void* userData)
623 {
624     curl_easy_setopt(m_handle, CURLOPT_READFUNCTION, callbackFunc);
625     curl_easy_setopt(m_handle, CURLOPT_READDATA, userData);
626 }
627
628 void CurlHandle::setSslCtxCallbackFunction(curl_ssl_ctx_callback callbackFunc, void* userData)
629 {
630     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_DATA, userData);
631     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
632 }
633
634 void CurlHandle::enableConnectionOnly()
635 {
636     curl_easy_setopt(m_handle, CURLOPT_CONNECT_ONLY, 1L);
637 }
638
639 std::optional<String> CurlHandle::getProxyUrl()
640 {
641     auto& proxy = CurlContext::singleton().proxySettings();
642     if (proxy.mode() == CurlProxySettings::Mode::Default)
643         return std::nullopt;
644
645     return proxy.url();
646 }
647
648 std::optional<long> CurlHandle::getResponseCode()
649 {
650     if (!m_handle)
651         return std::nullopt;
652
653     long responseCode;
654     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &responseCode);
655     if (errorCode != CURLE_OK)
656         return std::nullopt;
657
658     return responseCode;
659 }
660
661 std::optional<long> CurlHandle::getHttpConnectCode()
662 {
663     if (!m_handle)
664         return std::nullopt;
665
666     long httpConnectCode;
667     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_CONNECTCODE, &httpConnectCode);
668     if (errorCode != CURLE_OK)
669         return std::nullopt;
670
671     return httpConnectCode;
672 }
673
674 std::optional<long long> CurlHandle::getContentLength()
675 {
676     if (!m_handle)
677         return std::nullopt;
678
679     double contentLength;
680
681     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
682     if (errorCode != CURLE_OK)
683         return std::nullopt;
684
685     return static_cast<long long>(contentLength);
686 }
687
688 std::optional<long> CurlHandle::getHttpAuthAvail()
689 {
690     if (!m_handle)
691         return std::nullopt;
692
693     long httpAuthAvailable;
694     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTPAUTH_AVAIL, &httpAuthAvailable);
695     if (errorCode != CURLE_OK)
696         return std::nullopt;
697
698     return httpAuthAvailable;
699 }
700
701 std::optional<long> CurlHandle::getProxyAuthAvail()
702 {
703     if (!m_handle)
704         return std::nullopt;
705
706     long proxyAuthAvailable;
707     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_PROXYAUTH_AVAIL, &proxyAuthAvailable);
708     if (errorCode != CURLE_OK)
709         return std::nullopt;
710
711     return proxyAuthAvailable;
712 }
713
714 std::optional<long> CurlHandle::getHttpVersion()
715 {
716     if (!m_handle)
717         return std::nullopt;
718
719     long version;
720     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
721     if (errorCode != CURLE_OK)
722         return std::nullopt;
723
724     return version;
725 }
726
727 std::optional<NetworkLoadMetrics> CurlHandle::getNetworkLoadMetrics()
728 {
729     double nameLookup = 0.0;
730     double connect = 0.0;
731     double appConnect = 0.0;
732     double startTransfer = 0.0;
733     long requestHeaderSize = 0;
734     curl_off_t requestBodySize = 0;
735     long responseHeaderSize = 0;
736     curl_off_t responseBodySize = 0;
737     long version = 0;
738     char* ip = nullptr;
739     long port = 0;
740
741     if (!m_handle)
742         return std::nullopt;
743
744     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_NAMELOOKUP_TIME, &nameLookup);
745     if (errorCode != CURLE_OK)
746         return std::nullopt;
747
748     errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONNECT_TIME, &connect);
749     if (errorCode != CURLE_OK)
750         return std::nullopt;
751
752     errorCode = curl_easy_getinfo(m_handle, CURLINFO_APPCONNECT_TIME, &appConnect);
753     if (errorCode != CURLE_OK)
754         return std::nullopt;
755
756     errorCode = curl_easy_getinfo(m_handle, CURLINFO_STARTTRANSFER_TIME, &startTransfer);
757     if (errorCode != CURLE_OK)
758         return std::nullopt;
759
760     // FIXME: Gets total request size not just headers https://bugs.webkit.org/show_bug.cgi?id=188363
761     errorCode = curl_easy_getinfo(m_handle, CURLINFO_REQUEST_SIZE, &requestHeaderSize);
762     if (errorCode != CURLE_OK)
763         return std::nullopt;
764
765     errorCode = curl_easy_getinfo(m_handle, CURLINFO_SIZE_UPLOAD_T, &requestBodySize);
766     if (errorCode != CURLE_OK)
767         return std::nullopt;
768
769     errorCode = curl_easy_getinfo(m_handle, CURLINFO_HEADER_SIZE, &responseHeaderSize);
770     if (errorCode != CURLE_OK)
771         return std::nullopt;
772
773     errorCode = curl_easy_getinfo(m_handle, CURLINFO_SIZE_DOWNLOAD_T, &responseBodySize);
774     if (errorCode != CURLE_OK)
775         return std::nullopt;
776
777     errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_IP, &ip);
778     if (errorCode != CURLE_OK)
779         return std::nullopt;
780
781     errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_PORT, &port);
782     if (errorCode != CURLE_OK)
783         return std::nullopt;
784
785     errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
786     if (errorCode != CURLE_OK)
787         return std::nullopt;
788
789     NetworkLoadMetrics networkLoadMetrics;
790
791     networkLoadMetrics.domainLookupStart = Seconds(0);
792     networkLoadMetrics.domainLookupEnd = Seconds(nameLookup);
793     networkLoadMetrics.connectStart = Seconds(nameLookup);
794     networkLoadMetrics.connectEnd = Seconds(connect);
795
796     if (appConnect > 0.0) {
797         networkLoadMetrics.secureConnectionStart = Seconds(connect);
798         networkLoadMetrics.connectEnd = Seconds(appConnect);
799     }
800
801     networkLoadMetrics.requestStart = networkLoadMetrics.connectEnd;
802     networkLoadMetrics.responseStart = Seconds(startTransfer);
803
804     networkLoadMetrics.requestHeaderBytesSent = requestHeaderSize;
805     networkLoadMetrics.requestBodyBytesSent = requestBodySize;
806     networkLoadMetrics.responseHeaderBytesReceived = responseHeaderSize;
807     networkLoadMetrics.responseBodyBytesReceived = responseBodySize;
808
809     if (ip) {
810         networkLoadMetrics.remoteAddress = String(ip);
811         if (port)
812             networkLoadMetrics.remoteAddress.append(":" + String::number(port));
813     }
814
815     if (version == CURL_HTTP_VERSION_1_0)
816         networkLoadMetrics.protocol = httpVersion10;
817     else if (version == CURL_HTTP_VERSION_1_1)
818         networkLoadMetrics.protocol = httpVersion11;
819     else if (version == CURL_HTTP_VERSION_2)
820         networkLoadMetrics.protocol = httpVersion2;
821
822     return networkLoadMetrics;
823 }
824
825 std::optional<CertificateInfo> CurlHandle::certificateInfo() const
826 {
827     if (!m_sslVerifier)
828         return std::nullopt;
829
830     return m_sslVerifier->certificateInfo();
831 }
832
833 long long CurlHandle::maxCurlOffT()
834 {
835     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT() * 8 - 1)) - 1;
836
837     return maxCurlOffT;
838 }
839
840 int CurlHandle::expectedSizeOfCurlOffT()
841 {
842     // The size of a curl_off_t could be different in WebKit and in cURL depending on
843     // compilation flags of both.
844     static int expectedSizeOfCurlOffT = 0;
845     if (!expectedSizeOfCurlOffT) {
846         curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
847         if (infoData->features & CURL_VERSION_LARGEFILE)
848             expectedSizeOfCurlOffT = sizeof(long long);
849         else
850             expectedSizeOfCurlOffT = sizeof(int);
851     }
852
853     return expectedSizeOfCurlOffT;
854 }
855
856 #ifndef NDEBUG
857
858 void CurlHandle::enableVerboseIfUsed()
859 {
860     if (CurlContext::singleton().isVerbose())
861         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
862 }
863
864 void CurlHandle::enableStdErrIfUsed()
865 {
866     if (auto log = CurlContext::singleton().getLogFile())
867         curl_easy_setopt(m_handle, CURLOPT_STDERR, log);
868 }
869
870 #endif
871
872 // CurlSocketHandle
873
874 CurlSocketHandle::CurlSocketHandle(const URL& url, Function<void(CURLcode)>&& errorHandler)
875     : m_errorHandler(WTFMove(errorHandler))
876 {
877     // Libcurl is not responsible for the protocol handling. It just handles connection.
878     // Only scheme, host and port is required.
879     URL urlForConnection;
880     urlForConnection.setProtocol(url.protocolIs("wss") ? "https" : "http");
881     urlForConnection.setHostAndPort(url.hostAndPort());
882     setUrl(urlForConnection);
883
884     enableConnectionOnly();
885 }
886
887 bool CurlSocketHandle::connect()
888 {
889     CURLcode errorCode = perform();
890     if (errorCode != CURLE_OK) {
891         m_errorHandler(errorCode);
892         return false;
893     }
894
895     return true;
896 }
897
898 size_t CurlSocketHandle::send(const uint8_t* buffer, size_t size)
899 {
900     size_t totalBytesSent = 0;
901
902     while (totalBytesSent < size) {
903         size_t bytesSent = 0;
904         CURLcode errorCode = curl_easy_send(handle(), buffer + totalBytesSent, size - totalBytesSent, &bytesSent);
905         if (errorCode != CURLE_OK) {
906             if (errorCode != CURLE_AGAIN)
907                 m_errorHandler(errorCode);
908             break;
909         }
910
911         totalBytesSent += bytesSent;
912     }
913
914     return totalBytesSent;
915 }
916
917 std::optional<size_t> CurlSocketHandle::receive(uint8_t* buffer, size_t bufferSize)
918 {
919     size_t bytesRead = 0;
920
921     CURLcode errorCode = curl_easy_recv(handle(), buffer, bufferSize, &bytesRead);
922     if (errorCode != CURLE_OK) {
923         if (errorCode != CURLE_AGAIN)
924             m_errorHandler(errorCode);
925
926         return std::nullopt;
927     }
928
929     return bytesRead;
930 }
931
932 std::optional<CurlSocketHandle::WaitResult> CurlSocketHandle::wait(const Seconds& timeout, bool alsoWaitForWrite)
933 {
934     curl_socket_t socket;
935     CURLcode errorCode = curl_easy_getinfo(handle(), CURLINFO_ACTIVESOCKET, &socket);
936     if (errorCode != CURLE_OK) {
937         m_errorHandler(errorCode);
938         return std::nullopt;
939     }
940
941     int64_t usec = timeout.microsecondsAs<int64_t>();
942
943     struct timeval selectTimeout;
944     if (usec <= 0) {
945         selectTimeout.tv_sec = 0;
946         selectTimeout.tv_usec = 0;
947     } else {
948         selectTimeout.tv_sec = usec / 1000000;
949         selectTimeout.tv_usec = usec % 1000000;
950     }
951
952     int rc = 0;
953     int maxfd = static_cast<int>(socket) + 1;
954     fd_set fdread;
955     fd_set fdwrite;
956     fd_set fderr;
957
958     // Retry 'select' if it was interrupted by a process signal.
959     do {
960         FD_ZERO(&fdread);
961         FD_SET(socket, &fdread);
962
963         FD_ZERO(&fdwrite);
964         if (alsoWaitForWrite)
965             FD_SET(socket, &fdwrite);
966
967         FD_ZERO(&fderr);
968         FD_SET(socket, &fderr);
969
970         rc = ::select(maxfd, &fdread, &fdwrite, &fderr, &selectTimeout);
971     } while (rc == -1 && errno == EINTR);
972
973     if (rc <= 0)
974         return std::nullopt;
975
976     WaitResult result;
977     result.readable = FD_ISSET(socket, &fdread) || FD_ISSET(socket, &fderr);
978     result.writable = FD_ISSET(socket, &fdwrite);
979     return result;
980 }
981
982 }
983
984 #endif