LCOV - code coverage report
Current view: top level - frmts/http - httpdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 83 93 89.2 %
Date: 2025-01-18 12:42:00 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             : 
      20           2 : static std::string SanitizeDispositionFilename(const std::string &osVal)
      21             : {
      22           4 :     std::string osRet(osVal);
      23           2 :     if (!osRet.empty() && osRet[0] == '"')
      24             :     {
      25           2 :         const auto nEnd = osRet.find('"', 1);
      26           2 :         if (nEnd != std::string::npos)
      27           2 :             return osRet.substr(1, nEnd - 1);
      28             :     }
      29           0 :     return osRet;
      30             : }
      31             : 
      32             : /************************************************************************/
      33             : /*               HTTPFetchContentDispositionFilename()                 */
      34             : /************************************************************************/
      35             : 
      36          15 : static std::string HTTPFetchContentDispositionFilename(char **papszHeaders)
      37             : {
      38          15 :     char **papszIter = papszHeaders;
      39         130 :     while (papszIter && *papszIter)
      40             :     {
      41             :         /* For multipart, we have in raw format, but without end-of-line
      42             :          * characters */
      43         117 :         if (STARTS_WITH(*papszIter,
      44             :                         "Content-Disposition: attachment; filename="))
      45             :         {
      46           0 :             return SanitizeDispositionFilename(*papszIter + 42);
      47             :         }
      48             :         /* For single part, the headers are in KEY=VAL format, but with e-o-l
      49             :          * ... */
      50         117 :         else if (STARTS_WITH(*papszIter,
      51             :                              "Content-Disposition=attachment; filename="))
      52             :         {
      53           2 :             char *pszVal = (*papszIter + 41);
      54           2 :             char *pszEOL = strchr(pszVal, '\r');
      55           2 :             if (pszEOL)
      56           0 :                 *pszEOL = 0;
      57           2 :             pszEOL = strchr(pszVal, '\n');
      58           2 :             if (pszEOL)
      59           0 :                 *pszEOL = 0;
      60           2 :             return SanitizeDispositionFilename(pszVal);
      61             :         }
      62         115 :         papszIter++;
      63             :     }
      64          13 :     return std::string();
      65             : }
      66             : 
      67             : /************************************************************************/
      68             : /*                              HTTPOpen()                              */
      69             : /************************************************************************/
      70             : 
      71       28866 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
      72             : 
      73             : {
      74       28866 :     if (poOpenInfo->nHeaderBytes != 0)
      75        1404 :         return nullptr;
      76             : 
      77       27462 :     if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") &&
      78       27427 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") &&
      79       27424 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:"))
      80       27432 :         return nullptr;
      81             : 
      82             :     /* -------------------------------------------------------------------- */
      83             :     /*      Fetch the result.                                               */
      84             :     /* -------------------------------------------------------------------- */
      85          30 :     CPLErrorReset();
      86          18 :     CPLHTTPResult *psResult = CPLHTTPFetch(poOpenInfo->pszFilename, nullptr);
      87             : 
      88             :     /* -------------------------------------------------------------------- */
      89             :     /*      Try to handle errors.                                           */
      90             :     /* -------------------------------------------------------------------- */
      91          35 :     if (psResult == nullptr || psResult->nDataLen == 0 ||
      92          17 :         CPLGetLastErrorNo() != 0)
      93             :     {
      94           3 :         CPLHTTPDestroyResult(psResult);
      95           3 :         return nullptr;
      96             :     }
      97             : 
      98             :     /* -------------------------------------------------------------------- */
      99             :     /*      Create a memory file from the result.                           */
     100             :     /* -------------------------------------------------------------------- */
     101             :     std::string osFilename =
     102          30 :         HTTPFetchContentDispositionFilename(psResult->papszHeaders);
     103          15 :     if (osFilename.empty())
     104             :     {
     105          13 :         osFilename = CPLGetFilename(poOpenInfo->pszFilename);
     106             :         /* If we have special characters, let's default to a fixed name */
     107          13 :         if (strchr(osFilename.c_str(), '?') || strchr(osFilename.c_str(), '&'))
     108           0 :             osFilename = "file.dat";
     109             :     }
     110             : 
     111             :     // If changing the _gdal_http_ marker, change jpgdataset.cpp that tests for it
     112             :     const CPLString osResultFilename = VSIMemGenerateHiddenFilename(
     113          45 :         std::string("_gdal_http_").append(osFilename).c_str());
     114             : 
     115          15 :     VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
     116          15 :                                         psResult->nDataLen, TRUE);
     117             : 
     118          15 :     if (fp == nullptr)
     119           0 :         return nullptr;
     120             : 
     121          15 :     VSIFCloseL(fp);
     122             : 
     123             :     /* -------------------------------------------------------------------- */
     124             :     /*      Steal the memory buffer from HTTP result before destroying      */
     125             :     /*      it.                                                             */
     126             :     /* -------------------------------------------------------------------- */
     127          15 :     psResult->pabyData = nullptr;
     128          15 :     psResult->nDataLen = 0;
     129          15 :     psResult->nDataAlloc = 0;
     130             : 
     131          15 :     CPLHTTPDestroyResult(psResult);
     132             : 
     133             :     /* -------------------------------------------------------------------- */
     134             :     /*      Try opening this result as a gdaldataset.                       */
     135             :     /* -------------------------------------------------------------------- */
     136             :     /* suppress errors as not all drivers support /vsimem */
     137             : 
     138             :     GDALDataset *poDS;
     139          30 :     std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
     140             :     {
     141          30 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     142          15 :         CPLInstallErrorHandlerAccumulator(aoErrors);
     143          15 :         poDS = GDALDataset::Open(osResultFilename,
     144          15 :                                  poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
     145             :                                  poOpenInfo->papszAllowedDrivers,
     146          15 :                                  poOpenInfo->papszOpenOptions, nullptr);
     147          15 :         CPLUninstallErrorHandlerAccumulator();
     148             :     }
     149             : 
     150             :     // Re-emit silenced errors if open was successful
     151          15 :     if (poDS)
     152             :     {
     153           5 :         for (const auto &oError : aoErrors)
     154             :         {
     155           0 :             CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
     156             :         }
     157             :     }
     158             : 
     159             :     // The JP2OpenJPEG driver may need to reopen the file, hence this special
     160             :     // behavior
     161          20 :     if (poDS != nullptr && poDS->GetDriver() != nullptr &&
     162           5 :         EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG"))
     163             :     {
     164           1 :         poDS->MarkSuppressOnClose();
     165           1 :         return poDS;
     166             :     }
     167             : 
     168             :     /* -------------------------------------------------------------------- */
     169             :     /*      If opening it in memory didn't work, perhaps we need to         */
     170             :     /*      write to a temp file on disk?                                   */
     171             :     /* -------------------------------------------------------------------- */
     172          14 :     if (poDS == nullptr)
     173             :     {
     174          20 :         CPLString osTempFilename;
     175             : 
     176             : #ifdef _WIN32
     177             :         const std::string osPath =
     178             :             CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
     179             : #else
     180          20 :         const std::string osPath = "/tmp";
     181             : #endif
     182          10 :         osTempFilename = CPLFormFilenameSafe(
     183          10 :             osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
     184          10 :         if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
     185             :         {
     186           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     187             :                      "Failed to create temporary file:%s",
     188             :                      osTempFilename.c_str());
     189             :         }
     190             :         else
     191             :         {
     192          10 :             poDS = GDALDataset::Open(osTempFilename,
     193          10 :                                      poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
     194             :                                      poOpenInfo->papszAllowedDrivers,
     195          10 :                                      poOpenInfo->papszOpenOptions, nullptr);
     196          10 :             if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
     197           0 :                 poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
     198             :                                                 windows */
     199          10 :             if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
     200           0 :                 poDS->SetDescription(poOpenInfo->pszFilename);
     201             :         }
     202             :     }
     203           4 :     else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
     204           4 :         poDS->SetDescription(poOpenInfo->pszFilename);
     205             : 
     206             :     /* -------------------------------------------------------------------- */
     207             :     /*      Release our hold on the vsi memory file, though if it is        */
     208             :     /*      held open by a dataset it will continue to exist till that      */
     209             :     /*      lets it go.                                                     */
     210             :     /* -------------------------------------------------------------------- */
     211          14 :     VSIUnlink(osResultFilename);
     212             : 
     213          14 :     return poDS;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                         GDALRegister_HTTP()                          */
     218             : /************************************************************************/
     219             : 
     220        1682 : void GDALRegister_HTTP()
     221             : 
     222             : {
     223        1682 :     if (GDALGetDriverByName("HTTP") != nullptr)
     224         301 :         return;
     225             : 
     226        1381 :     GDALDriver *poDriver = new GDALDriver();
     227             : 
     228        1381 :     poDriver->SetDescription("HTTP");
     229        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     230        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     231        1381 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
     232             : 
     233        1381 :     poDriver->pfnOpen = HTTPOpen;
     234             : 
     235        1381 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     236             : }

Generated by: LCOV version 1.14