Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cpl_http.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  libcurl based HTTP client
4
 * Purpose:  libcurl based HTTP client
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2006, Frank Warmerdam
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "cpl_http.h"
16
17
#include <cstddef>
18
#include <cstring>
19
20
#include <algorithm>
21
#include <array>
22
#include <map>
23
#include <mutex>
24
#include <string>
25
#include <vector>
26
27
#include "cpl_http.h"
28
#include "cpl_error.h"
29
#include "cpl_multiproc.h"
30
#include "cpl_vsi_virtual.h"
31
#include "cpl_vsil_curl_class.h"
32
33
// gcc or clang complains about C-style cast in #define like
34
// CURL_ZERO_TERMINATED
35
#if defined(__GNUC__)
36
#pragma GCC diagnostic push
37
#pragma GCC diagnostic ignored "-Wold-style-cast"
38
#endif
39
40
#ifdef HAVE_CURL
41
42
#include "cpl_curl_priv.h"
43
44
#ifdef HAVE_OPENSSL_CRYPTO
45
#include <openssl/err.h>
46
#include <openssl/ssl.h>
47
#include <openssl/x509v3.h>
48
49
#if defined(_WIN32)
50
#include <wincrypt.h>
51
#endif
52
53
#endif
54
55
#ifdef HAVE_SIGACTION
56
#include <signal.h>
57
#endif
58
59
#define unchecked_curl_easy_setopt(handle, opt, param)                         \
60
    CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
61
62
#endif  // HAVE_CURL
63
64
// list of named persistent http sessions
65
66
#ifdef HAVE_CURL
67
static std::map<CPLString, CURL *> *poSessionMap = nullptr;
68
static std::map<CPLString, CURLM *> *poSessionMultiMap = nullptr;
69
static CPLMutex *hSessionMapMutex = nullptr;
70
static bool bHasCheckVersion = false;
71
static bool bSupportGZip = false;
72
static bool bSupportHTTP2 = false;
73
#if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
74
static std::vector<X509 *> *poWindowsCertificateList = nullptr;
75
76
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
77
#define EVP_PKEY_get0_RSA(x) (x->pkey.rsa)
78
#define EVP_PKEY_get0_DSA(x) (x->pkey.dsa)
79
#define X509_get_extension_flags(x) (x->ex_flags)
80
#define X509_get_key_usage(x) (x->ex_kusage)
81
#define X509_get_extended_key_usage(x) (x->ex_xkusage)
82
#endif
83
84
#endif  // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
85
86
#if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
87
88
// Ported from https://curl.haxx.se/libcurl/c/opensslthreadlock.html
89
static CPLMutex **pahSSLMutex = nullptr;
90
91
static void CPLOpenSSLLockingFunction(int mode, int n, const char * /*file*/,
92
                                      int /*line*/)
93
{
94
    if (mode & CRYPTO_LOCK)
95
    {
96
        CPLAcquireMutex(pahSSLMutex[n], 3600.0);
97
    }
98
    else
99
    {
100
        CPLReleaseMutex(pahSSLMutex[n]);
101
    }
102
}
103
104
static unsigned long CPLOpenSSLIdCallback(void)
105
{
106
    return static_cast<unsigned long>(CPLGetPID());
107
}
108
109
static void CPLOpenSSLInit()
110
{
111
    if (strstr(curl_version(), "OpenSSL") &&
112
        CPLTestBool(CPLGetConfigOption("CPL_OPENSSL_INIT_ENABLED", "YES")) &&
113
        CRYPTO_get_id_callback() == nullptr)
114
    {
115
        pahSSLMutex = static_cast<CPLMutex **>(
116
            CPLMalloc(CRYPTO_num_locks() * sizeof(CPLMutex *)));
117
        for (int i = 0; i < CRYPTO_num_locks(); i++)
118
        {
119
            pahSSLMutex[i] = CPLCreateMutex();
120
            CPLReleaseMutex(pahSSLMutex[i]);
121
        }
122
        CRYPTO_set_id_callback(CPLOpenSSLIdCallback);
123
        CRYPTO_set_locking_callback(CPLOpenSSLLockingFunction);
124
    }
125
}
126
127
static void CPLOpenSSLCleanup()
128
{
129
    if (pahSSLMutex)
130
    {
131
        for (int i = 0; i < CRYPTO_num_locks(); i++)
132
        {
133
            CPLDestroyMutex(pahSSLMutex[i]);
134
        }
135
        CPLFree(pahSSLMutex);
136
        pahSSLMutex = nullptr;
137
        CRYPTO_set_id_callback(nullptr);
138
        CRYPTO_set_locking_callback(nullptr);
139
    }
140
}
141
142
#endif
143
144
#if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
145
146
/************************************************************************/
147
/*                    CPLWindowsCertificateListCleanup()                */
148
/************************************************************************/
149
150
static void CPLWindowsCertificateListCleanup()
151
{
152
    if (poWindowsCertificateList)
153
    {
154
        for (auto &&pX509 : *poWindowsCertificateList)
155
        {
156
            X509_free(pX509);
157
        }
158
        delete poWindowsCertificateList;
159
        poWindowsCertificateList = nullptr;
160
    }
161
}
162
163
/************************************************************************/
164
/*                       LoadCAPICertificates()                         */
165
/************************************************************************/
166
167
static CPLErr LoadCAPICertificates(const char *pszName,
168
                                   std::vector<X509 *> *poCertificateList)
169
{
170
    CPLAssert(pszName);
171
    CPLAssert(poCertificateList);
172
173
    HCERTSTORE pCertStore = CertOpenSystemStore(
174
        reinterpret_cast<HCRYPTPROV_LEGACY>(nullptr), pszName);
175
    if (pCertStore == nullptr)
176
    {
177
        CPLError(CE_Failure, CPLE_AppDefined,
178
                 "CPLLoadCAPICertificates(): Unable open system "
179
                 "certificate store %s.",
180
                 pszName);
181
        return CE_Failure;
182
    }
183
184
    PCCERT_CONTEXT pCertificate =
185
        CertEnumCertificatesInStore(pCertStore, nullptr);
186
    while (pCertificate != nullptr)
187
    {
188
        X509 *pX509 = d2i_X509(
189
            nullptr,
190
            const_cast<unsigned char const **>(&pCertificate->pbCertEncoded),
191
            pCertificate->cbCertEncoded);
192
        if (pX509 == nullptr)
193
        {
194
            CPLError(CE_Warning, CPLE_AppDefined,
195
                     "CPLLoadCAPICertificates(): CertEnumCertificatesInStore() "
196
                     "returned a null certificate, skipping.");
197
        }
198
        else
199
        {
200
#ifdef DEBUG_VERBOSE
201
            char szSubject[256] = {0};
202
            CPLString osSubject;
203
            X509_NAME *pName = X509_get_subject_name(pX509);
204
            if (pName)
205
            {
206
                X509_NAME_oneline(pName, szSubject, sizeof(szSubject));
207
                osSubject = szSubject;
208
            }
209
            if (!osSubject.empty())
210
                CPLDebug("HTTP", "SSL Certificate: %s", osSubject.c_str());
211
#endif
212
            poCertificateList->push_back(pX509);
213
        }
214
        pCertificate = CertEnumCertificatesInStore(pCertStore, pCertificate);
215
    }
216
    CertCloseStore(pCertStore, 0);
217
    return CE_None;
218
}
219
220
/************************************************************************/
221
/*                       CPL_ssl_ctx_callback()                         */
222
/************************************************************************/
223
224
// Load certificates from Windows Crypto API store.
225
static CURLcode CPL_ssl_ctx_callback(CURL *, void *pSSL, void *)
226
{
227
    SSL_CTX *pSSL_CTX = static_cast<SSL_CTX *>(pSSL);
228
    if (pSSL_CTX == nullptr)
229
    {
230
        CPLError(CE_Failure, CPLE_AppDefined,
231
                 "CPL_ssl_ctx_callback(): OpenSSL context pointer is NULL.");
232
        return CURLE_ABORTED_BY_CALLBACK;
233
    }
234
235
    static std::mutex goMutex;
236
    {
237
        std::lock_guard<std::mutex> oLock(goMutex);
238
        if (poWindowsCertificateList == nullptr)
239
        {
240
            poWindowsCertificateList = new std::vector<X509 *>();
241
            if (!poWindowsCertificateList)
242
            {
243
                CPLError(CE_Failure, CPLE_AppDefined,
244
                         "CPL_ssl_ctx_callback(): Unable to allocate "
245
                         "structure to hold certificates.");
246
                return CURLE_FAILED_INIT;
247
            }
248
249
            const std::array<const char *, 3> aszStores{
250
                {"CA", "AuthRoot", "ROOT"}};
251
            for (auto &&pszStore : aszStores)
252
            {
253
                if (LoadCAPICertificates(pszStore, poWindowsCertificateList) ==
254
                    CE_Failure)
255
                {
256
                    CPLError(
257
                        CE_Failure, CPLE_AppDefined,
258
                        "CPL_ssl_ctx_callback(): Unable to load certificates "
259
                        "from '%s' store.",
260
                        pszStore);
261
                    return CURLE_FAILED_INIT;
262
                }
263
            }
264
265
            CPLDebug("HTTP", "Loading %d certificates from Windows store.",
266
                     static_cast<int>(poWindowsCertificateList->size()));
267
        }
268
    }
269
270
    X509_STORE *pX509Store = SSL_CTX_get_cert_store(pSSL_CTX);
271
    for (X509 *x509 : *poWindowsCertificateList)
272
        X509_STORE_add_cert(pX509Store, x509);
273
274
    return CURLE_OK;
275
}
276
277
#endif  // defined(_WIN32) && defined (HAVE_OPENSSL_CRYPTO)
278
279
/************************************************************************/
280
/*                       CheckCurlFeatures()                            */
281
/************************************************************************/
282
283
static void CheckCurlFeatures()
284
{
285
    CPLMutexHolder oHolder(&hSessionMapMutex);
286
    if (!bHasCheckVersion)
287
    {
288
        const char *pszVersion = curl_version();
289
        CPLDebug("HTTP", "%s", pszVersion);
290
        bSupportGZip = strstr(pszVersion, "zlib/") != nullptr;
291
        bSupportHTTP2 = strstr(curl_version(), "nghttp2/") != nullptr;
292
        bHasCheckVersion = true;
293
294
        curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
295
        if (data->version_num < LIBCURL_VERSION_NUM)
296
        {
297
            CPLError(CE_Warning, CPLE_AppDefined,
298
                     "GDAL was built against curl %d.%d.%d, but is "
299
                     "running against %s. Runtime failure is likely !",
300
                     LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR,
301
                     LIBCURL_VERSION_PATCH, data->version);
302
        }
303
        else if (data->version_num > LIBCURL_VERSION_NUM)
304
        {
305
            CPLDebug("HTTP",
306
                     "GDAL was built against curl %d.%d.%d, but is "
307
                     "running against %s.",
308
                     LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR,
309
                     LIBCURL_VERSION_PATCH, data->version);
310
        }
311
312
#if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
313
        CPLOpenSSLInit();
314
#endif
315
    }
316
}
317
318
/************************************************************************/
319
/*                            CPLWriteFct()                             */
320
/*                                                                      */
321
/*      Append incoming text to our collection buffer, reallocating     */
322
/*      it larger as needed.                                            */
323
/************************************************************************/
324
325
class CPLHTTPResultWithLimit
326
{
327
  public:
328
    CPLHTTPResult *psResult = nullptr;
329
    int nMaxFileSize = 0;
330
};
331
332
static size_t CPLWriteFct(void *buffer, size_t size, size_t nmemb,
333
                          void *reqInfo)
334
335
{
336
    CPLHTTPResultWithLimit *psResultWithLimit =
337
        static_cast<CPLHTTPResultWithLimit *>(reqInfo);
338
    CPLHTTPResult *psResult = psResultWithLimit->psResult;
339
340
    int nBytesToWrite = static_cast<int>(nmemb) * static_cast<int>(size);
341
    int nNewSize = psResult->nDataLen + nBytesToWrite + 1;
342
    if (nNewSize > psResult->nDataAlloc)
343
    {
344
        psResult->nDataAlloc = static_cast<int>(nNewSize * 1.25 + 100);
345
        GByte *pabyNewData = static_cast<GByte *>(
346
            VSIRealloc(psResult->pabyData, psResult->nDataAlloc));
347
        if (pabyNewData == nullptr)
348
        {
349
            VSIFree(psResult->pabyData);
350
            psResult->pabyData = nullptr;
351
            psResult->pszErrBuf = CPLStrdup(CPLString().Printf(
352
                "Out of memory allocating %d bytes for HTTP data buffer.",
353
                psResult->nDataAlloc));
354
            psResult->nDataAlloc = psResult->nDataLen = 0;
355
356
            return 0;
357
        }
358
        psResult->pabyData = pabyNewData;
359
    }
360
361
    memcpy(psResult->pabyData + psResult->nDataLen, buffer, nBytesToWrite);
362
363
    psResult->nDataLen += nBytesToWrite;
364
    psResult->pabyData[psResult->nDataLen] = 0;
365
366
    if (psResultWithLimit->nMaxFileSize > 0 &&
367
        psResult->nDataLen > psResultWithLimit->nMaxFileSize)
368
    {
369
        CPLError(CE_Failure, CPLE_AppDefined, "Maximum file size reached");
370
        return 0;
371
    }
372
373
    return nmemb;
374
}
375
376
/************************************************************************/
377
/*                           CPLHdrWriteFct()                           */
378
/************************************************************************/
379
static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb,
380
                             void *reqInfo)
381
{
382
    CPLHTTPResult *psResult = static_cast<CPLHTTPResult *>(reqInfo);
383
    // Copy the buffer to a char* and initialize with zeros (zero
384
    // terminate as well).
385
    size_t nBytes = size * nmemb;
386
    char *pszHdr = static_cast<char *>(CPLCalloc(1, nBytes + 1));
387
    memcpy(pszHdr, buffer, nBytes);
388
    size_t nIdx = nBytes - 1;
389
    // Remove end of line characters
390
    while (nIdx > 0 && (pszHdr[nIdx] == '\r' || pszHdr[nIdx] == '\n'))
391
    {
392
        pszHdr[nIdx] = 0;
393
        nIdx--;
394
    }
395
    char *pszKey = nullptr;
396
    const char *pszValue = CPLParseNameValue(pszHdr, &pszKey);
397
    if (pszKey && pszValue)
398
    {
399
        psResult->papszHeaders =
400
            CSLAddNameValue(psResult->papszHeaders, pszKey, pszValue);
401
    }
402
    CPLFree(pszHdr);
403
    CPLFree(pszKey);
404
    return nmemb;
405
}
406
407
/************************************************************************/
408
/*                        CPLHTTPReadFunction()                         */
409
/************************************************************************/
410
static size_t CPLHTTPReadFunction(char *buffer, size_t size, size_t nitems,
411
                                  void *arg)
412
{
413
    return VSIFReadL(buffer, size, nitems, static_cast<VSILFILE *>(arg));
414
}
415
416
/************************************************************************/
417
/*                        CPLHTTPSeekFunction()                         */
418
/************************************************************************/
419
static int CPLHTTPSeekFunction(void *arg, curl_off_t offset, int origin)
420
{
421
    if (VSIFSeekL(static_cast<VSILFILE *>(arg), offset, origin) == 0)
422
        return CURL_SEEKFUNC_OK;
423
    else
424
        return CURL_SEEKFUNC_FAIL;
425
}
426
427
/************************************************************************/
428
/*                        CPLHTTPFreeFunction()                         */
429
/************************************************************************/
430
static void CPLHTTPFreeFunction(void *arg)
431
{
432
    VSIFCloseL(static_cast<VSILFILE *>(arg));
433
}
434
435
typedef struct
436
{
437
    GDALProgressFunc pfnProgress;
438
    void *pProgressArg;
439
} CurlProcessData, *CurlProcessDataL;
440
441
static int NewProcessFunction(void *p, curl_off_t dltotal, curl_off_t dlnow,
442
                              curl_off_t ultotal, curl_off_t ulnow)
443
{
444
    CurlProcessDataL pData = static_cast<CurlProcessDataL>(p);
445
    if (nullptr != pData && pData->pfnProgress)
446
    {
447
        if (dltotal > 0)
448
        {
449
            const double dfDone = double(dlnow) / dltotal;
450
            return pData->pfnProgress(dfDone, "Downloading ...",
451
                                      pData->pProgressArg) == TRUE
452
                       ? 0
453
                       : 1;
454
        }
455
        else if (ultotal > 0)
456
        {
457
            const double dfDone = double(ulnow) / ultotal;
458
            return pData->pfnProgress(dfDone, "Uploading ...",
459
                                      pData->pProgressArg) == TRUE
460
                       ? 0
461
                       : 1;
462
        }
463
    }
464
    return 0;
465
}
466
467
#endif /* def HAVE_CURL */
468
469
/************************************************************************/
470
/*                     CPLHTTPSetDefaultUserAgent()                     */
471
/************************************************************************/
472
473
static std::string gosDefaultUserAgent;
474
475
/**
476
 * \brief Set the default user agent.
477
 *
478
 * GDAL core will by default call this method with "GDAL/x.y.z" where x.y.z
479
 * is the GDAL version number (during driver initialization). Applications may
480
 * override it.
481
 *
482
 * @param pszUserAgent String (or nullptr to cancel the default user agent)
483
 *
484
 * @since GDAL 3.7
485
 */
486
void CPLHTTPSetDefaultUserAgent(const char *pszUserAgent)
487
0
{
488
0
    gosDefaultUserAgent = pszUserAgent ? pszUserAgent : "";
489
0
}
490
491
/************************************************************************/
492
/*                       CPLHTTPGetOptionsFromEnv()                     */
493
/************************************************************************/
494
495
typedef struct
496
{
497
    const char *pszEnvVar;
498
    const char *pszOptionName;
499
} TupleEnvVarOptionName;
500
501
constexpr TupleEnvVarOptionName asAssocEnvVarOptionName[] = {
502
    {"GDAL_HTTP_VERSION", "HTTP_VERSION"},
503
    {"GDAL_HTTP_CONNECTTIMEOUT", "CONNECTTIMEOUT"},
504
    {"GDAL_HTTP_TIMEOUT", "TIMEOUT"},
505
    {"GDAL_HTTP_LOW_SPEED_TIME", "LOW_SPEED_TIME"},
506
    {"GDAL_HTTP_LOW_SPEED_LIMIT", "LOW_SPEED_LIMIT"},
507
    {"GDAL_HTTP_USERPWD", "USERPWD"},
508
    {"GDAL_HTTP_PROXY", "PROXY"},
509
    {"GDAL_HTTPS_PROXY", "HTTPS_PROXY"},
510
    {"GDAL_HTTP_PROXYUSERPWD", "PROXYUSERPWD"},
511
    {"GDAL_PROXY_AUTH", "PROXYAUTH"},
512
    {"GDAL_HTTP_NETRC", "NETRC"},
513
    {"GDAL_HTTP_NETRC_FILE", "NETRC_FILE"},
514
    {"GDAL_HTTP_MAX_RETRY", "MAX_RETRY"},
515
    {"GDAL_HTTP_RETRY_DELAY", "RETRY_DELAY"},
516
    {"GDAL_HTTP_RETRY_CODES", "RETRY_CODES"},
517
    {"GDAL_CURL_CA_BUNDLE", "CAINFO"},
518
    {"CURL_CA_BUNDLE", "CAINFO"},
519
    {"SSL_CERT_FILE", "CAINFO"},
520
    {"GDAL_HTTP_CAPATH", "CAPATH"},
521
    {"GDAL_HTTP_SSL_VERIFYSTATUS", "SSL_VERIFYSTATUS"},
522
    {"GDAL_HTTP_USE_CAPI_STORE", "USE_CAPI_STORE"},
523
    {"GDAL_HTTP_HEADERS", "HEADERS"},
524
    {"GDAL_HTTP_HEADER_FILE", "HEADER_FILE"},
525
    {"GDAL_HTTP_AUTH", "HTTPAUTH"},
526
    {"GDAL_GSSAPI_DELEGATION", "GSSAPI_DELEGATION"},
527
    {"GDAL_HTTP_BEARER", "HTTP_BEARER"},
528
    {"GDAL_HTTP_COOKIE", "COOKIE"},
529
    {"GDAL_HTTP_COOKIEFILE", "COOKIEFILE"},
530
    {"GDAL_HTTP_COOKIEJAR", "COOKIEJAR"},
531
    {"GDAL_HTTP_MAX_RETRY", "MAX_RETRY"},
532
    {"GDAL_HTTP_RETRY_DELAY", "RETRY_DELAY"},
533
    {"GDAL_HTTP_TCP_KEEPALIVE", "TCP_KEEPALIVE"},
534
    {"GDAL_HTTP_TCP_KEEPIDLE", "TCP_KEEPIDLE"},
535
    {"GDAL_HTTP_TCP_KEEPINTVL", "TCP_KEEPINTVL"},
536
};
537
538
char **CPLHTTPGetOptionsFromEnv(const char *pszFilename)
539
0
{
540
0
    CPLStringList aosOptions;
541
0
    std::string osNonStreamingFilename;
542
0
    if (pszFilename && STARTS_WITH(pszFilename, "/vsi"))
543
0
    {
544
0
        VSIFilesystemHandler *poFSHandler =
545
0
            VSIFileManager::GetHandler(pszFilename);
546
0
        osNonStreamingFilename =
547
0
            poFSHandler->GetNonStreamingFilename(pszFilename);
548
0
        if (osNonStreamingFilename == pszFilename)
549
0
        {
550
0
            osNonStreamingFilename.clear();
551
0
        }
552
0
        else
553
0
        {
554
            // CPLDebug("HTTP", "Non-streaming filename for %s: %s", pszFilename, osNonStreamingFilename.c_str());
555
0
        }
556
0
    }
557
0
    for (const auto &sTuple : asAssocEnvVarOptionName)
558
0
    {
559
0
        const char *pszVal = nullptr;
560
0
        if (pszFilename)
561
0
        {
562
0
            pszVal = VSIGetPathSpecificOption(pszFilename, sTuple.pszEnvVar,
563
0
                                              nullptr);
564
0
            if (!pszVal && !osNonStreamingFilename.empty())
565
0
            {
566
0
                pszVal = VSIGetPathSpecificOption(
567
0
                    osNonStreamingFilename.c_str(), sTuple.pszEnvVar, nullptr);
568
0
            }
569
0
        }
570
0
        if (!pszVal)
571
0
        {
572
0
            pszVal = CPLGetConfigOption(sTuple.pszEnvVar, nullptr);
573
0
        }
574
0
        if (pszVal)
575
0
        {
576
0
            aosOptions.AddNameValue(sTuple.pszOptionName, pszVal);
577
0
        }
578
0
    }
579
0
    return aosOptions.StealList();
580
0
}
581
582
/************************************************************************/
583
/*                      CPLHTTPGetNewRetryDelay()                       */
584
/************************************************************************/
585
586
/** Return the new retry delay.
587
 *
588
 * This takes into account the HTTP response code, the previous delay, the
589
 * HTTP payload error message, the Curl error message and a potential list of
590
 * retriable HTTP codes.
591
 *
592
 * @param response_code HTTP response code (e.g. 400)
593
 * @param dfOldDelay Previous delay (nominally in second)
594
 * @param pszErrBuf HTTP response body of the failed request (may be NULL)
595
 * @param pszCurlError Curl error as returned by CURLOPT_ERRORBUFFER (may be NULL)
596
 * @param pszRetriableCodes nullptr to limit to the default hard-coded scenarios,
597
 * "ALL" to ask to retry for all non-200 codes, or a comma-separated list of
598
 * HTTP codes (e.g. "400,500") that are accepted for retry.
599
 * @return the new delay, or 0 if no retry should be attempted.
600
 */
601
static double CPLHTTPGetNewRetryDelay(int response_code, double dfOldDelay,
602
                                      const char *pszErrBuf,
603
                                      const char *pszCurlError,
604
                                      const char *pszRetriableCodes)
605
0
{
606
0
    bool bRetry = false;
607
0
    if (pszRetriableCodes && pszRetriableCodes[0])
608
0
    {
609
0
        bRetry = EQUAL(pszRetriableCodes, "ALL") ||
610
0
                 strstr(pszRetriableCodes, CPLSPrintf("%d", response_code));
611
0
    }
612
0
    else if (response_code == 429 || response_code == 500 ||
613
0
             (response_code >= 502 && response_code <= 504) ||
614
             // S3 sends some client timeout errors as 400 Client Error
615
0
             (response_code == 400 && pszErrBuf &&
616
0
              strstr(pszErrBuf, "RequestTimeout")) ||
617
0
             (pszCurlError &&
618
0
              (strstr(pszCurlError, "Connection timed out") ||
619
0
               strstr(pszCurlError, "Operation timed out") ||
620
0
               strstr(pszCurlError, "Connection reset by peer") ||
621
0
               strstr(pszCurlError, "Connection was reset") ||
622
0
               strstr(pszCurlError, "SSL connection timeout"))))
623
0
    {
624
0
        bRetry = true;
625
0
    }
626
0
    if (bRetry)
627
0
    {
628
        // 'Operation timed out': seen during some long running operation 'hang'
629
        // no error but no response from server and we are in the cURL loop
630
        // infinitely.
631
632
        // 'Connection was reset': was found with Azure: server resets
633
        // connection during TLS handshake (10054 error code). It seems like
634
        // the server process crashed or something forced TCP reset;
635
        // the request succeeds on retry.
636
637
        // Use an exponential backoff factor of 2 plus some random jitter
638
        // We don't care about cryptographic quality randomness, hence:
639
0
#ifndef __COVERITY__
640
0
        return dfOldDelay * (2 + rand() * 0.5 / RAND_MAX);
641
#else
642
        return dfOldDelay * 2;
643
#endif
644
0
    }
645
0
    else
646
0
    {
647
0
        return 0;
648
0
    }
649
0
}
650
651
/*! @cond Doxygen_Suppress */
652
653
/************************************************************************/
654
/*                      CPLHTTPRetryParameters()                        */
655
/************************************************************************/
656
657
/** Constructs a CPLHTTPRetryParameters instance from configuration
658
 * options or path-specific options.
659
 *
660
 * @param aosHTTPOptions HTTP options returned by CPLHTTPGetOptionsFromEnv()
661
 */
662
CPLHTTPRetryParameters::CPLHTTPRetryParameters(
663
    const CPLStringList &aosHTTPOptions)
664
0
    : nMaxRetry(atoi(aosHTTPOptions.FetchNameValueDef(
665
0
          "MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)))),
666
0
      dfInitialDelay(CPLAtof(aosHTTPOptions.FetchNameValueDef(
667
0
          "RETRY_DELAY", CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)))),
668
0
      osRetryCodes(aosHTTPOptions.FetchNameValueDef("RETRY_CODES", ""))
669
0
{
670
0
}
671
672
/************************************************************************/
673
/*                        CPLHTTPRetryContext()                         */
674
/************************************************************************/
675
676
/** Constructor */
677
CPLHTTPRetryContext::CPLHTTPRetryContext(const CPLHTTPRetryParameters &oParams)
678
0
    : m_oParameters(oParams), m_dfNextDelay(oParams.dfInitialDelay)
679
0
{
680
0
}
681
682
/************************************************************************/
683
/*                     CPLHTTPRetryContext::CanRetry()                  */
684
/************************************************************************/
685
686
/** Returns whether we can attempt a new retry, based on the retry counter,
687
 * and increment that counter.
688
 */
689
bool CPLHTTPRetryContext::CanRetry()
690
0
{
691
0
    if (m_nRetryCount >= m_oParameters.nMaxRetry)
692
0
        return false;
693
0
    m_nRetryCount++;
694
0
    return true;
695
0
}
696
697
/** Returns whether we can attempt a new retry, based on the retry counter,
698
 * the response code, payload and curl error buffers.
699
 *
700
 * If successful, the retry counter is incremented, and GetCurrentDelay()
701
 * returns the delay to apply with CPLSleep().
702
 */
703
bool CPLHTTPRetryContext::CanRetry(int response_code, const char *pszErrBuf,
704
                                   const char *pszCurlError)
705
0
{
706
0
    if (m_nRetryCount >= m_oParameters.nMaxRetry)
707
0
        return false;
708
0
    m_dfCurDelay = m_dfNextDelay;
709
0
    m_dfNextDelay = CPLHTTPGetNewRetryDelay(response_code, m_dfNextDelay,
710
0
                                            pszErrBuf, pszCurlError,
711
0
                                            m_oParameters.osRetryCodes.c_str());
712
0
    if (m_dfNextDelay == 0.0)
713
0
        return false;
714
0
    m_nRetryCount++;
715
0
    return true;
716
0
}
717
718
/************************************************************************/
719
/*                CPLHTTPRetryContext::GetCurrentDelay()                */
720
/************************************************************************/
721
722
/** Returns the delay to apply. Only valid after a successful call to CanRetry() */
723
double CPLHTTPRetryContext::GetCurrentDelay() const
724
0
{
725
0
    if (m_nRetryCount == 0)
726
0
        CPLDebug("CPL",
727
0
                 "GetCurrentDelay() should only be called after CanRetry()");
728
0
    return m_dfCurDelay;
729
0
}
730
731
/*! @endcond Doxygen_Suppress */
732
733
#ifdef HAVE_CURL
734
735
/************************************************************************/
736
/*                      CPLHTTPEmitFetchDebug()                         */
737
/************************************************************************/
738
739
static void CPLHTTPEmitFetchDebug(const char *pszURL,
740
                                  const char *pszExtraDebug = "")
741
{
742
    const char *pszArobase = strchr(pszURL, '@');
743
    const char *pszSlash = strchr(pszURL, '/');
744
    const char *pszColon = (pszSlash) ? strchr(pszSlash, ':') : nullptr;
745
    if (pszArobase != nullptr && pszColon != nullptr &&
746
        pszArobase - pszColon > 0)
747
    {
748
        /* http://user:password@www.example.com */
749
        char *pszSanitizedURL = CPLStrdup(pszURL);
750
        pszSanitizedURL[pszColon - pszURL] = 0;
751
        CPLDebug("HTTP", "Fetch(%s:#password#%s%s)", pszSanitizedURL,
752
                 pszArobase, pszExtraDebug);
753
        CPLFree(pszSanitizedURL);
754
    }
755
    else
756
    {
757
        CPLDebug("HTTP", "Fetch(%s%s)", pszURL, pszExtraDebug);
758
    }
759
}
760
761
#endif
762
763
#ifdef HAVE_CURL
764
765
/************************************************************************/
766
/*                      class CPLHTTPPostFields                         */
767
/************************************************************************/
768
769
class CPLHTTPPostFields
770
{
771
  public:
772
    CPLHTTPPostFields() = default;
773
    CPLHTTPPostFields &operator=(const CPLHTTPPostFields &) = delete;
774
    CPLHTTPPostFields(const CPLHTTPPostFields &) = delete;
775
776
    CPLErr Fill(CURL *http_handle, CSLConstList papszOptions)
777
    {
778
        // Fill POST form if present
779
        const char *pszFormFilePath =
780
            CSLFetchNameValue(papszOptions, "FORM_FILE_PATH");
781
        const char *pszParametersCount =
782
            CSLFetchNameValue(papszOptions, "FORM_ITEM_COUNT");
783
784
        if (pszFormFilePath != nullptr || pszParametersCount != nullptr)
785
        {
786
            mime = curl_mime_init(http_handle);
787
            curl_mimepart *mimepart = curl_mime_addpart(mime);
788
            if (pszFormFilePath != nullptr)
789
            {
790
                const char *pszFormFileName =
791
                    CSLFetchNameValue(papszOptions, "FORM_FILE_NAME");
792
                const char *pszFilename = CPLGetFilename(pszFormFilePath);
793
                if (pszFormFileName == nullptr)
794
                {
795
                    pszFormFileName = pszFilename;
796
                }
797
798
                VSIStatBufL sStat;
799
                if (VSIStatL(pszFormFilePath, &sStat) == 0)
800
                {
801
                    VSILFILE *mime_fp = VSIFOpenL(pszFormFilePath, "rb");
802
                    if (mime_fp != nullptr)
803
                    {
804
                        curl_mime_name(mimepart, pszFormFileName);
805
                        CPL_IGNORE_RET_VAL(
806
                            curl_mime_filename(mimepart, pszFilename));
807
                        curl_mime_data_cb(
808
                            mimepart, sStat.st_size, CPLHTTPReadFunction,
809
                            CPLHTTPSeekFunction, CPLHTTPFreeFunction, mime_fp);
810
                    }
811
                    else
812
                    {
813
                        osErrMsg = CPLSPrintf("Failed to open file %s",
814
                                              pszFormFilePath);
815
                        return CE_Failure;
816
                    }
817
818
                    CPLDebug("HTTP", "Send file: %s, COPYNAME: %s",
819
                             pszFormFilePath, pszFormFileName);
820
                }
821
                else
822
                {
823
                    osErrMsg =
824
                        CPLSPrintf("File '%s' not found", pszFormFilePath);
825
                    return CE_Failure;
826
                }
827
            }
828
829
            int nParametersCount = 0;
830
            if (pszParametersCount != nullptr)
831
            {
832
                nParametersCount = atoi(pszParametersCount);
833
            }
834
835
            for (int i = 0; i < nParametersCount; ++i)
836
            {
837
                const char *pszKey = CSLFetchNameValue(
838
                    papszOptions, CPLSPrintf("FORM_KEY_%d", i));
839
                const char *pszValue = CSLFetchNameValue(
840
                    papszOptions, CPLSPrintf("FORM_VALUE_%d", i));
841
842
                if (nullptr == pszKey)
843
                {
844
                    osErrMsg = CPLSPrintf("Key #%d is not exists. Maybe wrong "
845
                                          "count of form items",
846
                                          i);
847
                    return CE_Failure;
848
                }
849
850
                if (nullptr == pszValue)
851
                {
852
                    osErrMsg = CPLSPrintf("Value #%d is not exists. Maybe "
853
                                          "wrong count of form items",
854
                                          i);
855
                    return CE_Failure;
856
                }
857
858
                mimepart = curl_mime_addpart(mime);
859
                curl_mime_name(mimepart, pszKey);
860
                CPL_IGNORE_RET_VAL(
861
                    curl_mime_data(mimepart, pszValue, CURL_ZERO_TERMINATED));
862
863
                CPLDebug("HTTP", "COPYNAME: %s, COPYCONTENTS: %s", pszKey,
864
                         pszValue);
865
            }
866
867
            unchecked_curl_easy_setopt(http_handle, CURLOPT_MIMEPOST, mime);
868
        }
869
        return CE_None;
870
    }
871
872
    ~CPLHTTPPostFields()
873
    {
874
        if (mime != nullptr)
875
        {
876
            curl_mime_free(mime);
877
        }
878
    }
879
880
    std::string GetErrorMessage() const
881
    {
882
        return osErrMsg;
883
    }
884
885
  private:
886
    curl_mime *mime = nullptr;
887
    std::string osErrMsg{};
888
};
889
890
/************************************************************************/
891
/*                       CPLHTTPFetchCleanup()                          */
892
/************************************************************************/
893
894
static void CPLHTTPFetchCleanup(CURL *http_handle, struct curl_slist *headers,
895
                                const char *pszPersistent,
896
                                CSLConstList papszOptions)
897
{
898
    if (CSLFetchNameValue(papszOptions, "POSTFIELDS"))
899
        unchecked_curl_easy_setopt(http_handle, CURLOPT_POST, 0);
900
    unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, nullptr);
901
902
    if (!pszPersistent)
903
        curl_easy_cleanup(http_handle);
904
905
    curl_slist_free_all(headers);
906
}
907
#endif  // HAVE_CURL
908
909
struct CPLHTTPFetchContext
910
{
911
    std::vector<std::pair<CPLHTTPFetchCallbackFunc, void *>> stack{};
912
};
913
914
/************************************************************************/
915
/*                        GetHTTPFetchContext()                         */
916
/************************************************************************/
917
918
static CPLHTTPFetchContext *GetHTTPFetchContext(bool bAlloc)
919
0
{
920
0
    int bError = FALSE;
921
0
    CPLHTTPFetchContext *psCtx = static_cast<CPLHTTPFetchContext *>(
922
0
        CPLGetTLSEx(CTLS_HTTPFETCHCALLBACK, &bError));
923
0
    if (bError)
924
0
        return nullptr;
925
926
0
    if (psCtx == nullptr && bAlloc)
927
0
    {
928
0
        const auto FreeFunc = [](void *pData)
929
0
        { delete static_cast<CPLHTTPFetchContext *>(pData); };
930
0
        psCtx = new CPLHTTPFetchContext();
931
0
        CPLSetTLSWithFreeFuncEx(CTLS_HTTPFETCHCALLBACK, psCtx, FreeFunc,
932
0
                                &bError);
933
0
        if (bError)
934
0
        {
935
0
            delete psCtx;
936
0
            psCtx = nullptr;
937
0
        }
938
0
    }
939
0
    return psCtx;
940
0
}
941
942
/************************************************************************/
943
/*                      CPLHTTPSetFetchCallback()                       */
944
/************************************************************************/
945
946
static CPLHTTPFetchCallbackFunc gpsHTTPFetchCallbackFunc = nullptr;
947
static void *gpHTTPFetchCallbackUserData = nullptr;
948
949
/** Installs an alternate callback to the default implementation of
950
 * CPLHTTPFetchEx().
951
 *
952
 * This callback will be used by all threads, unless contextual callbacks are
953
 * installed with CPLHTTPPushFetchCallback().
954
 *
955
 * It is the responsibility of the caller to make sure this function is not
956
 * called concurrently, or during CPLHTTPFetchEx() execution.
957
 *
958
 * @param pFunc Callback function to be called with CPLHTTPFetchEx() is called
959
 *              (or NULL to restore default handler)
960
 * @param pUserData Last argument to provide to the pFunc callback.
961
 *
962
 * @since GDAL 3.2
963
 */
964
void CPLHTTPSetFetchCallback(CPLHTTPFetchCallbackFunc pFunc, void *pUserData)
965
0
{
966
0
    gpsHTTPFetchCallbackFunc = pFunc;
967
0
    gpHTTPFetchCallbackUserData = pUserData;
968
0
}
969
970
/************************************************************************/
971
/*                      CPLHTTPPushFetchCallback()                      */
972
/************************************************************************/
973
974
/** Installs an alternate callback to the default implementation of
975
 * CPLHTTPFetchEx().
976
 *
977
 * This callback will only be used in the thread where this function has been
978
 * called. It must be un-installed by CPLHTTPPopFetchCallback(), which must also
979
 * be called from the same thread.
980
 *
981
 * @param pFunc Callback function to be called with CPLHTTPFetchEx() is called.
982
 * @param pUserData Last argument to provide to the pFunc callback.
983
 * @return TRUE in case of success.
984
 *
985
 * @since GDAL 3.2
986
 */
987
int CPLHTTPPushFetchCallback(CPLHTTPFetchCallbackFunc pFunc, void *pUserData)
988
0
{
989
0
    auto psCtx = GetHTTPFetchContext(true);
990
0
    if (psCtx == nullptr)
991
0
        return false;
992
0
    psCtx->stack.emplace_back(
993
0
        std::pair<CPLHTTPFetchCallbackFunc, void *>(pFunc, pUserData));
994
0
    return true;
995
0
}
996
997
/************************************************************************/
998
/*                       CPLHTTPPopFetchCallback()                      */
999
/************************************************************************/
1000
1001
/** Uninstalls a callback set by CPLHTTPPushFetchCallback().
1002
 *
1003
 * @see CPLHTTPPushFetchCallback()
1004
 * @return TRUE in case of success.
1005
 * @since GDAL 3.2
1006
 */
1007
int CPLHTTPPopFetchCallback(void)
1008
0
{
1009
0
    auto psCtx = GetHTTPFetchContext(false);
1010
0
    if (psCtx == nullptr || psCtx->stack.empty())
1011
0
    {
1012
0
        CPLError(
1013
0
            CE_Failure, CPLE_AppDefined,
1014
0
            "CPLHTTPPushFetchCallback / CPLHTTPPopFetchCallback not balanced");
1015
0
        return false;
1016
0
    }
1017
0
    else
1018
0
    {
1019
0
        psCtx->stack.pop_back();
1020
0
        return true;
1021
0
    }
1022
0
}
1023
1024
/************************************************************************/
1025
/*                           CPLHTTPFetch()                             */
1026
/************************************************************************/
1027
1028
// NOTE: when adding an option below, add it in asAssocEnvVarOptionName[]
1029
1030
// clang-format off
1031
/**
1032
 * \brief Fetch a document from an url and return in a string.
1033
 *
1034
 * Different options may be passed through the papszOptions function parameter,
1035
 * or for most of them through a configuration option:
1036
 * <ul>
1037
 * <li>CONNECTTIMEOUT=val, where
1038
 * val is in seconds (possibly with decimals). This is the maximum delay for the
1039
 * connection to be established before being aborted (GDAL >= 2.2).
1040
 * Corresponding configuration option: GDAL_HTTP_CONNECTTIMEOUT.
1041
 * </li>
1042
 * <li>TIMEOUT=val, where val is in seconds. This is the maximum delay for the
1043
 * whole request to complete before being aborted.
1044
 * Corresponding configuration option: GDAL_HTTP_TIMEOUT.
1045
 * </li>
1046
 * <li>LOW_SPEED_TIME=val,
1047
 * where val is in seconds. This is the maximum time where the transfer speed
1048
 * should be below the LOW_SPEED_LIMIT (if not specified 1b/s), before the
1049
 * transfer to be considered too slow and aborted. (GDAL >= 2.1).
1050
 * Corresponding configuration option: GDAL_HTTP_LOW_SPEED_TIME.
1051
 * </li>
1052
 * <li>LOW_SPEED_LIMIT=val, where val is in bytes/second. See LOW_SPEED_TIME.
1053
 * Has only effect if LOW_SPEED_TIME is specified too. (GDAL >= 2.1).
1054
 * Corresponding configuration option: GDAL_HTTP_LOW_SPEED_LIMIT.
1055
 * </li>
1056
 * <li>HEADERS=val, where val is an extra header to use when getting a web page.
1057
 *                  For example "Accept: application/x-ogcwkt"
1058
 * Corresponding configuration option: GDAL_HTTP_HEADERS.
1059
 * Starting with GDAL 3.6, the GDAL_HTTP_HEADERS configuration option can also
1060
 * be used to specify a comma separated list of key: value pairs. This is an
1061
 * alternative to the GDAL_HTTP_HEADER_FILE mechanism. If a comma or a
1062
 * double-quote character is needed in the value, then the key: value pair must
1063
 * be enclosed in double-quote characters. In that situation, backslash and
1064
 * double quote character must be backslash-escaped. e.g GDAL_HTTP_HEADERS=Foo:
1065
 * Bar,"Baz: escaped backslash \\, escaped double-quote \", end of
1066
 * value",Another: Header
1067
 * </li>
1068
 * <li>HEADER_FILE=filename: filename of a text file with "key: value" headers.
1069
 *     The content of the file is not cached, and thus it is read again before
1070
 *     issuing each HTTP request. (GDAL >= 2.2)
1071
 * Corresponding configuration option: GDAL_HTTP_HEADER_FILE.
1072
 * </li>
1073
 * <li>HTTPAUTH=[BASIC/NTLM/NEGOTIATE/ANY/ANYSAFE/BEARER] to specify an
1074
 * authentication scheme to use.
1075
 * Corresponding configuration option: GDAL_HTTP_AUTH.
1076
 * </li>
1077
 * <li>USERPWD=userid:password to specify a user and password for
1078
 * authentication.
1079
 * Corresponding configuration option: GDAL_HTTP_USERPWD.
1080
 * </li>
1081
 * <li>GSSAPI_DELEGATION=[NONE/POLICY/ALWAYS] set allowed
1082
 * GSS-API delegation. Relevant only with HTTPAUTH=NEGOTIATE (GDAL >= 3.3).
1083
 * Corresponding configuration option: GDAL_GSSAPI_DELEGATION (note: no "HTTP_" in the name)
1084
 * </li>
1085
 * <li>HTTP_BEARER=val set OAuth 2.0 Bearer Access Token.
1086
 * Relevant only with HTTPAUTH=BEARER (GDAL >= 3.9).
1087
 * Corresponding configuration option: GDAL_HTTP_BEARER
1088
 * </li>
1089
 * <li>POSTFIELDS=val, where val is a nul-terminated string to be passed to the
1090
 * server with a POST request.
1091
 * No Corresponding configuration option.
1092
 * </li>
1093
 * <li>PROXY=val, to make requests go through a
1094
 * proxy server, where val is of the form proxy.server.com:port_number. This
1095
 * option affects both HTTP and HTTPS URLs.
1096
 * Corresponding configuration option: GDAL_HTTP_PROXY.
1097
 * </li>
1098
 * <li>HTTPS_PROXY=val (GDAL
1099
 * >= 2.4), the same meaning as PROXY, but this option is taken into account
1100
 * only for HTTPS URLs.
1101
 * Corresponding configuration option: GDAL_HTTPS_PROXY.
1102
 * </li>
1103
 * <li>PROXYUSERPWD=val, where val is of the form username:password.
1104
 * Corresponding configuration option: GDAL_HTTP_PROXYUSERPWD
1105
 * </li>
1106
 * <li>PROXYAUTH=[BASIC/NTLM/DIGEST/NEGOTIATE/ANY/ANYSAFE] to
1107
 * specify an proxy authentication scheme to use..
1108
 * Corresponding configuration option: GDAL_PROXYAUTH (note: no "HTTP_" in the name)
1109
 * </li>
1110
 * <li>NETRC=[YES/NO] to
1111
 * enable or disable use of $HOME/.netrc (or NETRC_FILE), default YES.
1112
 * Corresponding configuration option: GDAL_HTTP_NETRC.
1113
 * </li>
1114
 * <li>NETRC_FILE=file name to read .netrc info from  (GDAL >= 3.7).
1115
 * Corresponding configuration option: GDAL_HTTP_NETRC_FILE.
1116
 * </li>
1117
 * <li>CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc...
1118
 * No corresponding configuration option.
1119
 * </li>
1120
 * <li>FORM_FILE_NAME=val, where val is upload file name. If this
1121
 * option and FORM_FILE_PATH present, request type will set to POST.
1122
 * No corresponding configuration option.
1123
 * </li>
1124
 * <li>FORM_FILE_PATH=val, where val is upload file path.
1125
 * No corresponding configuration option.
1126
 * </li>
1127
 * <li>FORM_KEY_0=val...FORM_KEY_N, where val is name of form item.
1128
 * No corresponding configuration option.
1129
 * </li>
1130
 * <li>FORM_VALUE_0=val...FORM_VALUE_N, where val is value of the form
1131
 * item.
1132
 * No corresponding configuration option.
1133
 * </li>
1134
 * <li>FORM_ITEM_COUNT=val, where val is count of form items.
1135
 * No corresponding configuration option.
1136
 * </li>
1137
 * <li>COOKIE=val, where val is formatted as COOKIE1=VALUE1; COOKIE2=VALUE2;...
1138
 * Corresponding configuration option: GDAL_HTTP_COOKIE.</li>
1139
 * <li>COOKIEFILE=val, where val is file name to read cookies from
1140
 * (GDAL >= 2.4).
1141
 * Corresponding configuration option: GDAL_HTTP_COOKIEFILE.</li>
1142
 * <li>COOKIEJAR=val, where val is file name to store cookies to (GDAL >= 2.4).
1143
 * Corresponding configuration option: GDAL_HTTP_COOKIEJAR.</li>
1144
 * <li>MAX_RETRY=val, where val is the maximum number of
1145
 * retry attempts, when a retry is allowed (cf RETRY_CODES option).
1146
 * Default is 0, meaning no retry.
1147
 * Corresponding configuration option: GDAL_HTTP_MAX_RETRY.
1148
 * </li>
1149
 * <li>RETRY_DELAY=val, where val is the number of seconds
1150
 * between retry attempts. Default is 30.
1151
 * Corresponding configuration option: GDAL_HTTP_RETRY_DELAY.
1152
 * <li>RETRY_CODES=val, where val is "ALL" or a comma-separated list of HTTP
1153
 * codes that are considered for retry. By default, 429, 500, 502, 503 or 504
1154
 * HTTP errors are considered, as well as other situations with a particular
1155
 * HTTP or Curl error message. (GDAL >= 3.10).
1156
 * Corresponding configuration option: GDAL_HTTP_RETRY_CODES.
1157
 * </li>
1158
 * <li>MAX_FILE_SIZE=val, where val is a number of bytes (GDAL >= 2.2)
1159
 * No corresponding configuration option.
1160
 * </li>
1161
 * <li>CAINFO=/path/to/bundle.crt. This is path to Certificate Authority (CA)
1162
 *     bundle file. By default, it will be looked for in a system location. If
1163
 *     the CAINFO option is not defined, GDAL will also look in the
1164
 *     CURL_CA_BUNDLE and SSL_CERT_FILE environment variables respectively
1165
 *     and use the first one found as the CAINFO value (GDAL >= 2.1.3). The
1166
 *     GDAL_CURL_CA_BUNDLE environment variable may also be used to set the
1167
 *     CAINFO value in GDAL >= 3.2.</li>
1168
 * <li>HTTP_VERSION=1.0/1.1/2/2TLS (GDAL >= 2.3)/2PRIOR_KNOWLEDGE (GDAL >= 3.10).
1169
 *     Specify HTTP version to use.
1170
 *     Will default to 1.1 generally (except on some controlled environments,
1171
 *     like Google Compute Engine VMs, where 2TLS will be the default).
1172
 *     Support for HTTP/2 requires curl 7.33 or later, built against nghttp2.
1173
 *     "2TLS" means that HTTP/2 will be attempted for HTTPS connections only.
1174
 *     Whereas "2" means that HTTP/2 will be attempted for HTTP or HTTPS.
1175
 *     "2PRIOR_KNOWLEDGE" means that the server will be assumed to support
1176
 *     HTTP/2.
1177
 *     Corresponding configuration option: GDAL_HTTP_VERSION.
1178
 * </li>
1179
 * <li>SSL_VERIFYSTATUS=YES/NO (GDAL >= 2.3, and curl >= 7.41): determines
1180
 * whether the status of the server cert using the "Certificate Status Request"
1181
 * TLS extension (aka. OCSP stapling) should be checked. If this option is
1182
 * enabled but the server does not support the TLS extension, the verification
1183
 * will fail. Default to NO.
1184
 * Corresponding configuration option: GDAL_HTTP_SSL_VERIFYSTATUS.
1185
 * </li>
1186
 * <li>USE_CAPI_STORE=YES/NO (GDAL >= 2.3,
1187
 * Windows only): whether CA certificates from the Windows certificate store.
1188
 * Defaults to NO.
1189
 * Corresponding configuration option: GDAL_HTTP_USE_CAPI_STORE.
1190
 * </li>
1191
 * <li>TCP_KEEPALIVE=YES/NO (GDAL >= 3.6): whether to
1192
 * enable TCP keep-alive. Defaults to NO.
1193
 * Corresponding configuration option: GDAL_HTTP_TCP_KEEPALIVE.
1194
 * </li>
1195
 * <li>TCP_KEEPIDLE=integer, in
1196
 * seconds (GDAL >= 3.6): keep-alive idle time. Defaults to 60. Only taken into
1197
 * account if TCP_KEEPALIVE=YES.
1198
 * Corresponding configuration option: GDAL_HTTP_TCP_KEEPIDLE.
1199
 * </li>
1200
 * <li>TCP_KEEPINTVL=integer, in seconds
1201
 * (GDAL >= 3.6): interval time between keep-alive probes. Defaults to 60. Only
1202
 * taken into account if TCP_KEEPALIVE=YES.
1203
 * Corresponding configuration option: GDAL_HTTP_TCP_KEEPINTVL.
1204
 * </li>
1205
 * <li>USERAGENT=string: value of User-Agent header. Starting with GDAL 3.7,
1206
 * GDAL core sets it by default (during driver initialization) to GDAL/x.y.z
1207
 * where x.y.z is the GDAL version number. Applications may override it with the
1208
 * CPLHTTPSetDefaultUserAgent() function.
1209
 * Corresponding configuration option: GDAL_HTTP_USERAGENT.
1210
 * </li>
1211
 * <li>SSLCERT=filename (GDAL >= 3.7): Filename of the the SSL client certificate.
1212
 * Cf https://curl.se/libcurl/c/CURLOPT_SSLCERT.html.
1213
 * Corresponding configuration option: GDAL_HTTP_SSLCERT.
1214
 * </li>
1215
 * <li>SSLCERTTYPE=string (GDAL >= 3.7): Format of the SSL certificate: "PEM"
1216
 * or "DER". Cf https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html.
1217
 * Corresponding configuration option: GDAL_HTTP_SSLCERTTYPE.
1218
 * </li>
1219
 * <li>SSLKEY=filename (GDAL >= 3.7): Private key file for TLS and SSL client
1220
 * certificate. Cf https://curl.se/libcurl/c/CURLOPT_SSLKEY.html.
1221
 * Corresponding configuration option: GDAL_HTTP_SSLKEY.
1222
 * </li>
1223
 * <li>KEYPASSWD=string (GDAL >= 3.7): Passphrase to private key.
1224
 * Cf https://curl.se/libcurl/c/CURLOPT_KEYPASSWD.html.
1225
 * Corresponding configuration option: GDAL_HTTP_KEYPASSWD.
1226
 * </ul>
1227
 *
1228
 * If an option is specified through papszOptions and as a configuration option,
1229
 * the former takes precedence over the later.
1230
 *
1231
 * Starting with GDAL 3.7, the above configuration options can also be specified
1232
 * as path-specific options with VSISetPathSpecificOption().
1233
 *
1234
 * @param pszURL valid URL recognized by underlying download library (libcurl)
1235
 * @param papszOptions option list as a NULL-terminated array of strings. May be
1236
 * NULL.
1237
 *
1238
 * @return a CPLHTTPResult* structure that must be freed by
1239
 * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled
1240
 */
1241
// clang-format on
1242
CPLHTTPResult *CPLHTTPFetch(const char *pszURL, CSLConstList papszOptions)
1243
0
{
1244
0
    return CPLHTTPFetchEx(pszURL, papszOptions, nullptr, nullptr, nullptr,
1245
0
                          nullptr);
1246
0
}
1247
1248
/**
1249
 * Fetch a document from an url and return in a string.
1250
 * @param  pszURL       Url to fetch document from web.
1251
 * @param  papszOptions Option list as a NULL-terminated array of strings.
1252
 * Available keys see in CPLHTTPFetch.
1253
 * @param  pfnProgress  Callback for reporting algorithm progress matching the
1254
 * GDALProgressFunc() semantics. May be NULL.
1255
 * @param  pProgressArg Callback argument passed to pfnProgress.
1256
 * @param  pfnWrite     Write function pointer matching the CPLHTTPWriteFunc()
1257
 * semantics. May be NULL.
1258
 * @param  pWriteArg    Argument which will pass to a write function.
1259
 * @return              A CPLHTTPResult* structure that must be freed by
1260
 * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled.
1261
 */
1262
CPLHTTPResult *CPLHTTPFetchEx(const char *pszURL, CSLConstList papszOptions,
1263
                              GDALProgressFunc pfnProgress, void *pProgressArg,
1264
                              CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg)
1265
1266
0
{
1267
0
    if (STARTS_WITH(pszURL, "/vsimem/") &&
1268
        // Disabled by default for potential security issues.
1269
0
        CPLTestBool(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")))
1270
0
    {
1271
0
        CPLString osURL(pszURL);
1272
0
        const char *pszCustomRequest =
1273
0
            CSLFetchNameValue(papszOptions, "CUSTOMREQUEST");
1274
0
        if (pszCustomRequest != nullptr)
1275
0
        {
1276
0
            osURL += "&CUSTOMREQUEST=";
1277
0
            osURL += pszCustomRequest;
1278
0
        }
1279
0
        const char *pszUserPwd = CSLFetchNameValue(papszOptions, "USERPWD");
1280
0
        if (pszUserPwd != nullptr)
1281
0
        {
1282
0
            osURL += "&USERPWD=";
1283
0
            osURL += pszUserPwd;
1284
0
        }
1285
0
        const char *pszPost = CSLFetchNameValue(papszOptions, "POSTFIELDS");
1286
0
        if (pszPost != nullptr)  // Hack: We append post content to filename.
1287
0
        {
1288
0
            osURL += "&POSTFIELDS=";
1289
0
            osURL += pszPost;
1290
0
        }
1291
0
        const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
1292
0
        if (pszHeaders != nullptr &&
1293
0
            CPLTestBool(
1294
0
                CPLGetConfigOption("CPL_CURL_VSIMEM_PRINT_HEADERS", "FALSE")))
1295
0
        {
1296
0
            osURL += "&HEADERS=";
1297
0
            osURL += pszHeaders;
1298
0
        }
1299
0
        vsi_l_offset nLength = 0;
1300
0
        CPLHTTPResult *psResult =
1301
0
            static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1302
0
        GByte *pabyData = VSIGetMemFileBuffer(osURL, &nLength, FALSE);
1303
0
        if (pabyData == nullptr)
1304
0
        {
1305
0
            CPLDebug("HTTP", "Cannot find %s", osURL.c_str());
1306
0
            psResult->nStatus = 1;
1307
0
            psResult->pszErrBuf =
1308
0
                CPLStrdup(CPLSPrintf("HTTP error code : %d", 404));
1309
0
            CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
1310
0
        }
1311
0
        else if (nLength != 0)
1312
0
        {
1313
0
            psResult->nDataLen = static_cast<int>(nLength);
1314
0
            psResult->pabyData = static_cast<GByte *>(
1315
0
                CPLMalloc(static_cast<size_t>(nLength) + 1));
1316
0
            memcpy(psResult->pabyData, pabyData, static_cast<size_t>(nLength));
1317
0
            psResult->pabyData[static_cast<size_t>(nLength)] = 0;
1318
0
        }
1319
1320
0
        if (psResult->pabyData != nullptr &&
1321
0
            STARTS_WITH(reinterpret_cast<char *>(psResult->pabyData),
1322
0
                        "Content-Type: "))
1323
0
        {
1324
0
            const char *pszContentType =
1325
0
                reinterpret_cast<char *>(psResult->pabyData) +
1326
0
                strlen("Content-type: ");
1327
0
            const char *pszEOL = strchr(pszContentType, '\r');
1328
0
            if (pszEOL)
1329
0
                pszEOL = strchr(pszContentType, '\n');
1330
0
            if (pszEOL)
1331
0
            {
1332
0
                size_t nContentLength = pszEOL - pszContentType;
1333
0
                psResult->pszContentType =
1334
0
                    static_cast<char *>(CPLMalloc(nContentLength + 1));
1335
0
                memcpy(psResult->pszContentType, pszContentType,
1336
0
                       nContentLength);
1337
0
                psResult->pszContentType[nContentLength] = 0;
1338
0
            }
1339
0
        }
1340
1341
0
        return psResult;
1342
0
    }
1343
1344
    // Try to user alternate network layer if set.
1345
0
    auto pCtx = GetHTTPFetchContext(false);
1346
0
    if (pCtx)
1347
0
    {
1348
0
        for (size_t i = pCtx->stack.size(); i > 0;)
1349
0
        {
1350
0
            --i;
1351
0
            const auto &cbk = pCtx->stack[i];
1352
0
            auto cbkFunc = cbk.first;
1353
0
            auto pUserData = cbk.second;
1354
0
            auto res = cbkFunc(pszURL, papszOptions, pfnProgress, pProgressArg,
1355
0
                               pfnWrite, pWriteArg, pUserData);
1356
0
            if (res)
1357
0
            {
1358
0
                if (CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT"))
1359
0
                {
1360
0
                    CPLHTTPDestroyResult(res);
1361
0
                    res = nullptr;
1362
0
                }
1363
0
                return res;
1364
0
            }
1365
0
        }
1366
0
    }
1367
1368
0
    if (gpsHTTPFetchCallbackFunc)
1369
0
    {
1370
0
        auto res = gpsHTTPFetchCallbackFunc(pszURL, papszOptions, pfnProgress,
1371
0
                                            pProgressArg, pfnWrite, pWriteArg,
1372
0
                                            gpHTTPFetchCallbackUserData);
1373
0
        if (res)
1374
0
        {
1375
0
            if (CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT"))
1376
0
            {
1377
0
                CPLHTTPDestroyResult(res);
1378
0
                res = nullptr;
1379
0
            }
1380
0
            return res;
1381
0
        }
1382
0
    }
1383
1384
0
#ifndef HAVE_CURL
1385
0
    CPLError(CE_Failure, CPLE_NotSupported,
1386
0
             "GDAL/OGR not compiled with libcurl support, "
1387
0
             "remote requests not supported.");
1388
0
    return nullptr;
1389
#else
1390
1391
    /* -------------------------------------------------------------------- */
1392
    /*      Are we using a persistent named session?  If so, search for     */
1393
    /*      or create it.                                                   */
1394
    /*                                                                      */
1395
    /*      Currently this code does not attempt to protect against         */
1396
    /*      multiple threads asking for the same named session.  If that    */
1397
    /*      occurs it will be in use in multiple threads at once, which     */
1398
    /*      will lead to potential crashes in libcurl.                      */
1399
    /* -------------------------------------------------------------------- */
1400
    CURL *http_handle = nullptr;
1401
1402
    const char *pszPersistent = CSLFetchNameValue(papszOptions, "PERSISTENT");
1403
    const char *pszClosePersistent =
1404
        CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT");
1405
    if (pszPersistent)
1406
    {
1407
        CPLString osSessionName = pszPersistent;
1408
        CPLMutexHolder oHolder(&hSessionMapMutex);
1409
1410
        if (poSessionMap == nullptr)
1411
            poSessionMap = new std::map<CPLString, CURL *>;
1412
        if (poSessionMap->count(osSessionName) == 0)
1413
        {
1414
            (*poSessionMap)[osSessionName] = curl_easy_init();
1415
            CPLDebug("HTTP", "Establish persistent session named '%s'.",
1416
                     osSessionName.c_str());
1417
        }
1418
1419
        http_handle = (*poSessionMap)[osSessionName];
1420
    }
1421
    /* -------------------------------------------------------------------- */
1422
    /*      Are we requested to close a persistent named session?           */
1423
    /* -------------------------------------------------------------------- */
1424
    else if (pszClosePersistent)
1425
    {
1426
        CPLString osSessionName = pszClosePersistent;
1427
        CPLMutexHolder oHolder(&hSessionMapMutex);
1428
1429
        if (poSessionMap)
1430
        {
1431
            std::map<CPLString, CURL *>::iterator oIter =
1432
                poSessionMap->find(osSessionName);
1433
            if (oIter != poSessionMap->end())
1434
            {
1435
                curl_easy_cleanup(oIter->second);
1436
                poSessionMap->erase(oIter);
1437
                if (poSessionMap->empty())
1438
                {
1439
                    delete poSessionMap;
1440
                    poSessionMap = nullptr;
1441
                }
1442
                CPLDebug("HTTP", "Ended persistent session named '%s'.",
1443
                         osSessionName.c_str());
1444
            }
1445
            else
1446
            {
1447
                CPLDebug("HTTP",
1448
                         "Could not find persistent session named '%s'.",
1449
                         osSessionName.c_str());
1450
            }
1451
        }
1452
1453
        return nullptr;
1454
    }
1455
    else
1456
        http_handle = curl_easy_init();
1457
1458
    /* -------------------------------------------------------------------- */
1459
    /*      Setup the request.                                              */
1460
    /* -------------------------------------------------------------------- */
1461
    char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1462
1463
    CPLHTTPEmitFetchDebug(pszURL);
1464
1465
    CPLHTTPResult *psResult =
1466
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1467
1468
    struct curl_slist *headers = reinterpret_cast<struct curl_slist *>(
1469
        CPLHTTPSetOptions(http_handle, pszURL, papszOptions));
1470
    if (headers != nullptr)
1471
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
1472
1473
    // Are we making a head request.
1474
    const char *pszNoBody = nullptr;
1475
    if ((pszNoBody = CSLFetchNameValue(papszOptions, "NO_BODY")) != nullptr)
1476
    {
1477
        if (CPLTestBool(pszNoBody))
1478
        {
1479
            CPLDebug("HTTP", "HEAD Request: %s", pszURL);
1480
            unchecked_curl_easy_setopt(http_handle, CURLOPT_NOBODY, 1L);
1481
        }
1482
    }
1483
1484
    // Capture response headers.
1485
    unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
1486
    unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION,
1487
                               CPLHdrWriteFct);
1488
1489
    CPLHTTPResultWithLimit sResultWithLimit;
1490
    if (nullptr == pfnWrite)
1491
    {
1492
        pfnWrite = CPLWriteFct;
1493
1494
        sResultWithLimit.psResult = psResult;
1495
        sResultWithLimit.nMaxFileSize = 0;
1496
        const char *pszMaxFileSize =
1497
            CSLFetchNameValue(papszOptions, "MAX_FILE_SIZE");
1498
        if (pszMaxFileSize != nullptr)
1499
        {
1500
            sResultWithLimit.nMaxFileSize = atoi(pszMaxFileSize);
1501
            // Only useful if size is returned by server before actual download.
1502
            unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXFILESIZE,
1503
                                       sResultWithLimit.nMaxFileSize);
1504
        }
1505
        pWriteArg = &sResultWithLimit;
1506
    }
1507
1508
    unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, pWriteArg);
1509
    unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, pfnWrite);
1510
1511
    CurlProcessData stProcessData = {pfnProgress, pProgressArg};
1512
    if (nullptr != pfnProgress)
1513
    {
1514
        unchecked_curl_easy_setopt(http_handle, CURLOPT_XFERINFOFUNCTION,
1515
                                   NewProcessFunction);
1516
        unchecked_curl_easy_setopt(http_handle, CURLOPT_XFERINFODATA,
1517
                                   &stProcessData);
1518
        unchecked_curl_easy_setopt(http_handle, CURLOPT_NOPROGRESS, 0L);
1519
    }
1520
1521
    szCurlErrBuf[0] = '\0';
1522
1523
    unchecked_curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
1524
1525
    bool bGZipRequested = false;
1526
    if (bSupportGZip && CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
1527
    {
1528
        bGZipRequested = true;
1529
        unchecked_curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
1530
    }
1531
1532
    CPLHTTPPostFields oPostFields;
1533
    if (oPostFields.Fill(http_handle, papszOptions) != CE_None)
1534
    {
1535
        psResult->nStatus = 34;  // CURLE_HTTP_POST_ERROR
1536
        psResult->pszErrBuf = CPLStrdup(oPostFields.GetErrorMessage().c_str());
1537
        CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
1538
        CPLHTTPFetchCleanup(http_handle, headers, pszPersistent, papszOptions);
1539
        return psResult;
1540
    }
1541
1542
    /* -------------------------------------------------------------------- */
1543
    /*      Depending on status code, retry this HTTP call until max        */
1544
    /*      retry has been reached                                          */
1545
    /* -------------------------------------------------------------------- */
1546
    const char *pszRetryDelay = CSLFetchNameValue(papszOptions, "RETRY_DELAY");
1547
    if (pszRetryDelay == nullptr)
1548
        pszRetryDelay = CPLGetConfigOption(
1549
            "GDAL_HTTP_RETRY_DELAY", CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY));
1550
    const char *pszMaxRetries = CSLFetchNameValue(papszOptions, "MAX_RETRY");
1551
    if (pszMaxRetries == nullptr)
1552
        pszMaxRetries = CPLGetConfigOption(
1553
            "GDAL_HTTP_MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY));
1554
    // coverity[tainted_data]
1555
    double dfRetryDelaySecs = CPLAtof(pszRetryDelay);
1556
    int nMaxRetries = atoi(pszMaxRetries);
1557
    const char *pszRetryCodes = CSLFetchNameValue(papszOptions, "RETRY_CODES");
1558
    if (!pszRetryCodes)
1559
        pszRetryCodes = CPLGetConfigOption("GDAL_HTTP_RETRY_CODES", nullptr);
1560
    int nRetryCount = 0;
1561
1562
    while (true)
1563
    {
1564
        /* --------------------------------------------------------------------
1565
         */
1566
        /*      Execute the request, waiting for results. */
1567
        /* --------------------------------------------------------------------
1568
         */
1569
        void *old_handler = CPLHTTPIgnoreSigPipe();
1570
        psResult->nStatus = static_cast<int>(curl_easy_perform(http_handle));
1571
        CPLHTTPRestoreSigPipeHandler(old_handler);
1572
1573
        /* --------------------------------------------------------------------
1574
         */
1575
        /*      Fetch content-type if possible. */
1576
        /* --------------------------------------------------------------------
1577
         */
1578
        psResult->pszContentType = nullptr;
1579
        curl_easy_getinfo(http_handle, CURLINFO_CONTENT_TYPE,
1580
                          &(psResult->pszContentType));
1581
        if (psResult->pszContentType != nullptr)
1582
            psResult->pszContentType = CPLStrdup(psResult->pszContentType);
1583
1584
        long response_code = 0;
1585
        curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code);
1586
        if (response_code != 200)
1587
        {
1588
            const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
1589
                static_cast<int>(response_code), dfRetryDelaySecs,
1590
                reinterpret_cast<const char *>(psResult->pabyData),
1591
                szCurlErrBuf, pszRetryCodes);
1592
            if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetries)
1593
            {
1594
                CPLError(CE_Warning, CPLE_AppDefined,
1595
                         "HTTP error code: %d - %s. "
1596
                         "Retrying again in %.1f secs",
1597
                         static_cast<int>(response_code), pszURL,
1598
                         dfRetryDelaySecs);
1599
                CPLSleep(dfRetryDelaySecs);
1600
                dfRetryDelaySecs = dfNewRetryDelay;
1601
                nRetryCount++;
1602
1603
                CPLFree(psResult->pszContentType);
1604
                psResult->pszContentType = nullptr;
1605
                CSLDestroy(psResult->papszHeaders);
1606
                psResult->papszHeaders = nullptr;
1607
                CPLFree(psResult->pabyData);
1608
                psResult->pabyData = nullptr;
1609
                psResult->nDataLen = 0;
1610
                psResult->nDataAlloc = 0;
1611
1612
                continue;
1613
            }
1614
        }
1615
1616
        /* --------------------------------------------------------------------
1617
         */
1618
        /*      Have we encountered some sort of error? */
1619
        /* --------------------------------------------------------------------
1620
         */
1621
        if (strlen(szCurlErrBuf) > 0)
1622
        {
1623
            bool bSkipError = false;
1624
            const char *pszContentLength =
1625
                CSLFetchNameValue(psResult->papszHeaders, "Content-Length");
1626
            // Some servers such as
1627
            // http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities
1628
            // invalidly return Content-Length as the uncompressed size, with
1629
            // makes curl to wait for more data and time-out finally. If we got
1630
            // the expected data size, then we don't emit an error but turn off
1631
            // GZip requests.
1632
            if (bGZipRequested &&
1633
                strstr(szCurlErrBuf, "transfer closed with") &&
1634
                strstr(szCurlErrBuf, "bytes remaining to read"))
1635
            {
1636
                if (pszContentLength && psResult->nDataLen != 0 &&
1637
                    atoi(pszContentLength) == psResult->nDataLen)
1638
                {
1639
                    const char *pszCurlGZIPOption =
1640
                        CPLGetConfigOption("CPL_CURL_GZIP", nullptr);
1641
                    if (pszCurlGZIPOption == nullptr)
1642
                    {
1643
                        CPLSetConfigOption("CPL_CURL_GZIP", "NO");
1644
                        CPLDebug("HTTP",
1645
                                 "Disabling CPL_CURL_GZIP, "
1646
                                 "because %s doesn't support it properly",
1647
                                 pszURL);
1648
                    }
1649
                    psResult->nStatus = 0;
1650
                    bSkipError = true;
1651
                }
1652
            }
1653
1654
            // Ignore SSL errors about non-properly terminated connection,
1655
            // often due to HTTP proxies
1656
            else if (pszContentLength == nullptr &&
1657
                     // Cf https://github.com/curl/curl/pull/3148
1658
                     (strstr(szCurlErrBuf,
1659
                             "GnuTLS recv error (-110): The TLS connection was "
1660
                             "non-properly terminated") != nullptr ||
1661
                      // Cf https://github.com/curl/curl/issues/9024
1662
                      strstr(szCurlErrBuf,
1663
                             "SSL_read: error:0A000126:SSL "
1664
                             "routines::unexpected eof while reading") !=
1665
                          nullptr))
1666
            {
1667
                psResult->nStatus = 0;
1668
                bSkipError = true;
1669
            }
1670
            else if (CPLTestBool(
1671
                         CPLGetConfigOption("CPL_CURL_IGNORE_ERROR", "NO")))
1672
            {
1673
                psResult->nStatus = 0;
1674
                bSkipError = true;
1675
            }
1676
1677
            if (!bSkipError)
1678
            {
1679
                psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
1680
                if (psResult->nDataLen > 0)
1681
                {
1682
                    CPLError(CE_Failure, CPLE_AppDefined,
1683
                             "%s. You may set the CPL_CURL_IGNORE_ERROR "
1684
                             "configuration option to YES to try to ignore it.",
1685
                             szCurlErrBuf);
1686
                }
1687
                else
1688
                {
1689
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", szCurlErrBuf);
1690
                }
1691
            }
1692
        }
1693
        else
1694
        {
1695
            if (response_code >= 400 && response_code < 600)
1696
            {
1697
                psResult->pszErrBuf = CPLStrdup(CPLSPrintf(
1698
                    "HTTP error code : %d", static_cast<int>(response_code)));
1699
                CPLError(CE_Failure, CPLE_AppDefined, "%s",
1700
                         psResult->pszErrBuf);
1701
            }
1702
        }
1703
        break;
1704
    }
1705
1706
    CPLHTTPFetchCleanup(http_handle, headers, pszPersistent, papszOptions);
1707
1708
    return psResult;
1709
#endif /* def HAVE_CURL */
1710
0
}
1711
1712
#ifdef HAVE_CURL
1713
/************************************************************************/
1714
/*                       CPLMultiPerformWait()                          */
1715
/************************************************************************/
1716
1717
bool CPLMultiPerformWait(void *hCurlMultiHandleIn, int & /*repeats*/)
1718
{
1719
    CURLM *hCurlMultiHandle = static_cast<CURLM *>(hCurlMultiHandleIn);
1720
1721
    // Wait for events on the sockets
1722
1723
    // Using curl_multi_poll() is preferred to avoid hitting the 1024 file
1724
    // descriptor limit
1725
1726
    int numfds = 0;
1727
    if (curl_multi_poll(hCurlMultiHandle, nullptr, 0, 1000, &numfds) !=
1728
        CURLM_OK)
1729
    {
1730
        CPLError(CE_Failure, CPLE_AppDefined, "curl_multi_poll() failed");
1731
        return false;
1732
    }
1733
    return true;
1734
}
1735
1736
class CPLHTTPErrorBuffer
1737
{
1738
  public:
1739
    char szBuffer[CURL_ERROR_SIZE + 1];
1740
1741
    CPLHTTPErrorBuffer()
1742
    {
1743
        szBuffer[0] = '\0';
1744
    }
1745
};
1746
1747
#endif  // HAVE_CURL
1748
1749
/************************************************************************/
1750
/*                           CPLHTTPMultiFetch()                        */
1751
/************************************************************************/
1752
1753
/**
1754
 * \brief Fetch several documents at once
1755
 *
1756
 * @param papszURL array of valid URLs recognized by underlying download library
1757
 * (libcurl)
1758
 * @param nURLCount number of URLs of papszURL
1759
 * @param nMaxSimultaneous maximum number of downloads to issue simultaneously.
1760
 *                         Any negative or zer value means unlimited.
1761
 * @param papszOptions option list as a NULL-terminated array of strings. May be
1762
 * NULL. Refer to CPLHTTPFetch() for valid options.
1763
 * @return an array of CPLHTTPResult* structures that must be freed by
1764
 * CPLHTTPDestroyMultiResult() or NULL if libcurl support is disabled
1765
 *
1766
 * @since GDAL 2.3
1767
 */
1768
CPLHTTPResult **CPLHTTPMultiFetch(const char *const *papszURL, int nURLCount,
1769
                                  int nMaxSimultaneous,
1770
                                  CSLConstList papszOptions)
1771
0
{
1772
0
#ifndef HAVE_CURL
1773
0
    (void)papszURL;
1774
0
    (void)nURLCount;
1775
0
    (void)nMaxSimultaneous;
1776
0
    (void)papszOptions;
1777
1778
0
    CPLError(CE_Failure, CPLE_NotSupported,
1779
0
             "GDAL/OGR not compiled with libcurl support, "
1780
0
             "remote requests not supported.");
1781
0
    return nullptr;
1782
#else  /* def HAVE_CURL */
1783
1784
    /* -------------------------------------------------------------------- */
1785
    /*      Are we using a persistent named session?  If so, search for     */
1786
    /*      or create it.                                                   */
1787
    /*                                                                      */
1788
    /*      Currently this code does not attempt to protect against         */
1789
    /*      multiple threads asking for the same named session.  If that    */
1790
    /*      occurs it will be in use in multiple threads at once, which     */
1791
    /*      will lead to potential crashes in libcurl.                      */
1792
    /* -------------------------------------------------------------------- */
1793
    CURLM *hCurlMultiHandle = nullptr;
1794
1795
    const char *pszPersistent = CSLFetchNameValue(papszOptions, "PERSISTENT");
1796
    const char *pszClosePersistent =
1797
        CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT");
1798
    if (pszPersistent)
1799
    {
1800
        CPLString osSessionName = pszPersistent;
1801
        CPLMutexHolder oHolder(&hSessionMapMutex);
1802
1803
        if (poSessionMultiMap == nullptr)
1804
            poSessionMultiMap = new std::map<CPLString, CURLM *>;
1805
        if (poSessionMultiMap->count(osSessionName) == 0)
1806
        {
1807
            (*poSessionMultiMap)[osSessionName] = curl_multi_init();
1808
            CPLDebug("HTTP", "Establish persistent session named '%s'.",
1809
                     osSessionName.c_str());
1810
        }
1811
1812
        hCurlMultiHandle = (*poSessionMultiMap)[osSessionName];
1813
    }
1814
    /* -------------------------------------------------------------------- */
1815
    /*      Are we requested to close a persistent named session?           */
1816
    /* -------------------------------------------------------------------- */
1817
    else if (pszClosePersistent)
1818
    {
1819
        CPLString osSessionName = pszClosePersistent;
1820
        CPLMutexHolder oHolder(&hSessionMapMutex);
1821
1822
        if (poSessionMultiMap)
1823
        {
1824
            auto oIter = poSessionMultiMap->find(osSessionName);
1825
            if (oIter != poSessionMultiMap->end())
1826
            {
1827
                VSICURLMultiCleanup(oIter->second);
1828
                poSessionMultiMap->erase(oIter);
1829
                if (poSessionMultiMap->empty())
1830
                {
1831
                    delete poSessionMultiMap;
1832
                    poSessionMultiMap = nullptr;
1833
                }
1834
                CPLDebug("HTTP", "Ended persistent session named '%s'.",
1835
                         osSessionName.c_str());
1836
            }
1837
            else
1838
            {
1839
                CPLDebug("HTTP",
1840
                         "Could not find persistent session named '%s'.",
1841
                         osSessionName.c_str());
1842
            }
1843
        }
1844
1845
        return nullptr;
1846
    }
1847
    else
1848
    {
1849
        hCurlMultiHandle = curl_multi_init();
1850
    }
1851
1852
    CPLHTTPResult **papsResults = static_cast<CPLHTTPResult **>(
1853
        CPLCalloc(nURLCount, sizeof(CPLHTTPResult *)));
1854
    std::vector<CURL *> asHandles;
1855
    std::vector<CPLHTTPResultWithLimit> asResults;
1856
    asResults.resize(nURLCount);
1857
    std::vector<struct curl_slist *> aHeaders;
1858
    aHeaders.resize(nURLCount);
1859
    std::vector<CPLHTTPErrorBuffer> asErrorBuffers;
1860
    asErrorBuffers.resize(nURLCount);
1861
1862
    for (int i = 0; i < nURLCount; i++)
1863
    {
1864
        papsResults[i] =
1865
            static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1866
1867
        const char *pszURL = papszURL[i];
1868
        CURL *http_handle = curl_easy_init();
1869
1870
        aHeaders[i] = reinterpret_cast<struct curl_slist *>(
1871
            CPLHTTPSetOptions(http_handle, pszURL, papszOptions));
1872
1873
        // Set Headers.
1874
        const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
1875
        if (pszHeaders != nullptr)
1876
        {
1877
            char **papszTokensHeaders =
1878
                CSLTokenizeString2(pszHeaders, "\r\n", 0);
1879
            for (int j = 0; papszTokensHeaders[j] != nullptr; ++j)
1880
                aHeaders[i] =
1881
                    curl_slist_append(aHeaders[i], papszTokensHeaders[j]);
1882
            CSLDestroy(papszTokensHeaders);
1883
        }
1884
1885
        if (aHeaders[i] != nullptr)
1886
            unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER,
1887
                                       aHeaders[i]);
1888
1889
        // Capture response headers.
1890
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERDATA,
1891
                                   papsResults[i]);
1892
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION,
1893
                                   CPLHdrWriteFct);
1894
1895
        asResults[i].psResult = papsResults[i];
1896
        const char *pszMaxFileSize =
1897
            CSLFetchNameValue(papszOptions, "MAX_FILE_SIZE");
1898
        if (pszMaxFileSize != nullptr)
1899
        {
1900
            asResults[i].nMaxFileSize = atoi(pszMaxFileSize);
1901
            // Only useful if size is returned by server before actual download.
1902
            unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXFILESIZE,
1903
                                       asResults[i].nMaxFileSize);
1904
        }
1905
1906
        unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEDATA,
1907
                                   &asResults[i]);
1908
        unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION,
1909
                                   CPLWriteFct);
1910
1911
        unchecked_curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER,
1912
                                   asErrorBuffers[i].szBuffer);
1913
1914
        if (bSupportGZip &&
1915
            CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
1916
        {
1917
            unchecked_curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
1918
        }
1919
1920
        asHandles.push_back(http_handle);
1921
    }
1922
1923
    int iCurRequest = 0;
1924
    for (;
1925
         iCurRequest <
1926
         std::min(nURLCount, nMaxSimultaneous > 0 ? nMaxSimultaneous : INT_MAX);
1927
         iCurRequest++)
1928
    {
1929
        CPLHTTPEmitFetchDebug(papszURL[iCurRequest],
1930
                              CPLSPrintf(" %d/%d", iCurRequest + 1, nURLCount));
1931
        curl_multi_add_handle(hCurlMultiHandle, asHandles[iCurRequest]);
1932
    }
1933
1934
    int repeats = 0;
1935
    void *old_handler = CPLHTTPIgnoreSigPipe();
1936
    while (true)
1937
    {
1938
        int still_running = 0;
1939
        while (curl_multi_perform(hCurlMultiHandle, &still_running) ==
1940
               CURLM_CALL_MULTI_PERFORM)
1941
        {
1942
            // loop
1943
        }
1944
        if (!still_running && iCurRequest == nURLCount)
1945
        {
1946
            break;
1947
        }
1948
1949
        bool bRequestsAdded = false;
1950
        CURLMsg *msg;
1951
        do
1952
        {
1953
            int msgq = 0;
1954
            msg = curl_multi_info_read(hCurlMultiHandle, &msgq);
1955
            if (msg && (msg->msg == CURLMSG_DONE))
1956
            {
1957
                if (iCurRequest < nURLCount)
1958
                {
1959
                    CPLHTTPEmitFetchDebug(
1960
                        papszURL[iCurRequest],
1961
                        CPLSPrintf(" %d/%d", iCurRequest + 1, nURLCount));
1962
                    curl_multi_add_handle(hCurlMultiHandle,
1963
                                          asHandles[iCurRequest]);
1964
                    iCurRequest++;
1965
                    bRequestsAdded = true;
1966
                }
1967
            }
1968
        } while (msg);
1969
1970
        if (!bRequestsAdded)
1971
            CPLMultiPerformWait(hCurlMultiHandle, repeats);
1972
    }
1973
    CPLHTTPRestoreSigPipeHandler(old_handler);
1974
1975
    for (int i = 0; i < nURLCount; i++)
1976
    {
1977
        if (asErrorBuffers[i].szBuffer[0] != '\0')
1978
            papsResults[i]->pszErrBuf = CPLStrdup(asErrorBuffers[i].szBuffer);
1979
        else
1980
        {
1981
            long response_code = 0;
1982
            curl_easy_getinfo(asHandles[i], CURLINFO_RESPONSE_CODE,
1983
                              &response_code);
1984
1985
            if (response_code >= 400 && response_code < 600)
1986
            {
1987
                papsResults[i]->pszErrBuf = CPLStrdup(CPLSPrintf(
1988
                    "HTTP error code : %d", static_cast<int>(response_code)));
1989
            }
1990
        }
1991
1992
        curl_easy_getinfo(asHandles[i], CURLINFO_CONTENT_TYPE,
1993
                          &(papsResults[i]->pszContentType));
1994
        if (papsResults[i]->pszContentType != nullptr)
1995
            papsResults[i]->pszContentType =
1996
                CPLStrdup(papsResults[i]->pszContentType);
1997
1998
        curl_multi_remove_handle(hCurlMultiHandle, asHandles[i]);
1999
        curl_easy_cleanup(asHandles[i]);
2000
    }
2001
2002
    if (!pszPersistent)
2003
        VSICURLMultiCleanup(hCurlMultiHandle);
2004
2005
    for (size_t i = 0; i < aHeaders.size(); i++)
2006
        curl_slist_free_all(aHeaders[i]);
2007
2008
    return papsResults;
2009
#endif /* def HAVE_CURL */
2010
0
}
2011
2012
/************************************************************************/
2013
/*                      CPLHTTPDestroyMultiResult()                     */
2014
/************************************************************************/
2015
/**
2016
 * \brief Clean the memory associated with the return value of
2017
 * CPLHTTPMultiFetch()
2018
 *
2019
 * @param papsResults pointer to the return value of CPLHTTPMultiFetch()
2020
 * @param nCount value of the nURLCount parameter passed to CPLHTTPMultiFetch()
2021
 * @since GDAL 2.3
2022
 */
2023
void CPLHTTPDestroyMultiResult(CPLHTTPResult **papsResults, int nCount)
2024
0
{
2025
0
    if (papsResults)
2026
0
    {
2027
0
        for (int i = 0; i < nCount; i++)
2028
0
        {
2029
0
            CPLHTTPDestroyResult(papsResults[i]);
2030
0
        }
2031
0
        CPLFree(papsResults);
2032
0
    }
2033
0
}
2034
2035
#ifdef HAVE_CURL
2036
2037
#ifdef _WIN32
2038
2039
#include <windows.h>
2040
2041
/************************************************************************/
2042
/*                     CPLFindWin32CurlCaBundleCrt()                    */
2043
/************************************************************************/
2044
2045
static const char *CPLFindWin32CurlCaBundleCrt()
2046
{
2047
    DWORD nResLen;
2048
    const DWORD nBufSize = MAX_PATH + 1;
2049
    char *pszFilePart = nullptr;
2050
2051
    char *pszPath = static_cast<char *>(CPLCalloc(1, nBufSize));
2052
2053
    nResLen = SearchPathA(nullptr, "curl-ca-bundle.crt", nullptr, nBufSize,
2054
                          pszPath, &pszFilePart);
2055
    if (nResLen > 0)
2056
    {
2057
        const char *pszRet = CPLSPrintf("%s", pszPath);
2058
        CPLFree(pszPath);
2059
        return pszRet;
2060
    }
2061
    CPLFree(pszPath);
2062
    return nullptr;
2063
}
2064
2065
#endif  // WIN32
2066
2067
/************************************************************************/
2068
/*                     CPLHTTPCurlDebugFunction()                       */
2069
/************************************************************************/
2070
2071
static int CPLHTTPCurlDebugFunction(CURL *handle, curl_infotype type,
2072
                                    char *data, size_t size, void *userp)
2073
{
2074
    (void)handle;
2075
    (void)userp;
2076
2077
    const char *pszDebugKey = nullptr;
2078
    if (type == CURLINFO_TEXT)
2079
    {
2080
        pszDebugKey = "CURL_INFO_TEXT";
2081
    }
2082
    else if (type == CURLINFO_HEADER_OUT)
2083
    {
2084
        pszDebugKey = "CURL_INFO_HEADER_OUT";
2085
    }
2086
    else if (type == CURLINFO_HEADER_IN)
2087
    {
2088
        pszDebugKey = "CURL_INFO_HEADER_IN";
2089
    }
2090
    else if (type == CURLINFO_DATA_IN &&
2091
             CPLTestBool(CPLGetConfigOption("CPL_CURL_VERBOSE_DATA_IN", "NO")))
2092
    {
2093
        pszDebugKey = "CURL_INFO_DATA_IN";
2094
    }
2095
2096
    if (pszDebugKey)
2097
    {
2098
        std::string osMsg(data, size);
2099
        if (!osMsg.empty() && osMsg.back() == '\n')
2100
            osMsg.pop_back();
2101
        CPLDebug(pszDebugKey, "%s", osMsg.c_str());
2102
    }
2103
    return 0;
2104
}
2105
2106
/************************************************************************/
2107
/*                         CPLHTTPSetOptions()                          */
2108
/************************************************************************/
2109
2110
// Note: papszOptions must be kept alive until curl_easy/multi_perform()
2111
// has completed, and we must be careful not to set short lived strings
2112
// with unchecked_curl_easy_setopt(), as long as we need to support curl < 7.17
2113
// see https://curl.haxx.se/libcurl/c/unchecked_curl_easy_setopt.html
2114
// caution: if we remove that assumption, we'll needto use
2115
// CURLOPT_COPYPOSTFIELDS
2116
2117
void *CPLHTTPSetOptions(void *pcurl, const char *pszURL,
2118
                        const char *const *papszOptions)
2119
{
2120
    CheckCurlFeatures();
2121
2122
    CURL *http_handle = reinterpret_cast<CURL *>(pcurl);
2123
2124
    unchecked_curl_easy_setopt(http_handle, CURLOPT_URL, pszURL);
2125
2126
    if (CPLTestBool(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO")))
2127
    {
2128
        unchecked_curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1);
2129
2130
        if (CPLIsDebugEnabled())
2131
        {
2132
            unchecked_curl_easy_setopt(http_handle, CURLOPT_DEBUGFUNCTION,
2133
                                       CPLHTTPCurlDebugFunction);
2134
        }
2135
    }
2136
2137
    const char *pszHttpVersion =
2138
        CSLFetchNameValue(papszOptions, "HTTP_VERSION");
2139
    if (pszHttpVersion == nullptr)
2140
        pszHttpVersion = CPLGetConfigOption("GDAL_HTTP_VERSION", nullptr);
2141
    if (pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0)
2142
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
2143
                                   CURL_HTTP_VERSION_1_0);
2144
    else if (pszHttpVersion && strcmp(pszHttpVersion, "1.1") == 0)
2145
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
2146
                                   CURL_HTTP_VERSION_1_1);
2147
    else if (pszHttpVersion && (strcmp(pszHttpVersion, "2") == 0 ||
2148
                                strcmp(pszHttpVersion, "2.0") == 0))
2149
    {
2150
        if (bSupportHTTP2)
2151
        {
2152
            // Try HTTP/2 both for HTTP and HTTPS. With fallback to HTTP/1.1
2153
            unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
2154
                                       CURL_HTTP_VERSION_2_0);
2155
        }
2156
    }
2157
    else if (pszHttpVersion && strcmp(pszHttpVersion, "2PRIOR_KNOWLEDGE") == 0)
2158
    {
2159
        if (bSupportHTTP2)
2160
        {
2161
            // Assume HTTP/2 is supported by the server. The cURL docs indicate
2162
            // that it makes no difference for HTTPS, but it does seem to work
2163
            // in practice.
2164
            unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
2165
                                       CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
2166
        }
2167
    }
2168
    else if (pszHttpVersion == nullptr || strcmp(pszHttpVersion, "2TLS") == 0)
2169
    {
2170
        if (bSupportHTTP2)
2171
        {
2172
            // Only enable this mode if explicitly required, or if the
2173
            // machine is a GCE instance. On other networks, requesting a
2174
            // file in HTTP/2 is found to be significantly slower than HTTP/1.1
2175
            // for unknown reasons.
2176
            if (pszHttpVersion != nullptr || CPLIsMachineForSureGCEInstance())
2177
            {
2178
                static bool bDebugEmitted = false;
2179
                if (!bDebugEmitted)
2180
                {
2181
                    CPLDebug("HTTP", "Using HTTP/2 for HTTPS when possible");
2182
                    bDebugEmitted = true;
2183
                }
2184
2185
                // CURL_HTTP_VERSION_2TLS means for HTTPS connection, try to
2186
                // negotiate HTTP/2 with the server (and fallback to HTTP/1.1
2187
                // otherwise), and for HTTP connection do HTTP/1
2188
                unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
2189
                                           CURL_HTTP_VERSION_2TLS);
2190
            }
2191
        }
2192
    }
2193
    else
2194
    {
2195
        CPLError(CE_Warning, CPLE_NotSupported, "HTTP_VERSION=%s not supported",
2196
                 pszHttpVersion);
2197
    }
2198
2199
    // Default value is 1 since curl 7.50.2. But worth applying it on
2200
    // previous versions as well.
2201
    const char *pszTCPNoDelay =
2202
        CSLFetchNameValueDef(papszOptions, "TCP_NODELAY", "1");
2203
    unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_NODELAY,
2204
                               atoi(pszTCPNoDelay));
2205
2206
    /* Support control over HTTPAUTH */
2207
    const char *pszHttpAuth = CSLFetchNameValue(papszOptions, "HTTPAUTH");
2208
    if (pszHttpAuth == nullptr)
2209
        pszHttpAuth = CPLGetConfigOption("GDAL_HTTP_AUTH", nullptr);
2210
    if (pszHttpAuth == nullptr)
2211
    {
2212
        /* do nothing */;
2213
    }
2214
    else if (EQUAL(pszHttpAuth, "BASIC"))
2215
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2216
                                   CURLAUTH_BASIC);
2217
    else if (EQUAL(pszHttpAuth, "NTLM"))
2218
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2219
                                   CURLAUTH_NTLM);
2220
    else if (EQUAL(pszHttpAuth, "ANY"))
2221
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
2222
    else if (EQUAL(pszHttpAuth, "ANYSAFE"))
2223
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2224
                                   CURLAUTH_ANYSAFE);
2225
    else if (EQUAL(pszHttpAuth, "BEARER"))
2226
    {
2227
        const char *pszAuthorizationHeaderAllowed = CSLFetchNameValueDef(
2228
            papszOptions, "AUTHORIZATION_HEADER_ALLOWED", "YES");
2229
        const bool bAuthorizationHeaderAllowed =
2230
            CPLTestBool(pszAuthorizationHeaderAllowed);
2231
        if (bAuthorizationHeaderAllowed)
2232
        {
2233
            const char *pszBearer =
2234
                CSLFetchNameValue(papszOptions, "HTTP_BEARER");
2235
            if (pszBearer == nullptr)
2236
                pszBearer = CPLGetConfigOption("GDAL_HTTP_BEARER", nullptr);
2237
            if (pszBearer != nullptr)
2238
                unchecked_curl_easy_setopt(http_handle, CURLOPT_XOAUTH2_BEARER,
2239
                                           pszBearer);
2240
            unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2241
                                       CURLAUTH_BEARER);
2242
        }
2243
    }
2244
    else if (EQUAL(pszHttpAuth, "NEGOTIATE"))
2245
        unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2246
                                   CURLAUTH_NEGOTIATE);
2247
    else
2248
    {
2249
        CPLError(CE_Warning, CPLE_AppDefined,
2250
                 "Unsupported HTTPAUTH value '%s', ignored.", pszHttpAuth);
2251
    }
2252
2253
    const char *pszGssDelegation =
2254
        CSLFetchNameValue(papszOptions, "GSSAPI_DELEGATION");
2255
    if (pszGssDelegation == nullptr)
2256
        pszGssDelegation =
2257
            CPLGetConfigOption("GDAL_GSSAPI_DELEGATION", nullptr);
2258
    if (pszGssDelegation == nullptr)
2259
    {
2260
    }
2261
    else if (EQUAL(pszGssDelegation, "NONE"))
2262
    {
2263
        unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2264
                                   CURLGSSAPI_DELEGATION_NONE);
2265
    }
2266
    else if (EQUAL(pszGssDelegation, "POLICY"))
2267
    {
2268
        unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2269
                                   CURLGSSAPI_DELEGATION_POLICY_FLAG);
2270
    }
2271
    else if (EQUAL(pszGssDelegation, "ALWAYS"))
2272
    {
2273
        unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2274
                                   CURLGSSAPI_DELEGATION_FLAG);
2275
    }
2276
    else
2277
    {
2278
        CPLError(CE_Warning, CPLE_AppDefined,
2279
                 "Unsupported GSSAPI_DELEGATION value '%s', ignored.",
2280
                 pszGssDelegation);
2281
    }
2282
2283
    // Support use of .netrc - default enabled.
2284
    const char *pszHttpNetrc = CSLFetchNameValue(papszOptions, "NETRC");
2285
    if (pszHttpNetrc == nullptr)
2286
        pszHttpNetrc = CPLGetConfigOption("GDAL_HTTP_NETRC", "YES");
2287
    if (pszHttpNetrc == nullptr || CPLTestBool(pszHttpNetrc))
2288
        unchecked_curl_easy_setopt(http_handle, CURLOPT_NETRC, 1L);
2289
2290
    // Custom .netrc file location
2291
    const char *pszHttpNetrcFile =
2292
        CSLFetchNameValue(papszOptions, "NETRC_FILE");
2293
    if (pszHttpNetrcFile == nullptr)
2294
        pszHttpNetrcFile = CPLGetConfigOption("GDAL_HTTP_NETRC_FILE", nullptr);
2295
    if (pszHttpNetrcFile)
2296
        unchecked_curl_easy_setopt(http_handle, CURLOPT_NETRC_FILE,
2297
                                   pszHttpNetrcFile);
2298
2299
    // Support setting userid:password.
2300
    const char *pszUserPwd = CSLFetchNameValue(papszOptions, "USERPWD");
2301
    if (pszUserPwd == nullptr)
2302
        pszUserPwd = CPLGetConfigOption("GDAL_HTTP_USERPWD", nullptr);
2303
    if (pszUserPwd != nullptr)
2304
        unchecked_curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd);
2305
2306
    // Set Proxy parameters.
2307
    const char *pszProxy = CSLFetchNameValue(papszOptions, "PROXY");
2308
    if (pszProxy == nullptr)
2309
        pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", nullptr);
2310
    if (pszProxy)
2311
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXY, pszProxy);
2312
2313
    const char *pszHttpsProxy = CSLFetchNameValue(papszOptions, "HTTPS_PROXY");
2314
    if (pszHttpsProxy == nullptr)
2315
        pszHttpsProxy = CPLGetConfigOption("GDAL_HTTPS_PROXY", nullptr);
2316
    if (pszHttpsProxy && (STARTS_WITH(pszURL, "https")))
2317
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXY, pszHttpsProxy);
2318
2319
    const char *pszProxyUserPwd =
2320
        CSLFetchNameValue(papszOptions, "PROXYUSERPWD");
2321
    if (pszProxyUserPwd == nullptr)
2322
        pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", nullptr);
2323
    if (pszProxyUserPwd)
2324
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYUSERPWD,
2325
                                   pszProxyUserPwd);
2326
2327
    // Support control over PROXYAUTH.
2328
    const char *pszProxyAuth = CSLFetchNameValue(papszOptions, "PROXYAUTH");
2329
    if (pszProxyAuth == nullptr)
2330
        pszProxyAuth = CPLGetConfigOption("GDAL_PROXY_AUTH", nullptr);
2331
    if (pszProxyAuth == nullptr)
2332
    {
2333
        // Do nothing.
2334
    }
2335
    else if (EQUAL(pszProxyAuth, "BASIC"))
2336
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2337
                                   CURLAUTH_BASIC);
2338
    else if (EQUAL(pszProxyAuth, "NTLM"))
2339
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2340
                                   CURLAUTH_NTLM);
2341
    else if (EQUAL(pszProxyAuth, "DIGEST"))
2342
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2343
                                   CURLAUTH_DIGEST);
2344
    else if (EQUAL(pszProxyAuth, "ANY"))
2345
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2346
                                   CURLAUTH_ANY);
2347
    else if (EQUAL(pszProxyAuth, "ANYSAFE"))
2348
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2349
                                   CURLAUTH_ANYSAFE);
2350
    else if (EQUAL(pszProxyAuth, "NEGOTIATE"))
2351
        unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2352
                                   CURLAUTH_NEGOTIATE);
2353
    else
2354
    {
2355
        CPLError(CE_Warning, CPLE_AppDefined,
2356
                 "Unsupported PROXYAUTH value '%s', ignored.", pszProxyAuth);
2357
    }
2358
2359
    unchecked_curl_easy_setopt(http_handle, CURLOPT_SUPPRESS_CONNECT_HEADERS,
2360
                               1L);
2361
2362
    unchecked_curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1);
2363
    const char *pszUnrestrictedAuth = CPLGetConfigOption(
2364
        "CPL_VSIL_CURL_AUTHORIZATION_HEADER_ALLOWED_IF_REDIRECT",
2365
        "IF_SAME_HOST");
2366
    if (!EQUAL(pszUnrestrictedAuth, "IF_SAME_HOST") &&
2367
        CPLTestBool(pszUnrestrictedAuth))
2368
    {
2369
        unchecked_curl_easy_setopt(http_handle, CURLOPT_UNRESTRICTED_AUTH, 1);
2370
    }
2371
2372
    unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10);
2373
    unchecked_curl_easy_setopt(http_handle, CURLOPT_POSTREDIR,
2374
                               CURL_REDIR_POST_ALL);
2375
2376
    // Set connect timeout.
2377
    const char *pszConnectTimeout =
2378
        CSLFetchNameValue(papszOptions, "CONNECTTIMEOUT");
2379
    if (pszConnectTimeout == nullptr)
2380
        pszConnectTimeout =
2381
            CPLGetConfigOption("GDAL_HTTP_CONNECTTIMEOUT", nullptr);
2382
    if (pszConnectTimeout != nullptr)
2383
    {
2384
        // coverity[tainted_data]
2385
        unchecked_curl_easy_setopt(
2386
            http_handle, CURLOPT_CONNECTTIMEOUT_MS,
2387
            static_cast<int>(1000 * CPLAtof(pszConnectTimeout)));
2388
    }
2389
2390
    // Set timeout.
2391
    const char *pszTimeout = CSLFetchNameValue(papszOptions, "TIMEOUT");
2392
    if (pszTimeout == nullptr)
2393
        pszTimeout = CPLGetConfigOption("GDAL_HTTP_TIMEOUT", nullptr);
2394
    if (pszTimeout != nullptr)
2395
    {
2396
        // coverity[tainted_data]
2397
        unchecked_curl_easy_setopt(
2398
            http_handle, CURLOPT_TIMEOUT_MS,
2399
            static_cast<int>(1000 * CPLAtof(pszTimeout)));
2400
    }
2401
2402
    // Set low speed time and limit.
2403
    const char *pszLowSpeedTime =
2404
        CSLFetchNameValue(papszOptions, "LOW_SPEED_TIME");
2405
    if (pszLowSpeedTime == nullptr)
2406
        pszLowSpeedTime =
2407
            CPLGetConfigOption("GDAL_HTTP_LOW_SPEED_TIME", nullptr);
2408
    if (pszLowSpeedTime != nullptr)
2409
    {
2410
        unchecked_curl_easy_setopt(http_handle, CURLOPT_LOW_SPEED_TIME,
2411
                                   atoi(pszLowSpeedTime));
2412
2413
        const char *pszLowSpeedLimit =
2414
            CSLFetchNameValue(papszOptions, "LOW_SPEED_LIMIT");
2415
        if (pszLowSpeedLimit == nullptr)
2416
            pszLowSpeedLimit =
2417
                CPLGetConfigOption("GDAL_HTTP_LOW_SPEED_LIMIT", "1");
2418
        unchecked_curl_easy_setopt(http_handle, CURLOPT_LOW_SPEED_LIMIT,
2419
                                   atoi(pszLowSpeedLimit));
2420
    }
2421
2422
    /* Disable some SSL verification */
2423
    const char *pszUnsafeSSL = CSLFetchNameValue(papszOptions, "UNSAFESSL");
2424
    if (pszUnsafeSSL == nullptr)
2425
        pszUnsafeSSL = CPLGetConfigOption("GDAL_HTTP_UNSAFESSL", nullptr);
2426
    if (pszUnsafeSSL != nullptr && CPLTestBool(pszUnsafeSSL))
2427
    {
2428
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L);
2429
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L);
2430
    }
2431
2432
    const char *pszUseCAPIStore =
2433
        CSLFetchNameValue(papszOptions, "USE_CAPI_STORE");
2434
    if (pszUseCAPIStore == nullptr)
2435
        pszUseCAPIStore = CPLGetConfigOption("GDAL_HTTP_USE_CAPI_STORE", "NO");
2436
    if (CPLTestBool(pszUseCAPIStore))
2437
    {
2438
#if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2439
        // Use certificates from Windows certificate store; requires
2440
        // crypt32.lib, OpenSSL crypto and ssl libraries.
2441
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_CTX_FUNCTION,
2442
                                   *CPL_ssl_ctx_callback);
2443
#else   // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2444
        CPLError(CE_Warning, CPLE_NotSupported,
2445
                 "GDAL_HTTP_USE_CAPI_STORE requested, but libcurl too old, "
2446
                 "non-Windows platform or OpenSSL missing.");
2447
#endif  // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2448
    }
2449
2450
    // Enable OCSP stapling if requested.
2451
    const char *pszSSLVerifyStatus =
2452
        CSLFetchNameValue(papszOptions, "SSL_VERIFYSTATUS");
2453
    if (pszSSLVerifyStatus == nullptr)
2454
        pszSSLVerifyStatus =
2455
            CPLGetConfigOption("GDAL_HTTP_SSL_VERIFYSTATUS", "NO");
2456
    if (CPLTestBool(pszSSLVerifyStatus))
2457
    {
2458
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYSTATUS, 1L);
2459
    }
2460
2461
    // Custom path to SSL certificates.
2462
    const char *pszCAInfo = CSLFetchNameValue(papszOptions, "CAINFO");
2463
    if (pszCAInfo == nullptr)
2464
        // Name of GDAL environment variable for the CA Bundle path
2465
        pszCAInfo = CPLGetConfigOption("GDAL_CURL_CA_BUNDLE", nullptr);
2466
    if (pszCAInfo == nullptr)
2467
        // Name of environment variable used by the curl binary
2468
        pszCAInfo = CPLGetConfigOption("CURL_CA_BUNDLE", nullptr);
2469
    if (pszCAInfo == nullptr)
2470
        // Name of environment variable used by the curl binary (tested
2471
        // after CURL_CA_BUNDLE
2472
        pszCAInfo = CPLGetConfigOption("SSL_CERT_FILE", nullptr);
2473
#ifdef _WIN32
2474
    if (pszCAInfo == nullptr)
2475
    {
2476
        pszCAInfo = CPLFindWin32CurlCaBundleCrt();
2477
    }
2478
#endif
2479
    if (pszCAInfo != nullptr)
2480
    {
2481
        unchecked_curl_easy_setopt(http_handle, CURLOPT_CAINFO, pszCAInfo);
2482
    }
2483
2484
    const char *pszCAPath = CSLFetchNameValue(papszOptions, "CAPATH");
2485
    if (pszCAPath != nullptr)
2486
    {
2487
        unchecked_curl_easy_setopt(http_handle, CURLOPT_CAPATH, pszCAPath);
2488
    }
2489
2490
    // Support for SSL client certificates
2491
2492
    // Filename of the the client certificate
2493
    const char *pszSSLCert = CSLFetchNameValue(papszOptions, "SSLCERT");
2494
    if (!pszSSLCert)
2495
        pszSSLCert = CPLGetConfigOption("GDAL_HTTP_SSLCERT", nullptr);
2496
    if (pszSSLCert)
2497
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLCERT, pszSSLCert);
2498
2499
    // private key file for TLS and SSL client cert
2500
    const char *pszSSLKey = CSLFetchNameValue(papszOptions, "SSLKEY");
2501
    if (!pszSSLKey)
2502
        pszSSLKey = CPLGetConfigOption("GDAL_HTTP_SSLKEY", nullptr);
2503
    if (pszSSLKey)
2504
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLKEY, pszSSLKey);
2505
2506
    // type of client SSL certificate ("PEM", "DER", ...)
2507
    const char *pszSSLCertType = CSLFetchNameValue(papszOptions, "SSLCERTTYPE");
2508
    if (!pszSSLCertType)
2509
        pszSSLCertType = CPLGetConfigOption("GDAL_HTTP_SSLCERTTYPE", nullptr);
2510
    if (pszSSLCertType)
2511
        unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLCERTTYPE,
2512
                                   pszSSLCertType);
2513
2514
    // passphrase to private key
2515
    const char *pszKeyPasswd = CSLFetchNameValue(papszOptions, "KEYPASSWD");
2516
    if (!pszKeyPasswd)
2517
        pszKeyPasswd = CPLGetConfigOption("GDAL_HTTP_KEYPASSWD", nullptr);
2518
    if (pszKeyPasswd)
2519
        unchecked_curl_easy_setopt(http_handle, CURLOPT_KEYPASSWD,
2520
                                   pszKeyPasswd);
2521
2522
    /* Set Referer */
2523
    const char *pszReferer = CSLFetchNameValue(papszOptions, "REFERER");
2524
    if (pszReferer != nullptr)
2525
        unchecked_curl_easy_setopt(http_handle, CURLOPT_REFERER, pszReferer);
2526
2527
    /* Set User-Agent */
2528
    const char *pszUserAgent = CSLFetchNameValue(papszOptions, "USERAGENT");
2529
    if (pszUserAgent == nullptr)
2530
        pszUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT",
2531
                                          gosDefaultUserAgent.c_str());
2532
    if (pszUserAgent != nullptr && !EQUAL(pszUserAgent, ""))
2533
    {
2534
        unchecked_curl_easy_setopt(http_handle, CURLOPT_USERAGENT,
2535
                                   pszUserAgent);
2536
    }
2537
2538
    /* NOSIGNAL should be set to true for timeout to work in multithread
2539
     * environments on Unix, requires libcurl 7.10 or more recent.
2540
     * (this force avoiding the use of signal handlers)
2541
     */
2542
    unchecked_curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1);
2543
2544
    const char *pszFormFilePath =
2545
        CSLFetchNameValue(papszOptions, "FORM_FILE_PATH");
2546
    const char *pszParametersCount =
2547
        CSLFetchNameValue(papszOptions, "FORM_ITEM_COUNT");
2548
    if (pszFormFilePath == nullptr && pszParametersCount == nullptr)
2549
    {
2550
        /* Set POST mode */
2551
        const char *pszPost = CSLFetchNameValue(papszOptions, "POSTFIELDS");
2552
        if (pszPost != nullptr)
2553
        {
2554
            CPLDebug("HTTP", "These POSTFIELDS were sent:%.4000s", pszPost);
2555
            unchecked_curl_easy_setopt(http_handle, CURLOPT_POST, 1);
2556
            unchecked_curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS,
2557
                                       pszPost);
2558
        }
2559
    }
2560
2561
    const char *pszCustomRequest =
2562
        CSLFetchNameValue(papszOptions, "CUSTOMREQUEST");
2563
    if (pszCustomRequest != nullptr)
2564
    {
2565
        unchecked_curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST,
2566
                                   pszCustomRequest);
2567
    }
2568
2569
    const char *pszCookie = CSLFetchNameValue(papszOptions, "COOKIE");
2570
    if (pszCookie == nullptr)
2571
        pszCookie = CPLGetConfigOption("GDAL_HTTP_COOKIE", nullptr);
2572
    if (pszCookie != nullptr)
2573
        unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIE, pszCookie);
2574
2575
    const char *pszCookieFile = CSLFetchNameValue(papszOptions, "COOKIEFILE");
2576
    if (pszCookieFile == nullptr)
2577
        pszCookieFile = CPLGetConfigOption("GDAL_HTTP_COOKIEFILE", nullptr);
2578
    if (pszCookieFile != nullptr)
2579
        unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIEFILE,
2580
                                   pszCookieFile);
2581
2582
    const char *pszCookieJar = CSLFetchNameValue(papszOptions, "COOKIEJAR");
2583
    if (pszCookieJar == nullptr)
2584
        pszCookieJar = CPLGetConfigOption("GDAL_HTTP_COOKIEJAR", nullptr);
2585
    if (pszCookieJar != nullptr)
2586
        unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIEJAR,
2587
                                   pszCookieJar);
2588
2589
    // TCP keep-alive
2590
    const char *pszTCPKeepAlive =
2591
        CSLFetchNameValue(papszOptions, "TCP_KEEPALIVE");
2592
    if (pszTCPKeepAlive == nullptr)
2593
        pszTCPKeepAlive = CPLGetConfigOption("GDAL_HTTP_TCP_KEEPALIVE", "YES");
2594
    if (pszTCPKeepAlive != nullptr && CPLTestBool(pszTCPKeepAlive))
2595
    {
2596
        // Set keep-alive interval.
2597
        int nKeepAliveInterval = 60;
2598
        const char *pszKeepAliveInterval =
2599
            CSLFetchNameValue(papszOptions, "TCP_KEEPINTVL");
2600
        if (pszKeepAliveInterval == nullptr)
2601
            pszKeepAliveInterval =
2602
                CPLGetConfigOption("GDAL_HTTP_TCP_KEEPINTVL", nullptr);
2603
        if (pszKeepAliveInterval != nullptr)
2604
            nKeepAliveInterval = atoi(pszKeepAliveInterval);
2605
2606
        // Set keep-alive idle wait time.
2607
        int nKeepAliveIdle = 60;
2608
        const char *pszKeepAliveIdle =
2609
            CSLFetchNameValue(papszOptions, "TCP_KEEPIDLE");
2610
        if (pszKeepAliveIdle == nullptr)
2611
            pszKeepAliveIdle =
2612
                CPLGetConfigOption("GDAL_HTTP_TCP_KEEPIDLE", nullptr);
2613
        if (pszKeepAliveIdle != nullptr)
2614
            nKeepAliveIdle = atoi(pszKeepAliveIdle);
2615
2616
        unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPALIVE, 1L);
2617
        unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPINTVL,
2618
                                   nKeepAliveInterval);
2619
        unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPIDLE,
2620
                                   nKeepAliveIdle);
2621
    }
2622
2623
    struct curl_slist *headers = nullptr;
2624
    const char *pszAccept = CSLFetchNameValue(papszOptions, "ACCEPT");
2625
    if (pszAccept)
2626
    {
2627
        headers =
2628
            curl_slist_append(headers, CPLSPrintf("Accept: %s", pszAccept));
2629
    }
2630
2631
    const auto AddHeader = [&headers, pszAccept](const char *pszHeader)
2632
    {
2633
        if (STARTS_WITH_CI(pszHeader, "Accept:") && pszAccept)
2634
        {
2635
            const char *pszVal = pszHeader + strlen("Accept:");
2636
            while (*pszVal == ' ')
2637
                ++pszVal;
2638
            if (!EQUAL(pszVal, pszAccept))
2639
            {
2640
                // Cf https://github.com/OSGeo/gdal/issues/7691#issuecomment-2873711603
2641
                CPLDebug(
2642
                    "HTTP",
2643
                    "Ignoring '%s' since ACCEPT option = '%s' is specified",
2644
                    pszHeader, pszAccept);
2645
            }
2646
        }
2647
        else
2648
        {
2649
            headers = curl_slist_append(headers, pszHeader);
2650
        }
2651
    };
2652
2653
    const char *pszHeaderFile = CSLFetchNameValue(papszOptions, "HEADER_FILE");
2654
    if (pszHeaderFile == nullptr)
2655
        pszHeaderFile = CPLGetConfigOption("GDAL_HTTP_HEADER_FILE", nullptr);
2656
    if (pszHeaderFile != nullptr)
2657
    {
2658
        VSILFILE *fp = nullptr;
2659
        // Do not allow /vsicurl/ access from /vsicurl because of
2660
        // GetCurlHandleFor() e.g. "/vsicurl/,HEADER_FILE=/vsicurl/,url= " would
2661
        // cause use of memory after free
2662
        if (!STARTS_WITH(pszHeaderFile, "/vsi") ||
2663
            STARTS_WITH(pszHeaderFile, "/vsimem/"))
2664
        {
2665
            fp = VSIFOpenL(pszHeaderFile, "rb");
2666
        }
2667
        if (fp == nullptr)
2668
        {
2669
            CPLError(CE_Failure, CPLE_FileIO, "Cannot read %s", pszHeaderFile);
2670
        }
2671
        else
2672
        {
2673
            const char *pszLine = nullptr;
2674
            while ((pszLine = CPLReadLineL(fp)) != nullptr)
2675
            {
2676
                AddHeader(pszLine);
2677
            }
2678
            VSIFCloseL(fp);
2679
        }
2680
    }
2681
2682
    const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
2683
    if (pszHeaders == nullptr)
2684
        pszHeaders = CPLGetConfigOption("GDAL_HTTP_HEADERS", nullptr);
2685
    if (pszHeaders)
2686
    {
2687
        bool bHeadersDone = false;
2688
        // Compatibility hack for "HEADERS=Accept: text/plain, application/json"
2689
        if (strstr(pszHeaders, "\r\n") == nullptr)
2690
        {
2691
            const char *pszComma = strchr(pszHeaders, ',');
2692
            if (pszComma != nullptr && strchr(pszComma, ':') == nullptr)
2693
            {
2694
                AddHeader(pszHeaders);
2695
                bHeadersDone = true;
2696
            }
2697
        }
2698
        if (!bHeadersDone)
2699
        {
2700
            const char *pszAuthorizationHeaderAllowed = CSLFetchNameValueDef(
2701
                papszOptions, "AUTHORIZATION_HEADER_ALLOWED", "YES");
2702
            const bool bAuthorizationHeaderAllowed =
2703
                CPLTestBool(pszAuthorizationHeaderAllowed);
2704
2705
            // We accept both raw headers with \r\n as a separator, or as
2706
            // a comma separated list of foo: bar values.
2707
            const CPLStringList aosTokens(
2708
                strstr(pszHeaders, "\r\n")
2709
                    ? CSLTokenizeString2(pszHeaders, "\r\n", 0)
2710
                    : CSLTokenizeString2(pszHeaders, ",", CSLT_HONOURSTRINGS));
2711
            for (int i = 0; i < aosTokens.size(); ++i)
2712
            {
2713
                if (bAuthorizationHeaderAllowed ||
2714
                    !STARTS_WITH_CI(aosTokens[i], "Authorization:"))
2715
                {
2716
                    AddHeader(aosTokens[i]);
2717
                }
2718
            }
2719
        }
2720
    }
2721
2722
    return headers;
2723
}
2724
2725
/************************************************************************/
2726
/*                         CPLHTTPIgnoreSigPipe()                       */
2727
/************************************************************************/
2728
2729
/* If using OpenSSL with Curl, openssl can cause SIGPIPE to be triggered */
2730
/* As we set CURLOPT_NOSIGNAL = 1, we must manually handle this situation */
2731
2732
void *CPLHTTPIgnoreSigPipe()
2733
{
2734
#if defined(SIGPIPE) && defined(HAVE_SIGACTION)
2735
    struct sigaction old_pipe_act;
2736
    struct sigaction action;
2737
    /* Get previous handler */
2738
    memset(&old_pipe_act, 0, sizeof(struct sigaction));
2739
    sigaction(SIGPIPE, nullptr, &old_pipe_act);
2740
2741
    /* Install new handler */
2742
    action = old_pipe_act;
2743
    action.sa_handler = SIG_IGN;
2744
    sigaction(SIGPIPE, &action, nullptr);
2745
2746
    void *ret = CPLMalloc(sizeof(old_pipe_act));
2747
    memcpy(ret, &old_pipe_act, sizeof(old_pipe_act));
2748
    return ret;
2749
#else
2750
    return nullptr;
2751
#endif
2752
}
2753
2754
/************************************************************************/
2755
/*                     CPLHTTPRestoreSigPipeHandler()                   */
2756
/************************************************************************/
2757
2758
void CPLHTTPRestoreSigPipeHandler(void *old_handler)
2759
{
2760
#if defined(SIGPIPE) && defined(HAVE_SIGACTION)
2761
    sigaction(SIGPIPE, static_cast<struct sigaction *>(old_handler), nullptr);
2762
    CPLFree(old_handler);
2763
#else
2764
    (void)old_handler;
2765
#endif
2766
}
2767
2768
#endif  // def HAVE_CURL
2769
2770
/************************************************************************/
2771
/*                           CPLHTTPEnabled()                           */
2772
/************************************************************************/
2773
2774
/**
2775
 * \brief Return if CPLHTTP services can be useful
2776
 *
2777
 * Those services depend on GDAL being build with libcurl support.
2778
 *
2779
 * @return TRUE if libcurl support is enabled
2780
 */
2781
int CPLHTTPEnabled()
2782
2783
0
{
2784
#ifdef HAVE_CURL
2785
    return TRUE;
2786
#else
2787
0
    return FALSE;
2788
0
#endif
2789
0
}
2790
2791
/************************************************************************/
2792
/*                           CPLHTTPCleanup()                           */
2793
/************************************************************************/
2794
2795
/**
2796
 * \brief Cleanup function to call at application termination
2797
 */
2798
void CPLHTTPCleanup()
2799
2800
0
{
2801
#ifdef HAVE_CURL
2802
    if (!hSessionMapMutex)
2803
        return;
2804
2805
    {
2806
        CPLMutexHolder oHolder(&hSessionMapMutex);
2807
        if (poSessionMap)
2808
        {
2809
            for (auto &kv : *poSessionMap)
2810
            {
2811
                curl_easy_cleanup(kv.second);
2812
            }
2813
            delete poSessionMap;
2814
            poSessionMap = nullptr;
2815
        }
2816
        if (poSessionMultiMap)
2817
        {
2818
            for (auto &kv : *poSessionMultiMap)
2819
            {
2820
                VSICURLMultiCleanup(kv.second);
2821
            }
2822
            delete poSessionMultiMap;
2823
            poSessionMultiMap = nullptr;
2824
        }
2825
    }
2826
2827
    // Not quite a safe sequence.
2828
    CPLDestroyMutex(hSessionMapMutex);
2829
    hSessionMapMutex = nullptr;
2830
2831
#if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2832
    // This cleanup must be absolutely done before CPLOpenSSLCleanup()
2833
    // for some unknown reason, but otherwise X509_free() in
2834
    // CPLWindowsCertificateListCleanup() will crash.
2835
    CPLWindowsCertificateListCleanup();
2836
#endif
2837
2838
#if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
2839
    CPLOpenSSLCleanup();
2840
#endif
2841
2842
#endif
2843
0
}
2844
2845
/************************************************************************/
2846
/*                        CPLHTTPDestroyResult()                        */
2847
/************************************************************************/
2848
2849
/**
2850
 * \brief Clean the memory associated with the return value of CPLHTTPFetch()
2851
 *
2852
 * @param psResult pointer to the return value of CPLHTTPFetch()
2853
 */
2854
void CPLHTTPDestroyResult(CPLHTTPResult *psResult)
2855
2856
0
{
2857
0
    if (psResult)
2858
0
    {
2859
0
        CPLFree(psResult->pabyData);
2860
0
        CPLFree(psResult->pszErrBuf);
2861
0
        CPLFree(psResult->pszContentType);
2862
0
        CSLDestroy(psResult->papszHeaders);
2863
2864
0
        for (int i = 0; i < psResult->nMimePartCount; i++)
2865
0
        {
2866
0
            CSLDestroy(psResult->pasMimePart[i].papszHeaders);
2867
0
        }
2868
0
        CPLFree(psResult->pasMimePart);
2869
2870
0
        CPLFree(psResult);
2871
0
    }
2872
0
}
2873
2874
/************************************************************************/
2875
/*                     CPLHTTPParseMultipartMime()                      */
2876
/************************************************************************/
2877
2878
/**
2879
 * \brief Parses a MIME multipart message.
2880
 *
2881
 * This function will iterate over each part and put it in a separate
2882
 * element of the pasMimePart array of the provided psResult structure.
2883
 *
2884
 * @param psResult pointer to the return value of CPLHTTPFetch()
2885
 * @return TRUE if the message contains MIME multipart message.
2886
 */
2887
int CPLHTTPParseMultipartMime(CPLHTTPResult *psResult)
2888
2889
0
{
2890
    /* -------------------------------------------------------------------- */
2891
    /*      Is it already done?                                             */
2892
    /* -------------------------------------------------------------------- */
2893
0
    if (psResult->nMimePartCount > 0)
2894
0
        return TRUE;
2895
2896
    /* -------------------------------------------------------------------- */
2897
    /*      Find the boundary setting in the content type.                  */
2898
    /* -------------------------------------------------------------------- */
2899
0
    const char *pszBound = nullptr;
2900
2901
0
    if (psResult->pszContentType != nullptr)
2902
0
        pszBound = strstr(psResult->pszContentType, "boundary=");
2903
2904
0
    if (pszBound == nullptr)
2905
0
    {
2906
0
        CPLError(CE_Failure, CPLE_AppDefined,
2907
0
                 "Unable to parse multi-part mime, no boundary setting.");
2908
0
        return FALSE;
2909
0
    }
2910
2911
0
    CPLString osBoundary;
2912
0
    char **papszTokens =
2913
0
        CSLTokenizeStringComplex(pszBound + 9, "\n ;", TRUE, FALSE);
2914
2915
0
    if (CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0)
2916
0
    {
2917
0
        CPLError(CE_Failure, CPLE_AppDefined,
2918
0
                 "Unable to parse multi-part mime, boundary not parsable.");
2919
0
        CSLDestroy(papszTokens);
2920
0
        return FALSE;
2921
0
    }
2922
2923
0
    osBoundary = "--";
2924
0
    osBoundary += papszTokens[0];
2925
0
    CSLDestroy(papszTokens);
2926
2927
    /* -------------------------------------------------------------------- */
2928
    /*      Find the start of the first chunk.                              */
2929
    /* -------------------------------------------------------------------- */
2930
0
    char *pszNext = psResult->pabyData
2931
0
                        ? strstr(reinterpret_cast<char *>(psResult->pabyData),
2932
0
                                 osBoundary.c_str())
2933
0
                        : nullptr;
2934
2935
0
    if (pszNext == nullptr)
2936
0
    {
2937
0
        CPLError(CE_Failure, CPLE_AppDefined, "No parts found.");
2938
0
        return FALSE;
2939
0
    }
2940
2941
0
    pszNext += osBoundary.size();
2942
0
    while (*pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0')
2943
0
        pszNext++;
2944
0
    if (*pszNext == '\r')
2945
0
        pszNext++;
2946
0
    if (*pszNext == '\n')
2947
0
        pszNext++;
2948
2949
    /* -------------------------------------------------------------------- */
2950
    /*      Loop over parts...                                              */
2951
    /* -------------------------------------------------------------------- */
2952
0
    while (true)
2953
0
    {
2954
0
        psResult->nMimePartCount++;
2955
0
        psResult->pasMimePart = static_cast<CPLMimePart *>(
2956
0
            CPLRealloc(psResult->pasMimePart,
2957
0
                       sizeof(CPLMimePart) * psResult->nMimePartCount));
2958
2959
0
        CPLMimePart *psPart =
2960
0
            psResult->pasMimePart + psResult->nMimePartCount - 1;
2961
2962
0
        memset(psPart, 0, sizeof(CPLMimePart));
2963
2964
        /* --------------------------------------------------------------------
2965
         */
2966
        /*      Collect headers. */
2967
        /* --------------------------------------------------------------------
2968
         */
2969
0
        while (*pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0')
2970
0
        {
2971
0
            if (!STARTS_WITH(pszNext, "Content-"))
2972
0
            {
2973
0
                break;
2974
0
            }
2975
0
            char *pszEOL = strstr(pszNext, "\n");
2976
2977
0
            if (pszEOL == nullptr)
2978
0
            {
2979
0
                CPLError(CE_Failure, CPLE_AppDefined,
2980
0
                         "Error while parsing multipart content (at line %d)",
2981
0
                         __LINE__);
2982
0
                return FALSE;
2983
0
            }
2984
2985
0
            *pszEOL = '\0';
2986
0
            bool bRestoreAntislashR = false;
2987
0
            if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r')
2988
0
            {
2989
0
                bRestoreAntislashR = true;
2990
0
                pszEOL[-1] = '\0';
2991
0
            }
2992
0
            char *pszKey = nullptr;
2993
0
            const char *pszValue = CPLParseNameValue(pszNext, &pszKey);
2994
0
            if (pszKey && pszValue)
2995
0
            {
2996
0
                psPart->papszHeaders =
2997
0
                    CSLSetNameValue(psPart->papszHeaders, pszKey, pszValue);
2998
0
            }
2999
0
            CPLFree(pszKey);
3000
0
            if (bRestoreAntislashR)
3001
0
                pszEOL[-1] = '\r';
3002
0
            *pszEOL = '\n';
3003
3004
0
            pszNext = pszEOL + 1;
3005
0
        }
3006
3007
0
        if (*pszNext == '\r')
3008
0
            pszNext++;
3009
0
        if (*pszNext == '\n')
3010
0
            pszNext++;
3011
3012
        /* --------------------------------------------------------------------
3013
         */
3014
        /*      Work out the data block size. */
3015
        /* --------------------------------------------------------------------
3016
         */
3017
0
        psPart->pabyData = reinterpret_cast<GByte *>(pszNext);
3018
3019
0
        int nBytesAvail = psResult->nDataLen -
3020
0
                          static_cast<int>(pszNext - reinterpret_cast<char *>(
3021
0
                                                         psResult->pabyData));
3022
3023
0
        while (nBytesAvail > 0 &&
3024
0
               (*pszNext != '-' ||
3025
0
                strncmp(pszNext, osBoundary, osBoundary.size()) != 0))
3026
0
        {
3027
0
            pszNext++;
3028
0
            nBytesAvail--;
3029
0
        }
3030
3031
0
        if (nBytesAvail == 0)
3032
0
        {
3033
0
            CPLError(CE_Failure, CPLE_AppDefined,
3034
0
                     "Error while parsing multipart content (at line %d)",
3035
0
                     __LINE__);
3036
0
            return FALSE;
3037
0
        }
3038
3039
0
        psPart->nDataLen = static_cast<int>(
3040
0
            pszNext - reinterpret_cast<char *>(psPart->pabyData));
3041
        // Normally the part should end with "\r\n--boundary_marker"
3042
0
        if (psPart->nDataLen >= 2 && pszNext[-2] == '\r' && pszNext[-1] == '\n')
3043
0
        {
3044
0
            psPart->nDataLen -= 2;
3045
0
        }
3046
3047
0
        pszNext += osBoundary.size();
3048
3049
0
        if (STARTS_WITH(pszNext, "--"))
3050
0
        {
3051
0
            break;
3052
0
        }
3053
3054
0
        if (*pszNext == '\r')
3055
0
            pszNext++;
3056
0
        if (*pszNext == '\n')
3057
0
            pszNext++;
3058
0
        else
3059
0
        {
3060
0
            CPLError(CE_Failure, CPLE_AppDefined,
3061
0
                     "Error while parsing multipart content (at line %d)",
3062
0
                     __LINE__);
3063
0
            return FALSE;
3064
0
        }
3065
0
    }
3066
3067
0
    return TRUE;
3068
0
}
3069
3070
#if defined(__GNUC__)
3071
#pragma GCC diagnostic pop
3072
#endif