[Curl] Use SQLite database in cookie jar implementation for Curl port
[WebKit-https.git] / Source / WebCore / platform / network / curl / CurlContext.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc.  All rights reserved.
3  * Copyright (C) 2017 Sony Interactive Entertainment Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "CurlContext.h"
29
30 #if USE(CURL)
31 #include "HTTPHeaderMap.h"
32 #include <NetworkLoadMetrics.h>
33 #include <wtf/MainThread.h>
34 #include <wtf/NeverDestroyed.h>
35 #include <wtf/text/CString.h>
36
37 #if OS(WINDOWS)
38 #include "WebCoreBundleWin.h"
39 #include <shlobj.h>
40 #include <shlwapi.h>
41 #endif
42
43 namespace WebCore {
44
45 // CurlContext -------------------------------------------------------------------
46
47 CurlContext& CurlContext::singleton()
48 {
49     static NeverDestroyed<CurlContext> sharedInstance;
50     return sharedInstance;
51 }
52
53 CurlContext::CurlContext()
54 {
55     initShareHandle();
56
57 #ifndef NDEBUG
58     m_verbose = getenv("DEBUG_CURL");
59
60     char* logFile = getenv("CURL_LOG_FILE");
61     if (logFile)
62         m_logFile = fopen(logFile, "a");
63 #endif
64 }
65
66 CurlContext::~CurlContext()
67 {
68 #ifndef NDEBUG
69     if (m_logFile)
70         fclose(m_logFile);
71 #endif
72 }
73
74 void CurlContext::initShareHandle()
75 {
76     CURL* curl = curl_easy_init();
77
78     if (!curl)
79         return;
80
81     curl_easy_setopt(curl, CURLOPT_SHARE, m_shareHandle.handle());
82
83     curl_easy_cleanup(curl);
84 }
85
86 // Proxy =======================
87
88 const String CurlContext::ProxyInfo::url() const
89 {
90     String userPass;
91     if (username.length() || password.length())
92         userPass = username + ":" + password + "@";
93
94     return String("http://") + userPass + host + ":" + String::number(port);
95 }
96
97 void CurlContext::setProxyInfo(const String& host,
98     unsigned long port,
99     CurlProxyType type,
100     const String& username,
101     const String& password)
102 {
103     ProxyInfo info;
104
105     info.host = host;
106     info.port = port;
107     info.type = type;
108     info.username = username;
109     info.password = password;
110
111     setProxyInfo(info);
112 }
113
114 bool CurlContext::isHttp2Enabled() const
115 {
116     curl_version_info_data* data = curl_version_info(CURLVERSION_NOW);
117     return data->features & CURL_VERSION_HTTP2;
118 }
119
120 // CurlShareHandle --------------------------------------------
121
122 CurlShareHandle::CurlShareHandle()
123 {
124     m_shareHandle = curl_share_init();
125     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
126     curl_share_setopt(m_shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
127     curl_share_setopt(m_shareHandle, CURLSHOPT_LOCKFUNC, lockCallback);
128     curl_share_setopt(m_shareHandle, CURLSHOPT_UNLOCKFUNC, unlockCallback);
129 }
130
131 CurlShareHandle::~CurlShareHandle()
132 {
133     if (m_shareHandle)
134         curl_share_cleanup(m_shareHandle);
135 }
136
137 void CurlShareHandle::lockCallback(CURL*, curl_lock_data data, curl_lock_access, void*)
138 {
139     if (auto* mutex = mutexFor(data))
140         mutex->lock();
141 }
142
143 void CurlShareHandle::unlockCallback(CURL*, curl_lock_data data, void*)
144 {
145     if (auto* mutex = mutexFor(data))
146         mutex->unlock();
147 }
148
149 StaticLock* CurlShareHandle::mutexFor(curl_lock_data data)
150 {
151     static StaticLock cookieMutex;
152     static StaticLock dnsMutex;
153     static StaticLock shareMutex;
154
155     switch (data) {
156     case CURL_LOCK_DATA_COOKIE:
157         return &cookieMutex;
158     case CURL_LOCK_DATA_DNS:
159         return &dnsMutex;
160     case CURL_LOCK_DATA_SHARE:
161         return &shareMutex;
162     default:
163         ASSERT_NOT_REACHED();
164         return nullptr;
165     }
166 }
167
168 // CurlMultiHandle --------------------------------------------
169
170 CurlMultiHandle::CurlMultiHandle()
171 {
172     m_multiHandle = curl_multi_init();
173 }
174
175 CurlMultiHandle::~CurlMultiHandle()
176 {
177     if (m_multiHandle)
178         curl_multi_cleanup(m_multiHandle);
179 }
180
181 CURLMcode CurlMultiHandle::addHandle(CURL* handle)
182 {
183     return curl_multi_add_handle(m_multiHandle, handle);
184 }
185
186 CURLMcode CurlMultiHandle::removeHandle(CURL* handle)
187 {
188     return curl_multi_remove_handle(m_multiHandle, handle);
189 }
190
191 CURLMcode CurlMultiHandle::getFdSet(fd_set& readFdSet, fd_set& writeFdSet, fd_set& excFdSet, int& maxFd)
192 {
193     FD_ZERO(&readFdSet);
194     FD_ZERO(&writeFdSet);
195     FD_ZERO(&excFdSet);
196     maxFd = 0;
197
198     return curl_multi_fdset(m_multiHandle, &readFdSet, &writeFdSet, &excFdSet, &maxFd);
199 }
200
201 CURLMcode CurlMultiHandle::perform(int& runningHandles)
202 {
203     return curl_multi_perform(m_multiHandle, &runningHandles);
204 }
205
206 CURLMsg* CurlMultiHandle::readInfo(int& messagesInQueue)
207 {
208     return curl_multi_info_read(m_multiHandle, &messagesInQueue);
209 }
210
211 // CurlHandle -------------------------------------------------
212
213 CurlHandle::CurlHandle()
214 {
215     m_handle = curl_easy_init();
216 }
217
218 CurlHandle::~CurlHandle()
219 {
220     if (m_handle)
221         curl_easy_cleanup(m_handle);
222 }
223
224 const String CurlHandle::errorDescription(CURLcode errorCode)
225 {
226     return String(curl_easy_strerror(errorCode));
227 }
228
229 void CurlHandle::initialize()
230 {
231     curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
232 }
233
234 CURLcode CurlHandle::perform()
235 {
236     return curl_easy_perform(m_handle);
237 }
238
239 CURLcode CurlHandle::pause(int bitmask)
240 {
241     return curl_easy_pause(m_handle, bitmask);
242 }
243
244 void CurlHandle::enableShareHandle()
245 {
246     curl_easy_setopt(m_handle, CURLOPT_SHARE, CurlContext::singleton().shareHandle().handle());
247 }
248
249 void CurlHandle::setUrl(const URL& url)
250 {
251     URL curlUrl = url;
252
253     // Remove any fragment part, otherwise curl will send it as part of the request.
254     curlUrl.removeFragmentIdentifier();
255
256     // Remove any query part sent to a local file.
257     if (curlUrl.isLocalFile()) {
258         // By setting the query to a null string it'll be removed.
259         if (!curlUrl.query().isEmpty())
260             curlUrl.setQuery(String());
261     }
262
263     // url is in ASCII so latin1() will only convert it to char* without character translation.
264     curl_easy_setopt(m_handle, CURLOPT_URL, curlUrl.string().latin1().data());
265 }
266
267 void CurlHandle::appendRequestHeaders(const HTTPHeaderMap& headers)
268 {
269     if (headers.size()) {
270         for (auto& entry : headers) {
271             auto& value = entry.value;
272             appendRequestHeader(entry.key, entry.value);
273         }
274     }
275 }
276
277 void CurlHandle::appendRequestHeader(const String& name, const String& value)
278 {
279     String header(name);
280
281     if (value.isEmpty()) {
282         // Insert the ; to tell curl that this header has an empty value.
283         header.append(";");
284     } else {
285         header.append(": ");
286         header.append(value);
287     }
288
289     appendRequestHeader(header);
290 }
291
292 void CurlHandle::removeRequestHeader(const String& name)
293 {
294     // Add a header with no content, the internally used header will get disabled. 
295     String header(name);
296     header.append(":");
297
298     appendRequestHeader(header);
299 }
300
301 void CurlHandle::appendRequestHeader(const String& header)
302 {
303     bool needToEnable = m_requestHeaders.isEmpty();
304
305     m_requestHeaders.append(header);
306
307     if (needToEnable)
308         enableRequestHeaders();
309 }
310
311 void CurlHandle::enableRequestHeaders()
312 {
313     if (m_requestHeaders.isEmpty())
314         return;
315
316     const struct curl_slist* headers = m_requestHeaders.head();
317     curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers);
318 }
319
320 void CurlHandle::enableHttp()
321 {
322     if (CurlContext::singleton().isHttp2Enabled()) {
323         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
324         curl_easy_setopt(m_handle, CURLOPT_PIPEWAIT, 1L);
325         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_ALPN, 1L);
326         curl_easy_setopt(m_handle, CURLOPT_SSL_ENABLE_NPN, 0L);
327     } else
328         curl_easy_setopt(m_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
329 }
330
331 void CurlHandle::enableHttpGetRequest()
332 {
333     enableHttp();
334     curl_easy_setopt(m_handle, CURLOPT_HTTPGET, 1L);
335 }
336
337 void CurlHandle::enableHttpHeadRequest()
338 {
339     enableHttp();
340     curl_easy_setopt(m_handle, CURLOPT_NOBODY, 1L);
341 }
342
343 void CurlHandle::enableHttpPostRequest()
344 {
345     enableHttp();
346     curl_easy_setopt(m_handle, CURLOPT_POST, 1L);
347     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, 0L);
348 }
349
350 void CurlHandle::setPostFields(const char* data, long size)
351 {
352     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDS, data);
353     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE, size);
354 }
355
356 void CurlHandle::setPostFieldLarge(curl_off_t size)
357 {
358     if (expectedSizeOfCurlOffT() != sizeof(long long))
359         size = static_cast<int>(size);
360
361     curl_easy_setopt(m_handle, CURLOPT_POSTFIELDSIZE_LARGE, size);
362 }
363
364 void CurlHandle::enableHttpPutRequest()
365 {
366     enableHttp();
367     curl_easy_setopt(m_handle, CURLOPT_UPLOAD, 1L);
368     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE, 0L);
369 }
370
371 void CurlHandle::setInFileSizeLarge(curl_off_t size)
372 {
373     if (expectedSizeOfCurlOffT() != sizeof(long long))
374         size = static_cast<int>(size);
375
376     curl_easy_setopt(m_handle, CURLOPT_INFILESIZE_LARGE, size);
377 }
378
379 void CurlHandle::setHttpCustomRequest(const String& method)
380 {
381     enableHttp();
382     curl_easy_setopt(m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
383 }
384
385 void CurlHandle::enableAcceptEncoding()
386 {
387     // enable all supported built-in compressions (gzip and deflate) through Accept-Encoding:
388     curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
389 }
390
391 void CurlHandle::enableAllowedProtocols()
392 {
393     static const long allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
394
395     curl_easy_setopt(m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
396 }
397
398 void CurlHandle::enableHttpAuthentication(long option)
399 {
400     curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, option);
401 }
402
403 void CurlHandle::setHttpAuthUserPass(const String& user, const String& password)
404 {
405     curl_easy_setopt(m_handle, CURLOPT_USERNAME, user.utf8().data());
406     curl_easy_setopt(m_handle, CURLOPT_PASSWORD, password.utf8().data());
407 }
408
409 void CurlHandle::setCACertPath(const char* path)
410 {
411     if (path)
412         curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
413 }
414
415 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
416 {
417     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, static_cast<long>(verifyPeer));
418 }
419
420 void CurlHandle::setSslVerifyHost(VerifyHost verifyHost)
421 {
422     curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, static_cast<long>(verifyHost));
423 }
424
425 void CurlHandle::setSslCert(const char* cert)
426 {
427     curl_easy_setopt(m_handle, CURLOPT_SSLCERT, cert);
428 }
429
430 void CurlHandle::setSslCertType(const char* type)
431 {
432     curl_easy_setopt(m_handle, CURLOPT_SSLCERTTYPE, type);
433 }
434
435 void CurlHandle::setSslKeyPassword(const char* password)
436 {
437     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
438 }
439
440 void CurlHandle::enableProxyIfExists()
441 {
442     auto& proxy = CurlContext::singleton().proxyInfo();
443
444     if (proxy.type != CurlProxyType::Invalid) {
445         curl_easy_setopt(m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
446         curl_easy_setopt(m_handle, CURLOPT_PROXYTYPE, proxy.type);
447     }
448 }
449
450 void CurlHandle::enableTimeout()
451 {
452     static const long dnsCacheTimeout = 5 * 60; // [sec.]
453
454     curl_easy_setopt(m_handle, CURLOPT_DNS_CACHE_TIMEOUT, dnsCacheTimeout);
455 }
456
457 void CurlHandle::setTimeout(long timeoutMilliseconds)
458 {
459     curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, timeoutMilliseconds);
460 }
461
462 void CurlHandle::setHeaderCallbackFunction(curl_write_callback callbackFunc, void* userData)
463 {
464     curl_easy_setopt(m_handle, CURLOPT_HEADERFUNCTION, callbackFunc);
465     curl_easy_setopt(m_handle, CURLOPT_HEADERDATA, userData);
466 }
467
468 void CurlHandle::setWriteCallbackFunction(curl_write_callback callbackFunc, void* userData)
469 {
470     curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, callbackFunc);
471     curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, userData);
472 }
473
474 void CurlHandle::setReadCallbackFunction(curl_read_callback callbackFunc, void* userData)
475 {
476     curl_easy_setopt(m_handle, CURLOPT_READFUNCTION, callbackFunc);
477     curl_easy_setopt(m_handle, CURLOPT_READDATA, userData);
478 }
479
480 void CurlHandle::setSslCtxCallbackFunction(curl_ssl_ctx_callback callbackFunc, void* userData)
481 {
482     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_DATA, userData);
483     curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
484 }
485
486 std::optional<uint16_t> CurlHandle::getPrimaryPort()
487 {
488     if (!m_handle)
489         return std::nullopt;
490
491     long port;
492     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_PRIMARY_PORT, &port);
493     if (errorCode != CURLE_OK)
494         return std::nullopt;
495
496     /*
497      * https://github.com/curl/curl/blob/master/lib/connect.c#L612-L660
498      * confirmed that `port` is originally unsigned short.
499      */
500     return static_cast<uint16_t>(port);
501 }
502
503 std::optional<long> CurlHandle::getResponseCode()
504 {
505     if (!m_handle)
506         return std::nullopt;
507
508     long responseCode;
509     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &responseCode);
510     if (errorCode != CURLE_OK)
511         return std::nullopt;
512
513     return responseCode;
514 }
515
516 std::optional<long> CurlHandle::getHttpConnectCode()
517 {
518     if (!m_handle)
519         return std::nullopt;
520
521     long httpConnectCode;
522     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_CONNECTCODE, &httpConnectCode);
523     if (errorCode != CURLE_OK)
524         return std::nullopt;
525
526     return httpConnectCode;
527 }
528
529 std::optional<long long> CurlHandle::getContentLength()
530 {
531     if (!m_handle)
532         return std::nullopt;
533
534     double contentLength;
535
536     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
537     if (errorCode != CURLE_OK)
538         return std::nullopt;
539
540     return static_cast<long long>(contentLength);
541 }
542
543 std::optional<long> CurlHandle::getHttpAuthAvail()
544 {
545     if (!m_handle)
546         return std::nullopt;
547
548     long httpAuthAvailable;
549     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTPAUTH_AVAIL, &httpAuthAvailable);
550     if (errorCode != CURLE_OK)
551         return std::nullopt;
552
553     return httpAuthAvailable;
554 }
555
556 std::optional<long> CurlHandle::getHttpVersion()
557 {
558     if (!m_handle)
559         return std::nullopt;
560
561     long version;
562     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_HTTP_VERSION, &version);
563     if (errorCode != CURLE_OK)
564         return std::nullopt;
565
566     return version;
567 }
568
569 std::optional<NetworkLoadMetrics> CurlHandle::getNetworkLoadMetrics()
570 {
571     double nameLookup = 0.0;
572     double connect = 0.0;
573     double appConnect = 0.0;
574     double startTransfer = 0.0;
575
576     if (!m_handle)
577         return std::nullopt;
578
579     CURLcode errorCode = curl_easy_getinfo(m_handle, CURLINFO_NAMELOOKUP_TIME, &nameLookup);
580     if (errorCode != CURLE_OK)
581         return std::nullopt;
582
583     errorCode = curl_easy_getinfo(m_handle, CURLINFO_CONNECT_TIME, &connect);
584     if (errorCode != CURLE_OK)
585         return std::nullopt;
586
587     errorCode = curl_easy_getinfo(m_handle, CURLINFO_APPCONNECT_TIME, &appConnect);
588     if (errorCode != CURLE_OK)
589         return std::nullopt;
590
591     errorCode = curl_easy_getinfo(m_handle, CURLINFO_STARTTRANSFER_TIME, &startTransfer);
592     if (errorCode != CURLE_OK)
593         return std::nullopt;
594
595     NetworkLoadMetrics networkLoadMetrics;
596
597     networkLoadMetrics.domainLookupStart = Seconds(0);
598     networkLoadMetrics.domainLookupEnd = Seconds(nameLookup);
599     networkLoadMetrics.connectStart = Seconds(nameLookup);
600     networkLoadMetrics.connectEnd = Seconds(connect);
601
602     if (appConnect > 0.0) {
603         networkLoadMetrics.secureConnectionStart = Seconds(connect);
604         networkLoadMetrics.connectEnd = Seconds(appConnect);
605     }
606
607     networkLoadMetrics.requestStart = networkLoadMetrics.connectEnd;
608     networkLoadMetrics.responseStart = Seconds(startTransfer);
609
610     return networkLoadMetrics;
611 }
612
613 long long CurlHandle::maxCurlOffT()
614 {
615     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT() * 8 - 1)) - 1;
616
617     return maxCurlOffT;
618 }
619
620 int CurlHandle::expectedSizeOfCurlOffT()
621 {
622     // The size of a curl_off_t could be different in WebKit and in cURL depending on
623     // compilation flags of both.
624     static int expectedSizeOfCurlOffT = 0;
625     if (!expectedSizeOfCurlOffT) {
626         curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
627         if (infoData->features & CURL_VERSION_LARGEFILE)
628             expectedSizeOfCurlOffT = sizeof(long long);
629         else
630             expectedSizeOfCurlOffT = sizeof(int);
631     }
632
633     return expectedSizeOfCurlOffT;
634 }
635
636 #ifndef NDEBUG
637
638 void CurlHandle::enableVerboseIfUsed()
639 {
640     if (CurlContext::singleton().isVerbose())
641         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
642 }
643
644 void CurlHandle::enableStdErrIfUsed()
645 {
646     if (CurlContext::singleton().getLogFile())
647         curl_easy_setopt(m_handle, CURLOPT_VERBOSE, 1);
648 }
649
650 #endif
651
652 }
653
654 #endif