LCOV - code coverage report
Current view: top level - frmts/http - httpdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 92 106 86.8 %
Date: 2026-04-19 18:43:50 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WCS Client Driver
       4             :  * Purpose:  Implementation of an HTTP fetching driver.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, 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_error_internal.h"
      15             : #include "cpl_string.h"
      16             : #include "cpl_http.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "gdal_driver.h"
      20             : #include "gdal_drivermanager.h"
      21             : #include "gdal_openinfo.h"
      22             : #include "gdal_cpp_functions.h"
      23             : 
      24           2 : static std::string SanitizeDispositionFilename(const std::string &osVal)
      25             : {
      26           4 :     std::string osRet(osVal);
      27           2 :     if (!osRet.empty() && osRet[0] == '"')
      28             :     {
      29           2 :         const auto nEnd = osRet.find('"', 1);
      30           2 :         if (nEnd != std::string::npos)
      31           2 :             return osRet.substr(1, nEnd - 1);
      32             :     }
      33           0 :     return osRet;
      34             : }
      35             : 
      36             : /************************************************************************/
      37             : /*                HTTPFetchContentDispositionFilename()                 */
      38             : /************************************************************************/
      39             : 
      40          18 : static std::string HTTPFetchContentDispositionFilename(char **papszHeaders)
      41             : {
      42          18 :     char **papszIter = papszHeaders;
      43         153 :     while (papszIter && *papszIter)
      44             :     {
      45             :         /* For multipart, we have in raw format, but without end-of-line
      46             :          * characters */
      47         137 :         if (STARTS_WITH(*papszIter,
      48             :                         "Content-Disposition: attachment; filename="))
      49             :         {
      50           0 :             return SanitizeDispositionFilename(*papszIter + 42);
      51             :         }
      52             :         /* For single part, the headers are in KEY=VAL format, but with e-o-l
      53             :          * ... */
      54         137 :         else if (STARTS_WITH(*papszIter,
      55             :                              "Content-Disposition=attachment; filename="))
      56             :         {
      57           2 :             char *pszVal = (*papszIter + 41);
      58           2 :             char *pszEOL = strchr(pszVal, '\r');
      59           2 :             if (pszEOL)
      60           0 :                 *pszEOL = 0;
      61           2 :             pszEOL = strchr(pszVal, '\n');
      62           2 :             if (pszEOL)
      63           0 :                 *pszEOL = 0;
      64           2 :             return SanitizeDispositionFilename(pszVal);
      65             :         }
      66         135 :         papszIter++;
      67             :     }
      68          16 :     return std::string();
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                              HTTPOpen()                              */
      73             : /************************************************************************/
      74             : 
      75       32961 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
      76             : 
      77             : {
      78       32961 :     if (poOpenInfo->nHeaderBytes != 0)
      79        1735 :         return nullptr;
      80             : 
      81       31226 :     const char *pszFilename = poOpenInfo->pszFilename;
      82       31226 :     if (STARTS_WITH_CI(pszFilename, "HTTP:http://") ||
      83       31226 :         STARTS_WITH_CI(pszFilename, "HTTP:https://") ||
      84       31226 :         STARTS_WITH_CI(pszFilename, "HTTP:ftp://"))
      85             :     {
      86           0 :         pszFilename += strlen("HTTP:");
      87             :     }
      88       31226 :     else if (!STARTS_WITH(pszFilename, "http://") &&
      89       31209 :              !STARTS_WITH(pszFilename, "https://") &&
      90       31203 :              !STARTS_WITH(pszFilename, "ftp://"))
      91             :     {
      92       31203 :         return nullptr;
      93             :     }
      94             : 
      95             :     /* -------------------------------------------------------------------- */
      96             :     /*      Fetch the result.                                               */
      97             :     /* -------------------------------------------------------------------- */
      98          23 :     CPLErrorReset();
      99          23 :     CPLHTTPResult *psResult = CPLHTTPFetch(pszFilename, nullptr);
     100             : 
     101             :     /* -------------------------------------------------------------------- */
     102             :     /*      Try to handle errors.                                           */
     103             :     /* -------------------------------------------------------------------- */
     104          45 :     if (psResult == nullptr || psResult->nDataLen == 0 ||
     105          22 :         CPLGetLastErrorNo() != 0)
     106             :     {
     107           5 :         CPLHTTPDestroyResult(psResult);
     108           5 :         return nullptr;
     109             :     }
     110             : 
     111             :     /* -------------------------------------------------------------------- */
     112             :     /*      Create a memory file from the result.                           */
     113             :     /* -------------------------------------------------------------------- */
     114             :     std::string osFilename =
     115          36 :         HTTPFetchContentDispositionFilename(psResult->papszHeaders);
     116          18 :     if (osFilename.empty())
     117             :     {
     118          16 :         osFilename = CPLGetFilename(pszFilename);
     119             :         /* If we have special characters, let's default to a fixed name */
     120          16 :         if (strchr(osFilename.c_str(), '?') || strchr(osFilename.c_str(), '&'))
     121           0 :             osFilename = "file.dat";
     122             :     }
     123             : 
     124             :     // If changing the _gdal_http_ marker, change jpgdataset.cpp that tests for it
     125             :     const CPLString osResultFilename = VSIMemGenerateHiddenFilename(
     126          54 :         std::string("_gdal_http_").append(osFilename).c_str());
     127             : 
     128          18 :     VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
     129          18 :                                         psResult->nDataLen, TRUE);
     130             : 
     131          18 :     if (fp == nullptr)
     132           0 :         return nullptr;
     133             : 
     134          18 :     VSIFCloseL(fp);
     135             : 
     136             :     /* -------------------------------------------------------------------- */
     137             :     /*      Steal the memory buffer from HTTP result before destroying      */
     138             :     /*      it.                                                             */
     139             :     /* -------------------------------------------------------------------- */
     140          18 :     psResult->pabyData = nullptr;
     141          18 :     psResult->nDataLen = 0;
     142          18 :     psResult->nDataAlloc = 0;
     143             : 
     144          18 :     CPLHTTPDestroyResult(psResult);
     145             : 
     146          36 :     CPLStringList aosOpenOptions;
     147           0 :     for (const char *pszStr :
     148          18 :          cpl::Iterate(const_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
     149             :     {
     150           0 :         if (STARTS_WITH_CI(pszStr, "NATIVE_DATA="))
     151             :         {
     152             :             // Avoid warning with "ogr2ogr out http://example.com/in.gpkg"
     153           0 :             aosOpenOptions.push_back(std::string("@").append(pszStr).c_str());
     154             :         }
     155             :         else
     156             :         {
     157           0 :             aosOpenOptions.push_back(pszStr);
     158             :         }
     159             :     }
     160             : 
     161             :     /* -------------------------------------------------------------------- */
     162             :     /*      Try opening this result as a gdaldataset.                       */
     163             :     /* -------------------------------------------------------------------- */
     164             : 
     165             :     /* suppress errors as not all drivers support /vsimem */
     166             : 
     167             :     GDALDataset *poDS;
     168          36 :     CPLErrorAccumulator oErrorAccumulator;
     169             :     {
     170          36 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     171          18 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
     172          18 :         CPL_IGNORE_RET_VAL(oAccumulator);
     173          18 :         poDS = GDALDataset::Open(
     174             :             osResultFilename,
     175          18 :             poOpenInfo->nOpenFlags & ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
     176          18 :             poOpenInfo->papszAllowedDrivers, aosOpenOptions.List(), nullptr);
     177             :     }
     178             : 
     179             :     // Re-emit silenced errors if open was successful
     180          18 :     if (poDS)
     181             :     {
     182           7 :         oErrorAccumulator.ReplayErrors();
     183             :     }
     184             : 
     185             :     // The JP2OpenJPEG, XLSX or ODS drivers may need to reopen the file, hence this special
     186             :     // behavior
     187          18 :     if (poDS)
     188             :     {
     189           7 :         auto poDriver = poDS->GetDriver();
     190          13 :         if (poDriver && (EQUAL(poDriver->GetDescription(), "JP2OpenJPEG") ||
     191           6 :                          EQUAL(poDriver->GetDescription(), "XLSX") ||
     192           6 :                          EQUAL(poDriver->GetDescription(), "ODS")))
     193             :         {
     194           1 :             poDS->MarkSuppressOnClose();
     195           1 :             return poDS;
     196             :         }
     197             :     }
     198             : 
     199             :     /* -------------------------------------------------------------------- */
     200             :     /*      If opening it in memory didn't work, perhaps we need to         */
     201             :     /*      write to a temp file on disk?                                   */
     202             :     /* -------------------------------------------------------------------- */
     203          17 :     if (poDS == nullptr)
     204             :     {
     205          22 :         CPLString osTempFilename;
     206             : 
     207             : #ifdef _WIN32
     208             :         const std::string osPath =
     209             :             CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
     210             : #else
     211          22 :         const std::string osPath = "/tmp";
     212             : #endif
     213          11 :         osTempFilename = CPLFormFilenameSafe(
     214          11 :             osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
     215          11 :         if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
     216             :         {
     217           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     218             :                      "Failed to create temporary file:%s",
     219             :                      osTempFilename.c_str());
     220             :         }
     221             :         else
     222             :         {
     223             :             poDS =
     224          11 :                 GDALDataset::Open(osTempFilename,
     225          11 :                                   poOpenInfo->nOpenFlags &
     226             :                                       ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
     227             :                                   poOpenInfo->papszAllowedDrivers,
     228          11 :                                   aosOpenOptions.List(), nullptr);
     229          11 :             if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
     230           0 :                 poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
     231             :                                                 windows */
     232          11 :             if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
     233           0 :                 poDS->SetDescription(poOpenInfo->pszFilename);
     234             :         }
     235             :     }
     236           6 :     else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
     237           6 :         poDS->SetDescription(poOpenInfo->pszFilename);
     238             : 
     239             :     /* -------------------------------------------------------------------- */
     240             :     /*      Release our hold on the vsi memory file, though if it is        */
     241             :     /*      held open by a dataset it will continue to exist till that      */
     242             :     /*      lets it go.                                                     */
     243             :     /* -------------------------------------------------------------------- */
     244          17 :     VSIUnlink(osResultFilename);
     245             : 
     246          17 :     return poDS;
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                         GDALRegister_HTTP()                          */
     251             : /************************************************************************/
     252             : 
     253        2066 : void GDALRegister_HTTP()
     254             : 
     255             : {
     256        2066 :     if (GDALGetDriverByName("HTTP") != nullptr)
     257         263 :         return;
     258             : 
     259        1803 :     GDALDriver *poDriver = new GDALDriver();
     260             : 
     261        1803 :     poDriver->SetDescription("HTTP");
     262        1803 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     263        1803 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     264        1803 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
     265             : 
     266        1803 :     poDriver->pfnOpen = HTTPOpen;
     267             : 
     268        1803 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     269             : }

Generated by: LCOV version 1.14