Unreviewed, rolling out r241620.
[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> 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 WTF::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::setHttpAuthUserPass(const String& user, const String& password, long authType)
518 {
519     curl_easy_setopt(m_handle, CURLOPT_USERNAME, user.utf8().data());
520     curl_easy_setopt(m_handle, CURLOPT_PASSWORD, password.utf8().data());
521     curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, authType);
522 }
523
524 void CurlHandle::setCACertPath(const char* path)
525 {
526     if (path)
527         curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
528 }
529
530 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
531 {
532     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, static_cast<long>(verifyPeer));
533 }
534
535 void CurlHandle::setSslVerifyHost(VerifyHost verifyHost)
536 {
537     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, static_cast<long>(verifyHost));
538 }
539
540 void CurlHandle::setSslCert(const char* cert)
541 {
542     curl_easy_setopt(m_handle, CURLOPT_SSLCERT, cert);
543 }
544
545 void CurlHandle::setSslCertType(const char* type)
546 {
547     curl_easy_setopt(m_handle, CURLOPT_SSLCERTTYPE, type);
548 }
549
550 void CurlHandle::setSslKeyPassword(const char* password)
551 {
552     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
553 }
554
555 void CurlHandle::setSslCipherList(const char* cipherList)
556 {
557     curl_easy_setopt(m_handle, CURLOPT_SSL_CIPHER_LIST, cipherList);
558 }
559
560 void CurlHandle::enableProxyIfExists()
561 {
562     auto& proxy = CurlContext::singleton().proxySettings();
563
564     switch (proxy.mode()) {
565     case CurlProxySettings::Mode::Default :
566         // For the proxy set by environment variable
567         if (!proxy.user().isEmpty())
568             curl_easy_setopt(m_handle, CURLOPT_PROXYUSERNAME, proxy.user().utf8().data());
569         if (!proxy.password().isEmpty())
570             curl_easy_setopt(m_handle, CURLOPT_PROXYPASSWORD, proxy.password().utf8().data());
571         curl_easy_setopt(m_handle, CURLOPT_PROXYAUTH, proxy.authMethod());
572         break;
573     case CurlProxySettings::Mode::NoProxy :
574         // Disable the use of a proxy, even if there is an environment variable set for it.
575         curl_easy_setopt(m_handle, CURLOPT_PROXY, "");
576         break;
577     case CurlProxySettings::Mode::Custom :
578         curl_easy_setopt(m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
579         curl_easy_setopt(m_handle, CURLOPT_NOPROXY, proxy.ignoreHosts().utf8().data());
580         curl_easy_setopt(m_handle, CURLOPT_PROXYAUTH, proxy.authMethod());
581         break;
582     }
583 }
584
585 static CURLoption safeTimeValue(double time)
586 {
587     auto value = static_cast<unsigned>(time >= 0.0 ? time : 0);
588     return static_cast<CURLoption>(value);
589 }
590
591 void CurlHandle::setDnsCacheTimeout(Seconds timeout)
592 {
593     curl_easy_setopt(m_handle, CURLOPT_DNS_CACHE_TIMEOUT, safeTimeValue(timeout.seconds()));
594 }
595
596 void CurlHandle::setConnectTimeout(Seconds timeout)
597 {
598     curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, safeTimeValue(timeout.seconds()));
599 }
600
601 void CurlHandle::setTimeout(Seconds timeout)
602 {
603     // Originally CURLOPT_TIMEOUT_MS was used here, but that is not the
604     // idle timeout, but entire duration time limit. It's not safe to specify
605     // such a time limit for communications, such as downloading.
606     // CURLOPT_LOW_SPEED_LIMIT is used instead. It enables the speed watcher
607     // and if the speed is below specified limit and last for specified duration,
608     // it invokes timeout error.
609     curl_easy_setopt(m_handle, CURLOPT_LOW_SPEED_LIMIT, 1L);
610     curl_easy_setopt(m_handle, CURLOPT_LOW_SPEED_TIME, safeTimeValue(timeout.seconds()));
611 }
612
613 void CurlHandle::setHeaderCallbackFunction(curl_write_callback callbackFunc, void* userData)
614 {
615     curl_easy_setopt(m_handle, CURLOPT_HEADERFUNCTION, callbackFunc);
616     curl_easy_setopt(m_handle, CURLOPT_HEADERDATA, userData);
617 }
618
619 void CurlHandle::setWriteCallbackFunction(curl_write_callback callbackFunc, void* userData)
620 {
621     curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, callbackFunc);
622     curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, userData);
623 }
624
625 void CurlHandle::setReadCallbackFunction(curl_read_callback callbackFunc, void* userData)
626 {
627     curl_easy_setopt(m_handle, CURLOPT_READFUNCTION, callbackFunc);
628     curl_easy_setopt(m_handle, CURLOPT_READDATA, userData);
629 }
630
631 void CurlHandle::setSslCtxCallbackFunction(curl_ssl_ctx_callback callbackFunc, void* userData)
632 {
633     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_DATA, userData);
634     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
635 }
636
637 void CurlHandle::enableConnectionOnly()
638 {
639     curl_easy_setopt(m_handle, CURLOPT_CONNECT_ONLY, 1L);
640 }
641
642 Optional<String> CurlHandle::getProxyUrl()
643 {
644     auto& proxy = CurlContext::singleton().proxySettings();
645     if (proxy.mode() == CurlProxySettings::Mode::Default)
646         return WTF::nullopt;
647
648     return proxy.url();
649 }
650
651 Optional<long> CurlHandle::getResponseCode()
652 {
653     if (!m_handle)
654         return WTF::nullopt;
655
656     long responseCode;
657     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &responseCode);
658     if (errorCode != CURLE_OK)
659         return WTF::nullopt;
660
661     return responseCode;
662 }
663
664 Optional<long> CurlHandle::getHttpConnectCode()
665 {
666     if (!m_handle)
667         return WTF::nullopt;
668
669     long httpConnectCode;
670     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_CONNECTCODE, &httpConnectCode);
671     if (errorCode != CURLE_OK)
672         return WTF::nullopt;
673
674     return httpConnectCode;
675 }
676
677 Optional<long long> CurlHandle::getContentLength()
678 {
679     if (!m_handle)
680         return WTF::nullopt;
681
682     double contentLength;
683
684     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
685     if (errorCode != CURLE_OK)
686         return WTF::nullopt;
687
688     return static_cast<long long>(contentLength);
689 }
690
691 Optional<long> CurlHandle::getHttpAuthAvail()
692 {
693     if (!m_handle)
694         return WTF::nullopt;
695
696     long httpAuthAvailable;
697     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTPAUTH_AVAIL, &httpAuthAvailable);
698     if (errorCode != CURLE_OK)
699         return WTF::nullopt;
700
701     return httpAuthAvailable;
702 }
703
704 Optional<long> CurlHandle::getProxyAuthAvail()
705 {
706     if (!m_handle)
707         return WTF::nullopt;
708
709     long proxyAuthAvailable;
710     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_PROXYAUTH_AVAIL, &proxyAuthAvailable);
711     if (errorCode != CURLE_OK)
712         return WTF::nullopt;
713
714     return proxyAuthAvailable;
715 }
716
717 Optional<long> CurlHandle::getHttpVersion()
718 {
719     if (!m_handle)
720         return WTF::nullopt;
721
722     long version;
723     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
724     if (errorCode != CURLE_OK)
725         return WTF::nullopt;
726
727     return version;
728 }
729
730 Optional<NetworkLoadMetrics> CurlHandle::getNetworkLoadMetrics(const WTF::Seconds& domainLookupStart)
731 {
732     double nameLookup = 0.0;
733     double connect = 0.0;
734     double appConnect = 0.0;
735     double startTransfer = 0.0;
736     long version = 0;
737
738     if (!m_handle)
739         return WTF::nullopt;
740
741     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_NAMELOOKUP_TIME, &nameLookup);
742     if (errorCode != CURLE_OK)
743         return WTF::nullopt;
744
745     errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONNECT_TIME, &connect);
746     if (errorCode != CURLE_OK)
747         return WTF::nullopt;
748
749     errorCode = curl_easy_getinfo(m_handle, CURLINFO_APPCONNECT_TIME, &appConnect);
750     if (errorCode != CURLE_OK)
751         return WTF::nullopt;
752
753     errorCode = curl_easy_getinfo(m_handle, CURLINFO_STARTTRANSFER_TIME, &startTransfer);
754     if (errorCode != CURLE_OK)
755         return WTF::nullopt;
756
757     errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
758     if (errorCode != CURLE_OK)
759         return WTF::nullopt;
760
761     NetworkLoadMetrics networkLoadMetrics;
762
763     networkLoadMetrics.domainLookupStart = domainLookupStart;
764     networkLoadMetrics.domainLookupEnd = domainLookupStart + Seconds(nameLookup);
765     networkLoadMetrics.connectStart = domainLookupStart + Seconds(nameLookup);
766     networkLoadMetrics.connectEnd = domainLookupStart + Seconds(connect);
767
768     if (appConnect > 0.0) {
769         networkLoadMetrics.secureConnectionStart = domainLookupStart + Seconds(connect);
770         networkLoadMetrics.connectEnd = domainLookupStart + Seconds(appConnect);
771     }
772
773     networkLoadMetrics.requestStart = networkLoadMetrics.connectEnd;
774     networkLoadMetrics.responseStart = domainLookupStart + Seconds(startTransfer);
775
776     if (version == CURL_HTTP_VERSION_1_0)
777         networkLoadMetrics.protocol = httpVersion10;
778     else if (version == CURL_HTTP_VERSION_1_1)
779         networkLoadMetrics.protocol = httpVersion11;
780     else if (version == CURL_HTTP_VERSION_2)
781         networkLoadMetrics.protocol = httpVersion2;
782
783     return networkLoadMetrics;
784 }
785
786 void CurlHandle::addExtraNetworkLoadMetrics(NetworkLoadMetrics& networkLoadMetrics)
787 {
788     long requestHeaderSize = 0;
789     curl_off_t requestBodySize = 0;
790     long responseHeaderSize = 0;
791     curl_off_t responseBodySize = 0;
792     char* ip = nullptr;
793     long port = 0;
794
795     // FIXME: Gets total request size not just headers https://bugs.webkit.org/show_bug.cgi?id=188363
796     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_REQUEST_SIZE, &requestHeaderSize);
797     if (errorCode != CURLE_OK)
798         return;
799
800     errorCode = curl_easy_getinfo(m_handle, CURLINFO_SIZE_UPLOAD_T, &requestBodySize);
801     if (errorCode != CURLE_OK)
802         return;
803
804     errorCode = curl_easy_getinfo(m_handle, CURLINFO_HEADER_SIZE, &responseHeaderSize);
805     if (errorCode != CURLE_OK)
806         return;
807
808     errorCode = curl_easy_getinfo(m_handle, CURLINFO_SIZE_DOWNLOAD_T, &responseBodySize);
809     if (errorCode != CURLE_OK)
810         return;
811
812     errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_IP, &ip);
813     if (errorCode != CURLE_OK)
814         return;
815
816     errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_PORT, &port);
817     if (errorCode != CURLE_OK)
818         return;
819
820     networkLoadMetrics.requestHeaderBytesSent = requestHeaderSize;
821     networkLoadMetrics.requestBodyBytesSent = requestBodySize;
822     networkLoadMetrics.responseHeaderBytesReceived = responseHeaderSize;
823     networkLoadMetrics.responseBodyBytesReceived = responseBodySize;
824
825     if (ip) {
826         networkLoadMetrics.remoteAddress = String(ip);
827         if (port)
828             networkLoadMetrics.remoteAddress.append(":" + String::number(port));
829     }
830 }
831
832 Optional<CertificateInfo> CurlHandle::certificateInfo() const
833 {
834     if (!m_sslVerifier)
835         return WTF::nullopt;
836
837     return m_sslVerifier->certificateInfo();
838 }
839
840 long long CurlHandle::maxCurlOffT()
841 {
842     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT() * 8 - 1)) - 1;
843
844     return maxCurlOffT;
845 }
846
847 int CurlHandle::expectedSizeOfCurlOffT()
848 {
849     // The size of a curl_off_t could be different in WebKit and in cURL depending on
850     // compilation flags of both.
851     static int expectedSizeOfCurlOffT = 0;
852     if (!expectedSizeOfCurlOffT) {
853         curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
854         if (infoData->features & CURL_VERSION_LARGEFILE)
855             expectedSizeOfCurlOffT = sizeof(long long);
856         else
857             expectedSizeOfCurlOffT = sizeof(int);
858     }
859
860     return expectedSizeOfCurlOffT;
861 }
862
863 #ifndef NDEBUG
864
865 void CurlHandle::enableVerboseIfUsed()
866 {
867     if (CurlContext::singleton().isVerbose())
868         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
869 }
870
871 void CurlHandle::enableStdErrIfUsed()
872 {
873     if (auto log = CurlContext::singleton().getLogFile())
874         curl_easy_setopt(m_handle, CURLOPT_STDERR, log);
875 }
876
877 #endif
878
879 // CurlSocketHandle
880
881 CurlSocketHandle::CurlSocketHandle(const URL& url, Function<void(CURLcode)>&& errorHandler)
882     : m_errorHandler(WTFMove(errorHandler))
883 {
884     // Libcurl is not responsible for the protocol handling. It just handles connection.
885     // Only scheme, host and port is required.
886     URL urlForConnection;
887     urlForConnection.setProtocol(url.protocolIs("wss") ? "https" : "http");
888     urlForConnection.setHostAndPort(url.hostAndPort());
889     setUrl(urlForConnection);
890
891     enableConnectionOnly();
892 }
893
894 bool CurlSocketHandle::connect()
895 {
896     CURLcode errorCode = perform();
897     if (errorCode != CURLE_OK) {
898         m_errorHandler(errorCode);
899         return false;
900     }
901
902     return true;
903 }
904
905 size_t CurlSocketHandle::send(const uint8_t* buffer, size_t size)
906 {
907     size_t totalBytesSent = 0;
908
909     while (totalBytesSent < size) {
910         size_t bytesSent = 0;
911         CURLcode errorCode = curl_easy_send(handle(), buffer + totalBytesSent, size - totalBytesSent, &bytesSent);
912         if (errorCode != CURLE_OK) {
913             if (errorCode != CURLE_AGAIN)
914                 m_errorHandler(errorCode);
915             break;
916         }
917
918         totalBytesSent += bytesSent;
919     }
920
921     return totalBytesSent;
922 }
923
924 Optional<size_t> CurlSocketHandle::receive(uint8_t* buffer, size_t bufferSize)
925 {
926     size_t bytesRead = 0;
927
928     CURLcode errorCode = curl_easy_recv(handle(), buffer, bufferSize, &bytesRead);
929     if (errorCode != CURLE_OK) {
930         if (errorCode != CURLE_AGAIN)
931             m_errorHandler(errorCode);
932
933         return WTF::nullopt;
934     }
935
936     return bytesRead;
937 }
938
939 Optional<CurlSocketHandle::WaitResult> CurlSocketHandle::wait(const Seconds& timeout, bool alsoWaitForWrite)
940 {
941     curl_socket_t socket;
942     CURLcode errorCode = curl_easy_getinfo(handle(), CURLINFO_ACTIVESOCKET, &socket);
943     if (errorCode != CURLE_OK) {
944         m_errorHandler(errorCode);
945         return WTF::nullopt;
946     }
947
948     int64_t usec = timeout.microsecondsAs<int64_t>();
949
950     struct timeval selectTimeout;
951     if (usec <= 0) {
952         selectTimeout.tv_sec = 0;
953         selectTimeout.tv_usec = 0;
954     } else {
955         selectTimeout.tv_sec = usec / 1000000;
956         selectTimeout.tv_usec = usec % 1000000;
957     }
958
959     int rc = 0;
960     int maxfd = static_cast<int>(socket) + 1;
961     fd_set fdread;
962     fd_set fdwrite;
963     fd_set fderr;
964
965     // Retry 'select' if it was interrupted by a process signal.
966     do {
967         FD_ZERO(&fdread);
968         FD_SET(socket, &fdread);
969
970         FD_ZERO(&fdwrite);
971         if (alsoWaitForWrite)
972             FD_SET(socket, &fdwrite);
973
974         FD_ZERO(&fderr);
975         FD_SET(socket, &fderr);
976
977         rc = ::select(maxfd, &fdread, &fdwrite, &fderr, &selectTimeout);
978     } while (rc == -1 && errno == EINTR);
979
980     if (rc <= 0)
981         return WTF::nullopt;
982
983     WaitResult result;
984     result.readable = FD_ISSET(socket, &fdread) || FD_ISSET(socket, &fderr);
985     result.writable = FD_ISSET(socket, &fdwrite);
986     return result;
987 }
988
989 }
990
991 #endif