~ubuntu-branches/debian/sid/gdal/sid

« back to all changes in this revision

Viewing changes to port/cpl_http.cpp

  • Committer: Package Import Robot
  • Author(s): Francesco Paolo Lovergine
  • Date: 2012-05-07 15:04:42 UTC
  • mfrom: (5.5.16 experimental)
  • Revision ID: package-import@ubuntu.com-20120507150442-2eks97loeh6rq005
Tags: 1.9.0-1
* Ready for sid, starting transition.
* All symfiles updated to latest builds.
* Added dh_numpy call in debian/rules to depend on numpy ABI.
* Policy bumped to 3.9.3, no changes required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/******************************************************************************
2
 
 * $Id: cpl_http.cpp 17927 2009-10-30 18:37:12Z rouault $
 
2
 * $Id: cpl_http.cpp 22588 2011-06-25 20:11:07Z rouault $
3
3
 *
4
 
 * Project:  WCS Client Driver
5
 
 * Purpose:  Implementation of Dataset and RasterBand classes for WCS.
 
4
 * Project:  libcurl based HTTP client
 
5
 * Purpose:  libcurl based HTTP client
6
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
7
 *
8
8
 ******************************************************************************
27
27
 * DEALINGS IN THE SOFTWARE.
28
28
 ****************************************************************************/
29
29
 
30
 
#include "cpl_conv.h"
 
30
#include <map>
31
31
#include "cpl_http.h"
 
32
#include "cpl_multiproc.h"
32
33
 
33
34
#ifdef HAVE_CURL
34
35
#  include <curl/curl.h>
35
 
#endif
36
 
 
37
 
CPL_CVSID("$Id: cpl_http.cpp 17927 2009-10-30 18:37:12Z rouault $");
 
36
 
 
37
 
 
38
/* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */
 
39
#if LIBCURL_VERSION_NUM < 0x070a07
 
40
#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
 
41
#endif
 
42
 
 
43
#endif
 
44
 
 
45
CPL_CVSID("$Id: cpl_http.cpp 22588 2011-06-25 20:11:07Z rouault $");
 
46
 
 
47
// list of named persistent http sessions 
 
48
 
 
49
#ifdef HAVE_CURL
 
50
static std::map<CPLString,CURL*> oSessionMap;
 
51
static void *hSessionMapMutex = NULL;
 
52
#endif
38
53
 
39
54
/************************************************************************/
40
55
/*                            CPLWriteFct()                             */
77
92
 
78
93
    return nmemb;
79
94
}
 
95
 
 
96
/************************************************************************/
 
97
/*                           CPLHdrWriteFct()                           */
 
98
/************************************************************************/
 
99
static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
 
100
{
 
101
    CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
 
102
    // copy the buffer to a char* and initialize with zeros (zero terminate as well)
 
103
    char* pszHdr = (char*)CPLCalloc(nmemb + 1, size);
 
104
    CPLPrintString(pszHdr, (char *)buffer, nmemb * size);
 
105
    char *pszKey = NULL;
 
106
    const char *pszValue = CPLParseNameValue(pszHdr, &pszKey );
 
107
    psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue);
 
108
    CPLFree(pszHdr);
 
109
    CPLFree(pszKey);
 
110
    return nmemb; 
 
111
}
 
112
 
80
113
#endif /* def HAVE_CURL */
81
114
 
82
115
/************************************************************************/
93
126
 * <li>TIMEOUT=val, where val is in seconds</li>
94
127
 * <li>HEADERS=val, where val is an extra header to use when getting a web page.
95
128
 *                  For example "Accept: application/x-ogcwkt"
96
 
 * <li>HTTPAUTH=[BASIC/NTLM/ANY] to specify an authentication scheme to use.
 
129
 * <li>HTTPAUTH=[BASIC/NTLM/GSSNEGOTIATE/ANY] to specify an authentication scheme to use.
97
130
 * <li>USERPWD=userid:password to specify a user and password for authentication
 
131
 * <li>POSTFIELDS=val, where val is a nul-terminated string to be passed to the server
 
132
 *                     with a POST request.
 
133
 * <li>PROXY=val, to make requests go through a proxy server, where val is of the
 
134
 *                form proxy.server.com:port_number
 
135
 * <li>PROXYUSERPWD=val, where val is of the form username:password
 
136
 * <li>CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc.. (GDAL >= 1.9.0)
98
137
 * </ul>
99
138
 *
100
 
 * @return a CPLHTTPResult* structure that must be freed by CPLHTTPDestroyResult(),
101
 
 *         or NULL if libcurl support is diabled
 
139
 * Alternatively, if not defined in the papszOptions arguments, the PROXY and 
 
140
 * PROXYUSERPWD values are searched in the configuration options named 
 
141
 * GDAL_HTTP_PROXY and GDAL_HTTP_PROXYUSERPWD, as proxy configuration belongs 
 
142
 * to networking setup and makes more sense at the configuration option level 
 
143
 * than at the connection level.
 
144
 *
 
145
 * @return a CPLHTTPResult* structure that must be freed by 
 
146
 * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled
102
147
 */
103
148
CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions )
104
149
 
105
150
{
106
151
#ifndef HAVE_CURL
 
152
    (void) papszOptions;
 
153
    (void) pszURL;
 
154
 
107
155
    CPLError( CE_Failure, CPLE_NotSupported,
108
156
              "GDAL/OGR not compiled with libcurl support, remote requests not supported." );
109
157
    return NULL;
110
158
#else
111
 
    CURL *http_handle;
 
159
/* -------------------------------------------------------------------- */
 
160
/*      Are we using a persistent named session?  If so, search for     */
 
161
/*      or create it.                                                   */
 
162
/*                                                                      */
 
163
/*      Currently this code does not attempt to protect against         */
 
164
/*      multiple threads asking for the same named session.  If that    */
 
165
/*      occurs it will be in use in multiple threads at once which      */
 
166
/*      might have bad consequences depending on what guarantees        */
 
167
/*      libcurl gives - which I have not investigated.                  */
 
168
/* -------------------------------------------------------------------- */
 
169
    CURL *http_handle = NULL;
 
170
 
 
171
    const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" );
 
172
    const char *pszClosePersistent = CSLFetchNameValue( papszOptions, "CLOSE_PERSISTENT" );
 
173
    if (pszPersistent)
 
174
    {
 
175
        CPLString osSessionName = pszPersistent;
 
176
        CPLMutexHolder oHolder( &hSessionMapMutex );
 
177
 
 
178
        if( oSessionMap.count( osSessionName ) == 0 )
 
179
        {
 
180
            oSessionMap[osSessionName] = curl_easy_init();
 
181
            CPLDebug( "HTTP", "Establish persistent session named '%s'.",
 
182
                      osSessionName.c_str() );
 
183
        }
 
184
 
 
185
        http_handle = oSessionMap[osSessionName];
 
186
    }
 
187
/* -------------------------------------------------------------------- */
 
188
/*      Are we requested to close a persistent named session?          */
 
189
/* -------------------------------------------------------------------- */
 
190
    else if (pszClosePersistent)
 
191
    {
 
192
        CPLString osSessionName = pszClosePersistent;
 
193
        CPLMutexHolder oHolder( &hSessionMapMutex );
 
194
 
 
195
        std::map<CPLString,CURL*>::iterator oIter = oSessionMap.find( osSessionName );
 
196
        if( oIter != oSessionMap.end() )
 
197
        {
 
198
            curl_easy_cleanup(oIter->second);
 
199
            oSessionMap.erase(oIter);
 
200
            CPLDebug( "HTTP", "Ended persistent session named '%s'.",
 
201
                      osSessionName.c_str() );
 
202
        }
 
203
        else
 
204
        {
 
205
            CPLDebug( "HTTP", "Could not find persistent session named '%s'.",
 
206
                      osSessionName.c_str() );
 
207
        }
 
208
 
 
209
        return NULL;
 
210
    }
 
211
    else
 
212
        http_handle = curl_easy_init();
 
213
 
 
214
/* -------------------------------------------------------------------- */
 
215
/*      Setup the request.                                              */
 
216
/* -------------------------------------------------------------------- */
112
217
    char szCurlErrBuf[CURL_ERROR_SIZE+1];
113
218
    CPLHTTPResult *psResult;
114
219
    struct curl_slist *headers=NULL; 
115
220
 
116
 
 
117
 
    CPLDebug( "HTTP", "Fetch(%s)", pszURL );
 
221
    const char* pszArobase = strchr(pszURL, '@');
 
222
    const char* pszSlash = strchr(pszURL, '/');
 
223
    const char* pszColon = (pszSlash) ? strchr(pszSlash, ':') : NULL;
 
224
    if (pszArobase != NULL && pszColon != NULL && pszArobase - pszColon > 0)
 
225
    {
 
226
        /* http://user:password@www.example.com */
 
227
        char* pszSanitizedURL = CPLStrdup(pszURL);
 
228
        pszSanitizedURL[pszColon-pszURL] = 0;
 
229
        CPLDebug( "HTTP", "Fetch(%s:#password#%s)", pszSanitizedURL, pszArobase );
 
230
        CPLFree(pszSanitizedURL);
 
231
    }
 
232
    else
 
233
    {
 
234
        CPLDebug( "HTTP", "Fetch(%s)", pszURL );
 
235
    }
118
236
 
119
237
    psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult));
120
238
 
121
 
    http_handle = curl_easy_init();
122
 
 
123
239
    curl_easy_setopt(http_handle, CURLOPT_URL, pszURL );
124
240
 
 
241
    if (CSLTestBoolean(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO")))
 
242
        curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1);
 
243
 
 
244
    const char *pszHttpVersion = CSLFetchNameValue( papszOptions, "HTTP_VERSION");
 
245
    if( pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0 )
 
246
        curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
 
247
 
125
248
    /* Support control over HTTPAUTH */
126
249
    const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" );
127
250
    if( pszHttpAuth == NULL )
135
258
        curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );
136
259
    else if( EQUAL(pszHttpAuth,"ANY") )
137
260
        curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
 
261
#ifdef CURLAUTH_GSSNEGOTIATE
 
262
    else if( EQUAL(pszHttpAuth,"NEGOTIATE") )
 
263
        curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE );
 
264
#endif
138
265
    else
139
266
    {
140
267
        CPLError( CE_Warning, CPLE_AppDefined,
154
281
    if( pszUserPwd != NULL )
155
282
        curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd );
156
283
 
 
284
    /* Set Proxy parameters */
 
285
    const char* pszProxy = CSLFetchNameValue( papszOptions, "PROXY" );
 
286
    if (pszProxy == NULL)
 
287
        pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", NULL);
 
288
    if (pszProxy)
 
289
        curl_easy_setopt(http_handle,CURLOPT_PROXY,pszProxy);
 
290
 
 
291
    const char* pszProxyUserPwd = CSLFetchNameValue( papszOptions, "PROXYUSERPWD" );
 
292
    if (pszProxyUserPwd == NULL)
 
293
        pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", NULL);
 
294
    if (pszProxyUserPwd)
 
295
        curl_easy_setopt(http_handle,CURLOPT_PROXYUSERPWD,pszProxyUserPwd);
 
296
 
 
297
    // turn off SSL verification, accept all servers with ssl
 
298
    curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
 
299
 
 
300
    /* Set POST mode */
 
301
    const char* pszPost = CSLFetchNameValue( papszOptions, "POSTFIELDS" );
 
302
    if( pszPost != NULL )
 
303
    {
 
304
        curl_easy_setopt(http_handle, CURLOPT_POST, 1 );
 
305
        curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS, pszPost );
 
306
    }
 
307
 
 
308
    const char* pszCustomRequest = CSLFetchNameValue( papszOptions, "CUSTOMREQUEST" );
 
309
    if( pszCustomRequest != NULL )
 
310
    {
 
311
        curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST, pszCustomRequest );
 
312
    }
 
313
 
157
314
    /* Enable following redirections.  Requires libcurl 7.10.1 at least */
158
315
    curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 );
159
316
    curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 );
171
328
        headers = curl_slist_append(headers, pszHeaders);
172
329
        curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
173
330
    }
174
 
                         
 
331
 
175
332
    /* NOSIGNAL should be set to true for timeout to work in multithread
176
333
     * environments on Unix, requires libcurl 7.10 or more recent.
177
334
     * (this force avoiding the use of sgnal handlers)
180
337
    curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 );
181
338
#endif
182
339
 
 
340
    // capture response headers
 
341
    curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
 
342
    curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct);
 
343
 
183
344
    curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult );
184
345
    curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct );
185
346
 
187
348
 
188
349
    curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf );
189
350
 
 
351
    static int bHasCheckVersion = FALSE;
 
352
    static int bSupportGZip = FALSE;
 
353
    if (!bHasCheckVersion)
 
354
    {
 
355
        bSupportGZip = strstr(curl_version(), "zlib/") != NULL;
 
356
        bHasCheckVersion = TRUE;
 
357
    }
 
358
    int bGZipRequested = FALSE;
 
359
    if (bSupportGZip && CSLTestBoolean(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
 
360
    {
 
361
        bGZipRequested = TRUE;
 
362
        curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
 
363
    }
 
364
 
 
365
/* -------------------------------------------------------------------- */
 
366
/*      Execute the request, waiting for results.                       */
 
367
/* -------------------------------------------------------------------- */
190
368
    psResult->nStatus = (int) curl_easy_perform( http_handle );
191
369
 
192
370
/* -------------------------------------------------------------------- */
205
383
/* -------------------------------------------------------------------- */
206
384
    if( strlen(szCurlErrBuf) > 0 )
207
385
    {
208
 
        psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
209
 
        CPLError( CE_Failure, CPLE_AppDefined, 
210
 
                  "%s", szCurlErrBuf );
211
 
    }
212
 
 
213
 
    curl_easy_cleanup( http_handle );
 
386
        int bSkipError = FALSE;
 
387
 
 
388
        /* Some servers such as http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities */
 
389
        /* invalidly return Content-Length as the uncompressed size, with makes curl to wait for more data */
 
390
        /* and time-out finally. If we got the expected data size, then we don't emit an error */
 
391
        /* but turn off GZip requests */
 
392
        if (bGZipRequested &&
 
393
            strstr(szCurlErrBuf, "transfer closed with") &&
 
394
            strstr(szCurlErrBuf, "bytes remaining to read"))
 
395
        {
 
396
            const char* pszContentLength =
 
397
                CSLFetchNameValue(psResult->papszHeaders, "Content-Length");
 
398
            if (pszContentLength && psResult->nDataLen != 0 &&
 
399
                atoi(pszContentLength) == psResult->nDataLen)
 
400
            {
 
401
                const char* pszCurlGZIPOption = CPLGetConfigOption("CPL_CURL_GZIP", NULL);
 
402
                if (pszCurlGZIPOption == NULL)
 
403
                {
 
404
                    CPLSetConfigOption("CPL_CURL_GZIP", "NO");
 
405
                    CPLDebug("HTTP", "Disabling CPL_CURL_GZIP, because %s doesn't support it properly",
 
406
                             pszURL);
 
407
                }
 
408
                psResult->nStatus = 0;
 
409
                bSkipError = TRUE;
 
410
            }
 
411
        }
 
412
        if (!bSkipError)
 
413
        {
 
414
            psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
 
415
            CPLError( CE_Failure, CPLE_AppDefined,
 
416
                    "%s", szCurlErrBuf );
 
417
        }
 
418
    }
 
419
    else
 
420
    {
 
421
        /* HTTP errors do not trigger curl errors. But we need to */
 
422
        /* propagate them to the caller though */
 
423
        long response_code = 0;
 
424
        curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code);
 
425
        if (response_code >= 400 && response_code < 600)
 
426
        {
 
427
            psResult->pszErrBuf = CPLStrdup(CPLSPrintf("HTTP error code : %d", (int)response_code));
 
428
            CPLError( CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf );
 
429
        }
 
430
    }
 
431
 
 
432
    if (!pszPersistent)
 
433
        curl_easy_cleanup( http_handle );
 
434
 
214
435
    curl_slist_free_all(headers);
215
436
 
216
437
    return psResult;
248
469
void CPLHTTPCleanup()
249
470
 
250
471
{
251
 
    /* nothing for now, but if we use the more complicated api later, 
252
 
       we will need to do cleanup, like mapserver maphttp.c does. */
 
472
#ifdef HAVE_CURL
 
473
    if( !hSessionMapMutex )
 
474
        return;
 
475
 
 
476
    {
 
477
        CPLMutexHolder oHolder( &hSessionMapMutex );
 
478
        std::map<CPLString,CURL*>::iterator oIt;
 
479
 
 
480
        for( oIt=oSessionMap.begin(); oIt != oSessionMap.end(); oIt++ )
 
481
            curl_easy_cleanup( oIt->second );
 
482
 
 
483
        oSessionMap.clear();
 
484
    }
 
485
 
 
486
    // not quite a safe sequence. 
 
487
    CPLDestroyMutex( hSessionMapMutex );
 
488
    hSessionMapMutex = NULL;
 
489
#endif
253
490
}
254
491
 
255
492
/************************************************************************/
269
506
        CPLFree( psResult->pabyData );
270
507
        CPLFree( psResult->pszErrBuf );
271
508
        CPLFree( psResult->pszContentType );
 
509
        CSLDestroy( psResult->papszHeaders );
 
510
 
 
511
        int i;
 
512
        for(i=0;i<psResult->nMimePartCount;i++)
 
513
        {
 
514
            CSLDestroy( psResult->pasMimePart[i].papszHeaders );
 
515
        }
 
516
        CPLFree(psResult->pasMimePart);
 
517
        
272
518
        CPLFree( psResult );
273
519
    }
274
520
}
319
565
    {
320
566
        CPLError( CE_Failure, CPLE_AppDefined,
321
567
                  "Unable to parse multi-part mime, boundary not parsable." );
 
568
        CSLDestroy( papszTokens );
322
569
        return FALSE;
323
570
    }
324
571
    
340
587
    }
341
588
 
342
589
    pszNext += strlen(osBoundary);
343
 
    while( *pszNext != '\n' && *pszNext != '\0' )
 
590
    while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' )
 
591
        pszNext++;
 
592
    if( *pszNext == '\r' )
344
593
        pszNext++;
345
594
    if( *pszNext == '\n' )
346
595
        pszNext++;
362
611
/* -------------------------------------------------------------------- */
363
612
/*      Collect headers.                                                */
364
613
/* -------------------------------------------------------------------- */
365
 
        while( *pszNext != '\n' && *pszNext != '\0' )
 
614
        while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' )
366
615
        {
367
616
            char *pszEOL = strstr(pszNext,"\n");
368
617
 
369
618
            if( pszEOL == NULL )
370
619
            {
371
 
                CPLAssert( FALSE );
372
 
                break;
 
620
                CPLError(CE_Failure, CPLE_AppDefined,
 
621
                         "Error while parsing multipart content (at line %d)", __LINE__);
 
622
                return FALSE;
373
623
            }
374
624
 
375
625
            *pszEOL = '\0';
 
626
            int bRestoreAntislashR = FALSE;
 
627
            if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r')
 
628
            {
 
629
                bRestoreAntislashR = TRUE;
 
630
                pszEOL[-1] = '\0';
 
631
            }
376
632
            psPart->papszHeaders = 
377
633
                CSLAddString( psPart->papszHeaders, pszNext );
 
634
            if (bRestoreAntislashR)
 
635
                pszEOL[-1] = '\r';
378
636
            *pszEOL = '\n';
379
637
            
380
638
            pszNext = pszEOL + 1;
381
639
        }
382
640
 
 
641
        if( *pszNext == '\r' )
 
642
            pszNext++;
383
643
        if( *pszNext == '\n' )
384
644
            pszNext++;
385
645
            
401
661
        
402
662
        if( nBytesAvail == 0 )
403
663
        {
404
 
            CPLAssert( FALSE );
405
 
            break;
 
664
            CPLError(CE_Failure, CPLE_AppDefined,
 
665
                        "Error while parsing multipart content (at line %d)", __LINE__);
 
666
            return FALSE;
406
667
        }
407
668
 
408
669
        psPart->nDataLen = pszNext - (const char *) psPart->pabyData;
412
673
        {
413
674
            break;
414
675
        }
415
 
        else if( *pszNext == '\n' )
 
676
 
 
677
        if( *pszNext == '\r' )
 
678
            pszNext++;
 
679
        if( *pszNext == '\n' )
416
680
            pszNext++;
417
681
        else
418
682
        {
419
 
            CPLAssert( FALSE );
420
 
            break;
 
683
            CPLError(CE_Failure, CPLE_AppDefined,
 
684
                        "Error while parsing multipart content (at line %d)", __LINE__);
 
685
            return FALSE;
421
686
        }
422
687
    }
423
688