LCOV - code coverage report
Current view: directory - frmts/wms - gdalhttp.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 152 125 82.2 %
Date: 2011-12-18 Functions: 4 4 100.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: gdalhttp.cpp 22007 2011-03-21 20:44:14Z rouault $
       3                 :  *
       4                 :  * Project:  WMS Client Driver
       5                 :  * Purpose:  Implementation of Dataset and RasterBand classes for WMS
       6                 :  *           and other similar services.
       7                 :  * Author:   Adam Nowacki, nowak@xpam.de
       8                 :  *
       9                 :  ******************************************************************************
      10                 :  * Copyright (c) 2007, Adam Nowacki
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions: 
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28                 :  * DEALINGS IN THE SOFTWARE.
      29                 :  ****************************************************************************/
      30                 : 
      31                 : #include "stdinc.h"
      32                 : 
      33                 : /* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */
      34                 : #if LIBCURL_VERSION_NUM < 0x070a07
      35                 : #define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
      36                 : #endif
      37                 : 
      38              64 : static size_t CPLHTTPWriteFunc(void *buffer, size_t count, size_t nmemb, void *req) {
      39              64 :     CPLHTTPRequest *psRequest = reinterpret_cast<CPLHTTPRequest *>(req);
      40              64 :     size_t size = count * nmemb;
      41                 : 
      42              64 :     if (size == 0) return 0;
      43                 : 
      44              64 :     const size_t required_size = psRequest->nDataLen + size + 1;
      45              64 :     if (required_size > psRequest->nDataAlloc) {
      46              13 :         size_t new_size = required_size * 2;
      47              13 :         if (new_size < 512) new_size = 512;
      48              13 :         psRequest->nDataAlloc = new_size;
      49              13 :         GByte * pabyNewData = reinterpret_cast<GByte *>(VSIRealloc(psRequest->pabyData, new_size));
      50              13 :         if (pabyNewData == NULL) {
      51               0 :             VSIFree(psRequest->pabyData);
      52               0 :             psRequest->pabyData = NULL;
      53               0 :             psRequest->pszError = CPLStrdup(CPLString().Printf("Out of memory allocating %u bytes for HTTP data buffer.", static_cast<int>(new_size)));
      54               0 :             psRequest->nDataAlloc = 0;
      55               0 :             psRequest->nDataLen = 0;
      56               0 :             return 0;
      57                 :         }
      58              13 :         psRequest->pabyData = pabyNewData;
      59                 :     }
      60              64 :     memcpy(psRequest->pabyData + psRequest->nDataLen, buffer, size);
      61              64 :     psRequest->nDataLen += size;
      62              64 :     psRequest->pabyData[psRequest->nDataLen] = 0;
      63              64 :     return nmemb;
      64                 : }
      65                 : 
      66               3 : void CPLHTTPInitializeRequest(CPLHTTPRequest *psRequest, const char *pszURL, const char *const *papszOptions) {
      67               3 :     psRequest->pszURL = CPLStrdup(pszURL);
      68               3 :     psRequest->papszOptions = CSLDuplicate(const_cast<char **>(papszOptions));
      69               3 :     psRequest->nStatus = 0;
      70               3 :     psRequest->pszContentType = 0;
      71               3 :     psRequest->pszError = 0;
      72               3 :     psRequest->pabyData = 0;
      73               3 :     psRequest->nDataLen = 0;
      74               3 :     psRequest->nDataAlloc = 0;
      75               3 :     psRequest->m_curl_handle = 0;
      76               3 :     psRequest->m_headers = 0;
      77               3 :     psRequest->m_curl_error = 0;
      78                 : 
      79               3 :     psRequest->m_curl_handle = curl_easy_init();
      80               3 :     if (psRequest->m_curl_handle == NULL) {
      81               0 :         CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPInitializeRequest(): Unable to create CURL handle.");
      82                 :     }
      83                 : 
      84                 :     /* Set User-Agent */
      85               3 :     const char *pszUserAgent = CSLFetchNameValue(const_cast<char **>(psRequest->papszOptions), "USERAGENT");
      86               3 :     if (pszUserAgent == NULL)
      87               3 :         pszUserAgent = "GDAL WMS driver (http://www.gdal.org/frmt_wms.html)";
      88               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_USERAGENT, pszUserAgent);
      89                 : 
      90                 :     /* Set Referer */
      91               3 :     const char *pszReferer = CSLFetchNameValue(const_cast<char **>(psRequest->papszOptions), "REFERER");
      92               3 :     if (pszReferer != NULL)
      93               0 :         curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_REFERER, pszReferer);
      94                 : 
      95                 :     /* Set URL */
      96               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_URL, psRequest->pszURL);
      97                 : 
      98                 :     /* Set timeout.*/
      99               3 :     const char *timeout = CSLFetchNameValue(const_cast<char **>(psRequest->papszOptions), "TIMEOUT");
     100               3 :     if (timeout != NULL) {
     101               3 :         curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_TIMEOUT, atoi(timeout));
     102                 :     }
     103                 : 
     104                 :     /* Set Headers (copied&pasted from cpl_http.cpp, but unused by callers of CPLHTTPInitializeRequest) .*/
     105               3 :     const char *headers = CSLFetchNameValue(const_cast<char **>(psRequest->papszOptions), "HEADERS");
     106               3 :     if (headers != NULL) {
     107               0 :         psRequest->m_headers = curl_slist_append(psRequest->m_headers, headers);
     108               0 :         curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_HTTPHEADER, psRequest->m_headers);
     109                 :     }
     110                 :     
     111               3 :     if (CSLFetchBoolean(const_cast<char **>(psRequest->papszOptions), "UNSAFESSL", 0)) {
     112               0 :         curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
     113               0 :         curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
     114                 :     }
     115                 : 
     116                 :     /* Enable following redirections.  Requires libcurl 7.10.1 at least */
     117               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_FOLLOWLOCATION, 1);
     118               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_MAXREDIRS, 10);
     119                 : 
     120                 :     /* NOSIGNAL should be set to true for timeout to work in multithread
     121                 :     environments on Unix, requires libcurl 7.10 or more recent.
     122                 :     (this force avoiding the use of sgnal handlers) */
     123                 : #ifdef CURLOPT_NOSIGNAL
     124                 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_NOSIGNAL, 1);
     125                 : #endif
     126                 : 
     127               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEDATA, psRequest);
     128               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEFUNCTION, CPLHTTPWriteFunc);
     129                 : 
     130               3 :     psRequest->m_curl_error = reinterpret_cast<char *>(CPLMalloc(CURL_ERROR_SIZE + 1));
     131               3 :     psRequest->m_curl_error[0] = '\0';
     132               3 :     curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_ERRORBUFFER, psRequest->m_curl_error);
     133                 :     
     134                 :     /* Set Proxy parameters */
     135               3 :     const char* pszProxy = CSLFetchNameValue( const_cast<char **>(psRequest->papszOptions), "PROXY" );
     136               3 :     if (pszProxy == NULL)
     137               3 :         pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", NULL);
     138               3 :     if (pszProxy)
     139               0 :         curl_easy_setopt(psRequest->m_curl_handle,CURLOPT_PROXY,pszProxy);
     140                 : 
     141               3 :     const char* pszProxyUserPwd = CSLFetchNameValue( const_cast<char **>(psRequest->papszOptions), "PROXYUSERPWD" );
     142               3 :     if (pszProxyUserPwd == NULL)
     143               3 :         pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", NULL);
     144               3 :     if (pszProxyUserPwd)
     145               0 :         curl_easy_setopt(psRequest->m_curl_handle,CURLOPT_PROXYUSERPWD,pszProxyUserPwd);
     146               3 : }
     147                 : 
     148               3 : void CPLHTTPCleanupRequest(CPLHTTPRequest *psRequest) {
     149               3 :     if (psRequest->m_curl_handle) {
     150               3 :         curl_easy_cleanup(psRequest->m_curl_handle);
     151               3 :         psRequest->m_curl_handle = 0;
     152                 :     }
     153               3 :     if (psRequest->m_headers) {
     154               0 :         curl_slist_free_all(psRequest->m_headers);
     155               0 :         psRequest->m_headers = 0;
     156                 :     }
     157               3 :     if (psRequest->m_curl_error) {
     158               3 :         CPLFree(psRequest->m_curl_error);
     159               3 :         psRequest->m_curl_error = 0;
     160                 :     }
     161                 : 
     162               3 :     if (psRequest->pszContentType) {
     163               3 :         CPLFree(psRequest->pszContentType);
     164               3 :         psRequest->pszContentType = 0;
     165                 :     }
     166               3 :     if (psRequest->pszError) {
     167               0 :         CPLFree(psRequest->pszError);
     168               0 :         psRequest->pszError = 0;
     169                 :     }
     170               3 :     if (psRequest->pabyData) {
     171               3 :         CPLFree(psRequest->pabyData);
     172               3 :         psRequest->pabyData = 0;
     173               3 :         psRequest->nDataLen = 0;
     174               3 :         psRequest->nDataAlloc = 0;
     175                 :     }
     176               3 :     if (psRequest->papszOptions) {
     177               3 :         CSLDestroy(psRequest->papszOptions);
     178               3 :         psRequest->papszOptions = 0;
     179                 :     }
     180               3 :     if (psRequest->pszURL) {
     181               3 :         CPLFree(psRequest->pszURL);
     182               3 :         psRequest->pszURL = 0;
     183                 :     }
     184               3 : }
     185                 : 
     186               2 : CPLErr CPLHTTPFetchMulti(CPLHTTPRequest *pasRequest, int nRequestCount, const char *const *papszOptions) {
     187               2 :     CPLErr ret = CE_None;
     188               2 :     CURLM *curl_multi = 0;
     189                 :     int still_running;
     190                 :     int max_conn;
     191                 :     int i, conn_i;
     192                 : 
     193               2 :     const char *max_conn_opt = CSLFetchNameValue(const_cast<char **>(papszOptions), "MAXCONN");
     194               4 :     if (max_conn_opt && (max_conn_opt[0] != '\0')) {
     195               2 :         max_conn = MAX(1, MIN(atoi(max_conn_opt), 1000));
     196                 :     } else {
     197               0 :         max_conn = 5;
     198                 :     }
     199                 : 
     200               2 :     curl_multi = curl_multi_init();
     201               2 :     if (curl_multi == NULL) {
     202               0 :         CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): Unable to create CURL multi-handle.");
     203                 :     }
     204                 : 
     205                 :     // add at most max_conn requests
     206               5 :     for (conn_i = 0; conn_i < MIN(nRequestCount, max_conn); ++conn_i) {
     207               3 :         CPLHTTPRequest *const psRequest = &pasRequest[conn_i];
     208               3 :         CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL);
     209               3 :         curl_multi_add_handle(curl_multi, psRequest->m_curl_handle);
     210                 :     }
     211                 : 
     212               6 :     while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM);
     213              87 :     while (still_running || (conn_i != nRequestCount)) {
     214                 :         struct timeval timeout;
     215                 :         fd_set fdread, fdwrite, fdexcep;
     216                 :         int maxfd;
     217                 :         CURLMsg *msg;
     218                 :         int msgs_in_queue;
     219                 : 
     220              84 :         do {
     221              84 :             msg = curl_multi_info_read(curl_multi, &msgs_in_queue);
     222              84 :             if (msg != NULL) {
     223               1 :                 if (msg->msg == CURLMSG_DONE) { // transfer completed, check if we have more waiting and add them
     224               1 :                     if (conn_i < nRequestCount) {
     225               0 :                         CPLHTTPRequest *const psRequest = &pasRequest[conn_i];
     226               0 :                         CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL);
     227               0 :                         curl_multi_add_handle(curl_multi, psRequest->m_curl_handle);
     228               0 :                         ++conn_i;
     229                 :                     }
     230                 :                 }
     231                 :             }
     232                 :         } while (msg != NULL);
     233              83 :         FD_ZERO(&fdread);
     234              83 :         FD_ZERO(&fdwrite);
     235              83 :         FD_ZERO(&fdexcep);
     236              83 :         curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd);
     237              83 :         timeout.tv_sec = 0;
     238              83 :         timeout.tv_usec = 100000;
     239              83 :         select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
     240              98 :         while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM);
     241                 :     }
     242                 : 
     243               2 :     if (conn_i != nRequestCount) { // something gone really really wrong
     244               0 :         CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): conn_i != nRequestCount, this should never happen ...");
     245                 :     }
     246               5 :     for (i = 0; i < nRequestCount; ++i) {
     247               3 :         CPLHTTPRequest *const psRequest = &pasRequest[i];
     248                 : 
     249               3 :         long response_code = 0;
     250               3 :         curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_RESPONSE_CODE, &response_code);
     251               3 :         psRequest->nStatus = response_code;
     252                 : 
     253               3 :         char *content_type = 0;
     254               3 :         curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_CONTENT_TYPE, &content_type);
     255               3 :         if (content_type) psRequest->pszContentType = CPLStrdup(content_type);
     256                 : 
     257               3 :         if ((psRequest->pszError == NULL) && (psRequest->m_curl_error != NULL) && (psRequest->m_curl_error[0] != '\0')) {
     258               0 :             psRequest->pszError = CPLStrdup(psRequest->m_curl_error);
     259                 :         }
     260                 : 
     261                 :         /* In the case of a file:// URL, curl will return a status == 0, so if there's no */
     262                 :         /* error returned, patch the status code to be 200, as it would be for http:// */
     263               3 :         if (strncmp(psRequest->pszURL, "file://", 7) == 0 && psRequest->nStatus == 0 &&
     264                 :             psRequest->pszError == NULL)
     265                 :         {
     266               0 :             psRequest->nStatus = 200;
     267                 :         }
     268                 : 
     269                 :         CPLDebug("HTTP", "Request [%d] %s : status = %d, content type = %s, error = %s",
     270                 :                  i, psRequest->pszURL, psRequest->nStatus,
     271                 :                  (psRequest->pszContentType) ? psRequest->pszContentType : "(null)",
     272               3 :                  (psRequest->pszError) ? psRequest->pszError : "(null)");
     273                 : 
     274               3 :         curl_multi_remove_handle(curl_multi, pasRequest[i].m_curl_handle);
     275                 :     }
     276               2 :     curl_multi_cleanup(curl_multi);
     277                 : 
     278               2 :     return ret;
     279                 : }

Generated by: LCOV version 1.7