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