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