~ubuntu-branches/debian/sid/osgearth/sid

« back to all changes in this revision

Viewing changes to src/osgEarth/HTTPClient.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pirmin Kalberer
  • Date: 2011-07-14 22:13:36 UTC
  • Revision ID: james.westby@ubuntu.com-20110714221336-94igk9rskxveh794
Tags: upstream-2.0+dfsg
ImportĀ upstreamĀ versionĀ 2.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*-c++-*- */
 
2
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 
3
 * Copyright 2008-2010 Pelican Mapping
 
4
 * http://osgearth.org
 
5
 *
 
6
 * osgEarth is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU Lesser General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public License
 
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 
18
 */
 
19
 
 
20
#include <curl/curl.h>
 
21
#include <curl/types.h>
 
22
#include <osgEarth/HTTPClient>
 
23
#include <osgEarth/Registry>
 
24
#include <osgEarth/Version>
 
25
#include <osgDB/Registry>
 
26
#include <osgDB/FileNameUtils>
 
27
#include <osg/Notify>
 
28
#include <string.h>
 
29
#include <sstream>
 
30
#include <fstream>
 
31
#include <iterator>
 
32
#include <iostream>
 
33
#include <algorithm>
 
34
 
 
35
#define LC "[HTTPClient] "
 
36
 
 
37
#undef  OE_DEBUG
 
38
#define OE_DEBUG OE_NULL
 
39
 
 
40
using namespace osgEarth;
 
41
 
 
42
//----------------------------------------------------------------------------
 
43
 
 
44
ProxySettings::ProxySettings( const Config& conf )
 
45
{
 
46
    mergeConfig( conf );
 
47
}
 
48
 
 
49
ProxySettings::ProxySettings( const std::string& host, int port ) :
 
50
_hostName(host),
 
51
_port(port)
 
52
{
 
53
    //nop
 
54
}
 
55
 
 
56
void
 
57
ProxySettings::mergeConfig( const Config& conf )
 
58
{
 
59
    _hostName = conf.value<std::string>( "host", "" );
 
60
    _port = conf.value<int>( "port", 8080 );
 
61
        _userName = conf.value<std::string>( "username", "" );
 
62
        _password = conf.value<std::string>( "password", "" );
 
63
}
 
64
 
 
65
Config
 
66
ProxySettings::getConfig() const
 
67
{
 
68
    Config conf( "proxy" );
 
69
    conf.add( "host", _hostName );
 
70
    conf.add( "port", toString(_port) );
 
71
        conf.add( "username", _userName);
 
72
        conf.add( "password", _password);
 
73
 
 
74
    return conf;
 
75
}
 
76
 
 
77
/****************************************************************************/
 
78
   
 
79
namespace osgEarth
 
80
{
 
81
    struct StreamObject
 
82
    {
 
83
        StreamObject(std::ostream* stream) : _stream(stream) { }
 
84
 
 
85
        void write(const char* ptr, size_t realsize)
 
86
        {
 
87
            if (_stream) _stream->write(ptr, realsize);
 
88
        }
 
89
 
 
90
        std::ostream* _stream;
 
91
        std::string     _resultMimeType;
 
92
    };
 
93
 
 
94
    static size_t
 
95
    StreamObjectReadCallback(void* ptr, size_t size, size_t nmemb, void* data)
 
96
    {
 
97
        size_t realsize = size* nmemb;
 
98
        StreamObject* sp = (StreamObject*)data;
 
99
        sp->write((const char*)ptr, realsize);
 
100
        return realsize;
 
101
    }
 
102
}
 
103
 
 
104
static int CurlProgressCallback(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
 
105
{
 
106
    ProgressCallback* callback = (ProgressCallback*)clientp;
 
107
    bool cancelled = false;
 
108
    if (callback)
 
109
    {
 
110
        cancelled = callback->isCanceled() || callback->reportProgress(dlnow, dltotal);
 
111
    }
 
112
    return cancelled;
 
113
}
 
114
 
 
115
/****************************************************************************/
 
116
 
 
117
HTTPRequest::HTTPRequest( const std::string& url )
 
118
: _url( url )
 
119
{
 
120
    //NOP
 
121
}
 
122
 
 
123
HTTPRequest::HTTPRequest( const HTTPRequest& rhs ) :
 
124
_parameters( rhs._parameters ),
 
125
_url( rhs._url )
 
126
{
 
127
    //nop
 
128
}
 
129
 
 
130
void
 
131
HTTPRequest::addParameter( const std::string& name, const std::string& value )
 
132
{
 
133
    _parameters[name] = value;
 
134
}
 
135
 
 
136
void
 
137
HTTPRequest::addParameter( const std::string& name, int value )
 
138
{
 
139
    std::stringstream buf;
 
140
    buf << value;
 
141
        std::string bufStr;
 
142
    bufStr = buf.str();
 
143
    _parameters[name] = bufStr;
 
144
}
 
145
 
 
146
void
 
147
HTTPRequest::addParameter( const std::string& name, double value )
 
148
{
 
149
    std::stringstream buf;
 
150
    buf << value;
 
151
        std::string bufStr;
 
152
    bufStr = buf.str();
 
153
    _parameters[name] = bufStr;
 
154
}
 
155
 
 
156
const HTTPRequest::Parameters&
 
157
HTTPRequest::getParameters() const
 
158
{
 
159
    return _parameters; 
 
160
}
 
161
 
 
162
std::string
 
163
HTTPRequest::getURL() const
 
164
{
 
165
    if ( _parameters.size() == 0 )
 
166
    {
 
167
        return _url;
 
168
    }
 
169
    else
 
170
    {
 
171
        std::stringstream buf;
 
172
        buf << _url;
 
173
        for( Parameters::const_iterator i = _parameters.begin(); i != _parameters.end(); i++ )
 
174
        {
 
175
            buf << ( i == _parameters.begin() && _url.find( "?" ) == std::string::npos? "?" : "&" );
 
176
            buf << i->first << "=" << i->second;
 
177
        }
 
178
                std::string bufStr;
 
179
                bufStr = buf.str();
 
180
        return bufStr;
 
181
    }
 
182
}
 
183
 
 
184
/****************************************************************************/
 
185
 
 
186
HTTPResponse::HTTPResponse( long _code )
 
187
: _response_code( _code ),
 
188
  _cancelled(false)
 
189
{
 
190
    _parts.reserve(1);
 
191
}
 
192
 
 
193
HTTPResponse::HTTPResponse( const HTTPResponse& rhs ) :
 
194
_response_code( rhs._response_code ),
 
195
_parts( rhs._parts ),
 
196
_mimeType( rhs._mimeType ),
 
197
_cancelled( rhs._cancelled )
 
198
{
 
199
    //nop
 
200
}
 
201
 
 
202
long
 
203
HTTPResponse::getCode() const {
 
204
    return _response_code;
 
205
}
 
206
 
 
207
bool
 
208
HTTPResponse::isOK() const {
 
209
    return _response_code == 200L && !isCancelled();
 
210
}
 
211
 
 
212
bool
 
213
HTTPResponse::isCancelled() const {
 
214
    return _cancelled;
 
215
}
 
216
 
 
217
unsigned int
 
218
HTTPResponse::getNumParts() const {
 
219
    return _parts.size();
 
220
}
 
221
 
 
222
unsigned int
 
223
HTTPResponse::getPartSize( unsigned int n ) const {
 
224
    return _parts[n]->_size;
 
225
}
 
226
 
 
227
const std::string&
 
228
HTTPResponse::getPartHeader( unsigned int n, const std::string& name ) const {
 
229
    return _parts[n]->_headers[name];
 
230
}
 
231
 
 
232
std::istream&
 
233
HTTPResponse::getPartStream( unsigned int n ) const {
 
234
    return _parts[n]->_stream;
 
235
}
 
236
 
 
237
std::string
 
238
HTTPResponse::getPartAsString( unsigned int n ) const {
 
239
        std::string streamStr;
 
240
        streamStr = _parts[n]->_stream.str();
 
241
    return streamStr;
 
242
}
 
243
 
 
244
const std::string&
 
245
HTTPResponse::getMimeType() const {
 
246
    return _mimeType;
 
247
}
 
248
 
 
249
/****************************************************************************/
 
250
 
 
251
#define QUOTE_(X) #X
 
252
#define QUOTE(X) QUOTE_(X)
 
253
#define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION)
 
254
 
 
255
typedef std::map< OpenThreads::Thread*, osg::ref_ptr<HTTPClient> >    ThreadClientMap;        
 
256
static OpenThreads::Mutex          _threadClientMapMutex;
 
257
static ThreadClientMap             _threadClientMap;
 
258
static optional<ProxySettings>     _proxySettings;
 
259
static std::string                 _userAgent = USER_AGENT;
 
260
 
 
261
HTTPClient& HTTPClient::getClient()
 
262
{
 
263
    OpenThreads::ScopedLock<OpenThreads::Mutex>  lock(_threadClientMapMutex);
 
264
    static unsigned int numClients = 0;
 
265
    osg::ref_ptr<HTTPClient>& client = _threadClientMap[OpenThreads::Thread::CurrentThread()];
 
266
    if (!client) 
 
267
    {
 
268
        client = new HTTPClient();
 
269
        numClients++;
 
270
    }
 
271
 
 
272
    return *client;
 
273
}
 
274
 
 
275
HTTPClient::HTTPClient()
 
276
{
 
277
    _previousHttpAuthentication = 0;
 
278
    _curl_handle = curl_easy_init();
 
279
 
 
280
 
 
281
        //Get the user agent
 
282
        std::string userAgent = _userAgent;
 
283
        const char* userAgentEnv = getenv("OSGEARTH_USERAGENT");
 
284
    if (userAgentEnv)
 
285
    {
 
286
                userAgent = std::string(userAgentEnv);        
 
287
    }
 
288
 
 
289
        OE_DEBUG << LC << "HTTPClient setting userAgent=" << userAgent << std::endl;
 
290
 
 
291
    curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, userAgent.c_str() );
 
292
    curl_easy_setopt( _curl_handle, CURLOPT_WRITEFUNCTION, osgEarth::StreamObjectReadCallback );
 
293
    curl_easy_setopt( _curl_handle, CURLOPT_FOLLOWLOCATION, (void*)1 );
 
294
    curl_easy_setopt( _curl_handle, CURLOPT_MAXREDIRS, (void*)5 );
 
295
    curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSFUNCTION, &CurlProgressCallback);
 
296
    curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //FALSE);
 
297
    //curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, 1L );
 
298
}
 
299
 
 
300
HTTPClient::~HTTPClient()
 
301
{
 
302
    if (_curl_handle) curl_easy_cleanup( _curl_handle );
 
303
    _curl_handle = 0;
 
304
}
 
305
 
 
306
void
 
307
HTTPClient::setProxySettings( const ProxySettings &proxySettings )
 
308
{
 
309
        _proxySettings = proxySettings;
 
310
}
 
311
 
 
312
const std::string& HTTPClient::getUserAgent()
 
313
{
 
314
        return _userAgent;
 
315
}
 
316
 
 
317
void  HTTPClient::setUserAgent(const std::string& userAgent)
 
318
{
 
319
        _userAgent = userAgent;
 
320
}
 
321
 
 
322
void
 
323
HTTPClient::readOptions( const osgDB::ReaderWriter::Options* options, std::string& proxy_host, std::string& proxy_port) const
 
324
{
 
325
    // try to set proxy host/port by reading the CURL proxy options
 
326
    if ( options )
 
327
    {
 
328
        std::istringstream iss( options->getOptionString() );
 
329
        std::string opt;
 
330
        while( iss >> opt )
 
331
        {
 
332
            int index = opt.find( "=" );
 
333
            if( opt.substr( 0, index ) == "OSG_CURL_PROXY" )
 
334
            {
 
335
                proxy_host = opt.substr( index+1 );
 
336
            }
 
337
            else if ( opt.substr( 0, index ) == "OSG_CURL_PROXYPORT" )
 
338
            {
 
339
                proxy_port = opt.substr( index+1 );
 
340
            }
 
341
        }
 
342
    }
 
343
}
 
344
 
 
345
// from: http://www.rosettacode.org/wiki/Tokenizing_A_String#C.2B.2B
 
346
static std::vector<std::string> 
 
347
tokenize_str(const std::string & str, const std::string & delims=", \t")
 
348
{
 
349
  using namespace std;
 
350
  // Skip delims at beginning, find start of first token
 
351
  string::size_type lastPos = str.find_first_not_of(delims, 0);
 
352
  // Find next delimiter @ end of token
 
353
  string::size_type pos     = str.find_first_of(delims, lastPos);
 
354
 
 
355
  // output vector
 
356
  vector<string> tokens;
 
357
 
 
358
  while (string::npos != pos || string::npos != lastPos)
 
359
    {
 
360
      // Found a token, add it to the vector.
 
361
      tokens.push_back(str.substr(lastPos, pos - lastPos));
 
362
      // Skip delims.  Note the "not_of". this is beginning of token
 
363
      lastPos = str.find_first_not_of(delims, pos);
 
364
      // Find next delimiter at end of token.
 
365
      pos     = str.find_first_of(delims, lastPos);
 
366
    }
 
367
 
 
368
  return tokens;
 
369
}
 
370
 
 
371
 
 
372
void
 
373
HTTPClient::decodeMultipartStream(const std::string&   boundary,
 
374
                                  HTTPResponse::Part*  input,
 
375
                                  HTTPResponse::Parts& output) const
 
376
{
 
377
    std::string bstr = std::string("--") + boundary;
 
378
    std::string line;
 
379
    char tempbuf[256];
 
380
 
 
381
    // first thing in the stream should be the boundary.
 
382
    input->_stream.read( tempbuf, bstr.length() );
 
383
    tempbuf[bstr.length()] = 0;
 
384
    line = tempbuf;
 
385
    if ( line != bstr )
 
386
    {
 
387
        OE_WARN << LC 
 
388
            << "decodeMultipartStream: protocol violation; "
 
389
            << "expecting boundary; instead got: \"" 
 
390
            << line
 
391
            << "\"" << std::endl;
 
392
        return;
 
393
    }
 
394
 
 
395
    for( bool done=false; !done; )
 
396
    {
 
397
        osg::ref_ptr<HTTPResponse::Part> next_part = new HTTPResponse::Part();
 
398
 
 
399
        // first finish off the boundary.
 
400
        std::getline( input->_stream, line );
 
401
        if ( line == "--" )
 
402
        {
 
403
            done = true;
 
404
        }
 
405
        else
 
406
        {
 
407
            // read all headers. this ends with a blank line.
 
408
            line = " ";
 
409
            while( line.length() > 0 && !done )
 
410
            {
 
411
                std::getline( input->_stream, line );
 
412
 
 
413
                // check for EOS:
 
414
                if ( line == "--" )
 
415
                {
 
416
                    done = true;
 
417
                }
 
418
                else
 
419
                {
 
420
                    std::vector<std::string> tized = tokenize_str( line, ":" );
 
421
                    if ( tized.size() >= 2 )
 
422
                        next_part->_headers[tized[0]] = tized[1];
 
423
                }
 
424
            }
 
425
        }
 
426
 
 
427
        if ( !done )
 
428
        {
 
429
            // read data until we reach the boundary
 
430
            unsigned int bstr_ptr = 0;
 
431
            std::string temp;
 
432
            //unsigned int c = 0;
 
433
            while( bstr_ptr < bstr.length() )
 
434
            {
 
435
                char b;
 
436
                input->_stream.read( &b, 1 );
 
437
                if ( b == bstr[bstr_ptr] )
 
438
                {
 
439
                    bstr_ptr++;
 
440
                }
 
441
                else
 
442
                {
 
443
                    for( unsigned int i=0; i<bstr_ptr; i++ )
 
444
                    {
 
445
                        next_part->_stream << bstr[i];
 
446
                    }
 
447
                    next_part->_stream << b;
 
448
                    next_part->_size += bstr_ptr + 1;
 
449
                    bstr_ptr = 0;
 
450
                }
 
451
            }
 
452
            output.push_back( next_part.get() );
 
453
        }
 
454
    }
 
455
}
 
456
 
 
457
HTTPResponse
 
458
HTTPClient::get( const HTTPRequest& request,
 
459
                 const osgDB::ReaderWriter::Options* options,
 
460
                 ProgressCallback* callback)
 
461
{
 
462
    return getClient().doGet( request, options, callback );
 
463
}
 
464
 
 
465
HTTPResponse 
 
466
HTTPClient::get( const std::string &url,
 
467
                 const osgDB::ReaderWriter::Options* options,
 
468
                 ProgressCallback* callback)
 
469
{
 
470
    return getClient().doGet( url, options, callback);
 
471
}
 
472
 
 
473
HTTPClient::ResultCode
 
474
HTTPClient::readImageFile(const std::string &filename,
 
475
                          osg::ref_ptr<osg::Image>& output,
 
476
                          const osgDB::ReaderWriter::Options *options,
 
477
                          osgEarth::ProgressCallback *callback)
 
478
{
 
479
    return getClient().doReadImageFile( filename, output, options, callback );
 
480
}
 
481
 
 
482
HTTPClient::ResultCode
 
483
HTTPClient::readNodeFile(const std::string& filename,
 
484
                         osg::ref_ptr<osg::Node>& output,
 
485
                         const osgDB::ReaderWriter::Options *options,
 
486
                         osgEarth::ProgressCallback *callback)
 
487
{
 
488
    return getClient().doReadNodeFile( filename, output, options, callback );
 
489
}
 
490
 
 
491
HTTPClient::ResultCode
 
492
HTTPClient::readString(const std::string& filename,
 
493
                       std::string& output,
 
494
                       osgEarth::ProgressCallback* callback)
 
495
{
 
496
    return getClient().doReadString( filename, output, callback );
 
497
}
 
498
 
 
499
HTTPResponse
 
500
HTTPClient::doGet( const HTTPRequest& request, const osgDB::ReaderWriter::Options* options, ProgressCallback* callback) const
 
501
{
 
502
    OE_DEBUG << LC << "doGet " << request.getURL() << std::endl;
 
503
 
 
504
    const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ? 
 
505
            options->getAuthenticationMap() :
 
506
            osgDB::Registry::instance()->getAuthenticationMap();
 
507
 
 
508
    std::string proxy_host;
 
509
    std::string proxy_port = "8080";
 
510
 
 
511
        std::string proxy_auth;
 
512
 
 
513
        //Try to get the proxy settings from the global settings
 
514
        if (_proxySettings.isSet())
 
515
        {
 
516
                proxy_host = _proxySettings.get().hostName();
 
517
                std::stringstream buf;
 
518
                buf << _proxySettings.get().port();
 
519
                proxy_port = buf.str();
 
520
 
 
521
                std::string proxy_username = _proxySettings.get().userName();
 
522
                std::string proxy_password = _proxySettings.get().password();
 
523
                if (!proxy_username.empty() && !proxy_password.empty())
 
524
                {
 
525
                        proxy_auth = proxy_username + ":" + proxy_password;
 
526
                }
 
527
        }
 
528
 
 
529
        //Try to get the proxy settings from the local options that are passed in.
 
530
    readOptions( options, proxy_host, proxy_port );
 
531
 
 
532
        //Try to get the proxy settings from the environment variable
 
533
    const char* proxyEnvAddress = getenv("OSG_CURL_PROXY");
 
534
    if (proxyEnvAddress) //Env Proxy Settings
 
535
    {
 
536
                proxy_host = std::string(proxyEnvAddress);
 
537
 
 
538
        const char* proxyEnvPort = getenv("OSG_CURL_PROXYPORT"); //Searching Proxy Port on Env
 
539
                if (proxyEnvPort)
 
540
                {
 
541
                        proxy_port = std::string( proxyEnvPort );
 
542
                }
 
543
    }
 
544
 
 
545
        const char* proxyEnvAuth = getenv("OSGEARTH_CURL_PROXYAUTH");   
 
546
        if (proxyEnvAuth)
 
547
        {
 
548
                proxy_auth = std::string(proxyEnvAuth);
 
549
        }
 
550
 
 
551
    // Set up proxy server:
 
552
    std::string proxy_addr;
 
553
    if ( !proxy_host.empty() )
 
554
    {
 
555
        std::stringstream buf;
 
556
        buf << proxy_host << ":" << proxy_port;
 
557
                std::string bufStr;
 
558
                bufStr = buf.str();
 
559
        proxy_addr = bufStr;
 
560
    
 
561
        OE_DEBUG << LC << "setting proxy: " << proxy_addr << std::endl;
 
562
                //curl_easy_setopt( _curl_handle, CURLOPT_HTTPPROXYTUNNEL, 1 ); 
 
563
        curl_easy_setopt( _curl_handle, CURLOPT_PROXY, proxy_addr.c_str() );
 
564
 
 
565
                //Setup the proxy authentication if setup
 
566
                if (!proxy_auth.empty())
 
567
                {
 
568
                        OE_DEBUG << LC << "Setting up proxy authentication " << proxy_auth << std::endl;
 
569
                        curl_easy_setopt( _curl_handle, CURLOPT_PROXYUSERPWD, proxy_auth.c_str());
 
570
                }
 
571
    }
 
572
 
 
573
    const osgDB::AuthenticationDetails* details = authenticationMap ?
 
574
        authenticationMap->getAuthenticationDetails(request.getURL()) :
 
575
        0;
 
576
 
 
577
        if (details)
 
578
        {
 
579
            const std::string colon(":");
 
580
            std::string password(details->username + colon + details->password);
 
581
            curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, password.c_str());
 
582
            const_cast<HTTPClient*>(this)->_previousPassword = password;
 
583
 
 
584
            // use for https.
 
585
            // curl_easy_setopt(_curl, CURLOPT_KEYPASSWD, password.c_str());
 
586
 
 
587
#if LIBCURL_VERSION_NUM >= 0x070a07
 
588
            if (details->httpAuthentication != _previousHttpAuthentication)
 
589
            { 
 
590
                curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, details->httpAuthentication); 
 
591
                const_cast<HTTPClient*>(this)->_previousHttpAuthentication = details->httpAuthentication;
 
592
            }
 
593
#endif
 
594
    }
 
595
    else
 
596
    {
 
597
        if (!_previousPassword.empty())
 
598
        {
 
599
            curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, 0);
 
600
            const_cast<HTTPClient*>(this)->_previousPassword.clear();
 
601
        }
 
602
 
 
603
#if LIBCURL_VERSION_NUM >= 0x070a07
 
604
        // need to reset if previously set.
 
605
        if (_previousHttpAuthentication!=0)
 
606
        {
 
607
            curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, 0); 
 
608
            const_cast<HTTPClient*>(this)->_previousHttpAuthentication = 0;
 
609
        }
 
610
#endif
 
611
    }
 
612
 
 
613
    osg::ref_ptr<HTTPResponse::Part> part = new HTTPResponse::Part();
 
614
    StreamObject sp( &part->_stream );
 
615
 
 
616
    //Take a temporary ref to the callback
 
617
    osg::ref_ptr<ProgressCallback> progressCallback = callback;
 
618
    curl_easy_setopt( _curl_handle, CURLOPT_URL, request.getURL().c_str() );
 
619
    if (callback)
 
620
    {
 
621
        curl_easy_setopt(_curl_handle, CURLOPT_PROGRESSDATA, progressCallback.get());
 
622
    }
 
623
 
 
624
    char errorBuf[CURL_ERROR_SIZE];
 
625
    errorBuf[0] = 0;
 
626
    curl_easy_setopt( _curl_handle, CURLOPT_ERRORBUFFER, (void*)errorBuf );
 
627
 
 
628
    curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)&sp);
 
629
    CURLcode res = curl_easy_perform( _curl_handle );
 
630
    curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)0 );
 
631
    curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSDATA, (void*)0);
 
632
 
 
633
    long response_code = 0L;
 
634
        if (!proxy_addr.empty())
 
635
        {
 
636
                long connect_code = 0L;
 
637
        curl_easy_getinfo( _curl_handle, CURLINFO_HTTP_CONNECTCODE, &connect_code );
 
638
                OE_DEBUG << LC << "proxy connect code " << connect_code << std::endl;
 
639
        }
 
640
        
 
641
    curl_easy_getinfo( _curl_handle, CURLINFO_RESPONSE_CODE, &response_code );     
 
642
 
 
643
        OE_DEBUG << LC << "got response, code = " << response_code << std::endl;
 
644
 
 
645
    HTTPResponse response( response_code );
 
646
   
 
647
    if ( response_code == 200L && res != CURLE_ABORTED_BY_CALLBACK && res != CURLE_OPERATION_TIMEDOUT ) //res == 0 )
 
648
    {
 
649
        // check for multipart content:
 
650
        char* content_type_cp;
 
651
        curl_easy_getinfo( _curl_handle, CURLINFO_CONTENT_TYPE, &content_type_cp );
 
652
        if ( content_type_cp == NULL )
 
653
        {
 
654
            OE_NOTICE << LC
 
655
                << "NULL Content-Type (protocol violation) " 
 
656
                << "URL=" << request.getURL() << std::endl;
 
657
            return NULL;
 
658
        }
 
659
 
 
660
        // NOTE:
 
661
        //   WCS 1.1 specified a "multipart/mixed" response, but ArcGIS Server gives a "multipart/related"
 
662
        //   content type ...
 
663
 
 
664
        std::string content_type( content_type_cp );
 
665
        //OE_NOTICE << "[osgEarth.HTTPClient] content-type = \"" << content_type << "\"" << std::endl;
 
666
        if ( content_type.length() > 9 && ::strstr( content_type.c_str(), "multipart" ) == content_type.c_str() )
 
667
        //if ( content_type == "multipart/mixed; boundary=wcs" ) //todo: parse this.
 
668
        {
 
669
            //OE_NOTICE << "[osgEarth.HTTPClient] detected multipart data; decoding..." << std::endl;
 
670
            //TODO: parse out the "wcs" -- this is WCS-specific
 
671
            decodeMultipartStream( "wcs", part.get(), response._parts );
 
672
        }
 
673
        else
 
674
        {
 
675
            //OE_NOTICE << "[osgEarth.HTTPClient] detected single part data" << std::endl;
 
676
            response._parts.push_back( part.get() );
 
677
        }
 
678
    }
 
679
    else if (res == CURLE_ABORTED_BY_CALLBACK || res == CURLE_OPERATION_TIMEDOUT)
 
680
    {
 
681
        //If we were aborted by a callback, then it was cancelled by a user
 
682
        response._cancelled = true;
 
683
    }
 
684
    else
 
685
    {        
 
686
        //if ( callback )
 
687
        //{
 
688
        //    if ( errorBuf[0] ) {
 
689
        //        callback->message() = errorBuf;
 
690
        //    }
 
691
        //    else {
 
692
        //        std::stringstream buf;
 
693
        //        buf << "HTTP Code " << response.getCode();
 
694
        //        callback->message() = buf.str();
 
695
        //    }
 
696
        //}
 
697
        //else {
 
698
        //    OE_NOTICE << "[osgEarth] [HTTP] error, code = " << code << std::endl;
 
699
        //}
 
700
    }
 
701
 
 
702
    // Store the mime-type, if any. (Note: CURL manages the buffer returned by
 
703
    // this call.)
 
704
    char* ctbuf = NULL;
 
705
    if ( curl_easy_getinfo(_curl_handle, CURLINFO_CONTENT_TYPE, &ctbuf) == 0 && ctbuf )
 
706
    {
 
707
        response._mimeType = ctbuf;
 
708
    }
 
709
 
 
710
    return response;
 
711
}
 
712
 
 
713
 
 
714
HTTPResponse
 
715
HTTPClient::doGet( const std::string& url, const osgDB::ReaderWriter::Options* options, ProgressCallback* callback) const
 
716
{
 
717
    return doGet( HTTPRequest( url ), options, callback );
 
718
}
 
719
 
 
720
bool
 
721
HTTPClient::downloadFile(const std::string &url, const std::string &filename)
 
722
{
 
723
    // download the data
 
724
    HTTPResponse response = this->doGet( HTTPRequest(url) );
 
725
 
 
726
    if ( response.isOK() )
 
727
    {
 
728
        unsigned int part_num = response.getNumParts() > 1? 1 : 0;
 
729
        std::istream& input_stream = response.getPartStream( part_num );
 
730
 
 
731
        std::ofstream fout;
 
732
        fout.open(filename.c_str(), std::ios::out | std::ios::binary);
 
733
 
 
734
        input_stream.seekg (0, std::ios::end);
 
735
        int length = input_stream.tellg();
 
736
        input_stream.seekg (0, std::ios::beg);
 
737
 
 
738
        char *buffer = new char[length];
 
739
        input_stream.read(buffer, length);
 
740
        fout.write(buffer, length);
 
741
        delete[] buffer;
 
742
        fout.close();
 
743
        return true;
 
744
    }
 
745
    else
 
746
    {
 
747
        OE_WARN << LC << "Error downloading file " << filename << std::endl;
 
748
        return false;
 
749
    } 
 
750
}
 
751
 
 
752
HTTPClient::ResultCode
 
753
HTTPClient::doReadImageFile(const std::string& filename, 
 
754
                            osg::ref_ptr<osg::Image>& output,
 
755
                            const osgDB::ReaderWriter::Options *options,
 
756
                            osgEarth::ProgressCallback *callback)
 
757
{
 
758
    ResultCode result = RESULT_OK;
 
759
 
 
760
    if ( osgDB::containsServerAddress( filename ) )
 
761
    {
 
762
        HTTPResponse response = this->doGet(filename, options, callback);
 
763
 
 
764
        if (response.isOK())
 
765
        {
 
766
            osgDB::ReaderWriter* reader = 0L;
 
767
 
 
768
            // try to look up a reader by mime-type first:
 
769
            std::string mimeType = response.getMimeType();
 
770
            OE_DEBUG << LC << "Looking up extension for mime-type " << mimeType << std::endl;
 
771
            if ( !mimeType.empty() )
 
772
            {
 
773
                reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType);
 
774
            }
 
775
 
 
776
            if ( !reader )
 
777
            {
 
778
                // Try to find a reader by file extension. If this fails, we will fetch the file
 
779
                // anyway and try to get a reader via mime-type.
 
780
                std::string ext = osgDB::getFileExtension( filename );
 
781
                reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext );
 
782
                //OE_NOTICE << "Reading " << filename << " with mime " << response.getMimeType() << std::endl;
 
783
            }
 
784
 
 
785
            if (!reader)
 
786
            {
 
787
                OE_WARN << LC << "Can't find an OSG plugin to read "<<filename<<std::endl;
 
788
                result = RESULT_NO_READER;
 
789
            }
 
790
 
 
791
            else 
 
792
            {
 
793
                osgDB::ReaderWriter::ReadResult rr = reader->readImage(response.getPartStream(0), options);
 
794
                if ( rr.validImage() )
 
795
                {
 
796
                    output = rr.takeImage();
 
797
                }
 
798
                else 
 
799
                {
 
800
                    if ( !rr.message().empty() )
 
801
                    {
 
802
                        OE_WARN << LC << "HTTP error: " << rr.message() << std::endl;
 
803
                    }
 
804
                    OE_WARN << LC << reader->className() << " failed to read image from " << filename << std::endl;
 
805
                    result = RESULT_READER_ERROR;
 
806
                }
 
807
            }
 
808
        }
 
809
        else
 
810
        {
 
811
            result =
 
812
                response.isCancelled() ? RESULT_CANCELED :
 
813
                response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
 
814
                response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
 
815
                RESULT_UNKNOWN_ERROR;
 
816
 
 
817
            //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
 
818
            if (HTTPClient::isRecoverable( result ) )
 
819
            {
 
820
                if (callback)
 
821
                {
 
822
                    OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
 
823
                    callback->setNeedsRetry( true );
 
824
                }
 
825
            }
 
826
 
 
827
            //if ( response.isCancelled() )
 
828
            //    OE_NOTICE << "HTTP cancel: " << filename << std::endl;
 
829
            //else
 
830
            //    OE_NOTICE << "HTTP ERROR " << response.getCode() << ": " << filename << std::endl;
 
831
 
 
832
            /*if (response.isCancelled())
 
833
                OE_NOTICE << "Request for " << filename << " was cancelled " << std::endl;*/
 
834
        }
 
835
    }
 
836
    else
 
837
    {
 
838
        output = osgDB::readImageFile( filename, options );
 
839
        if ( !output.valid() )
 
840
            result = RESULT_NOT_FOUND;
 
841
    }
 
842
 
 
843
    return result;
 
844
}
 
845
 
 
846
HTTPClient::ResultCode
 
847
HTTPClient::doReadNodeFile(const std::string& filename,
 
848
                           osg::ref_ptr<osg::Node>& output,
 
849
                           const osgDB::ReaderWriter::Options *options,
 
850
                           osgEarth::ProgressCallback *callback)
 
851
{
 
852
    ResultCode result = RESULT_OK;
 
853
 
 
854
    if ( osgDB::containsServerAddress( filename ) )
 
855
    {
 
856
        HTTPResponse response = this->doGet(filename, options, callback);
 
857
        if (response.isOK())
 
858
        {
 
859
            // Try to find a reader by file extension. If this fails, we will fetch the file
 
860
            // anyway and try to get a reader via mime-type.
 
861
            std::string ext = osgDB::getFileExtension( filename );
 
862
            osgDB::ReaderWriter *reader =
 
863
                osgDB::Registry::instance()->getReaderWriterForExtension( ext );
 
864
 
 
865
            //If we didn't get a reader by extension, try to get it via mime type
 
866
            if (!reader)
 
867
            {
 
868
                std::string mimeType = response.getMimeType();
 
869
                OE_DEBUG << LC << "Looking up extension for mime-type " << mimeType << std::endl;
 
870
                if ( mimeType.length() > 0 )
 
871
                {
 
872
                    reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType);
 
873
                }
 
874
            }
 
875
 
 
876
            // if we still didn't get it, bad news
 
877
            if (!reader)
 
878
            {
 
879
                OE_NOTICE<<LC<<"Error: No ReaderWriter for file "<<filename<<std::endl;
 
880
                result = RESULT_NO_READER;
 
881
            }
 
882
 
 
883
            else
 
884
            {
 
885
                osgDB::ReaderWriter::ReadResult rr = reader->readNode(response.getPartStream(0), options);
 
886
                if ( rr.validNode() )
 
887
                {
 
888
                    output = rr.takeNode();
 
889
                }
 
890
                else
 
891
                {
 
892
                    if ( rr.error() )
 
893
                    {
 
894
                        OE_WARN << LC << "HTTP Reader Error: " << rr.message() << std::endl;
 
895
                    }
 
896
                    result = RESULT_READER_ERROR;
 
897
                }
 
898
            }
 
899
        }
 
900
        else
 
901
        {
 
902
            result =
 
903
                response.isCancelled() ? RESULT_CANCELED :
 
904
                response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
 
905
                response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
 
906
                RESULT_UNKNOWN_ERROR;
 
907
 
 
908
            //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
 
909
            if (HTTPClient::isRecoverable( result ) )
 
910
            {
 
911
                if (callback)
 
912
                {
 
913
                    OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
 
914
                    callback->setNeedsRetry( true );
 
915
                }
 
916
            }
 
917
               
 
918
            /*if (response.isCancelled())
 
919
                OE_NOTICE << "Request for " << filename << " was cancelled " << std::endl;*/
 
920
        }
 
921
    }
 
922
    else
 
923
    {
 
924
        output = osgDB::readNodeFile( filename, options );
 
925
        if ( !output.valid() )
 
926
            result = RESULT_NOT_FOUND;
 
927
    }
 
928
 
 
929
    return result;
 
930
}
 
931
 
 
932
 
 
933
HTTPClient::ResultCode 
 
934
HTTPClient::doReadString(const std::string& filename,
 
935
                         std::string& output,
 
936
                         osgEarth::ProgressCallback* callback )
 
937
{
 
938
    ResultCode result = RESULT_OK;
 
939
 
 
940
    if ( osgDB::containsServerAddress( filename ) )
 
941
    {
 
942
        HTTPResponse response = this->doGet( filename, NULL, callback );
 
943
        if ( response.isOK() )
 
944
        {
 
945
            output = response.getPartAsString( 0 );
 
946
        }
 
947
        else
 
948
        {
 
949
            result =
 
950
                response.isCancelled() ? RESULT_CANCELED :
 
951
                response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
 
952
                response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
 
953
                RESULT_UNKNOWN_ERROR;
 
954
 
 
955
            //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
 
956
            if (HTTPClient::isRecoverable( result ) )
 
957
            {
 
958
                if (callback)
 
959
                {
 
960
                    OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
 
961
                    callback->setNeedsRetry( true );
 
962
                }
 
963
            }
 
964
        }
 
965
    }
 
966
    else
 
967
    {
 
968
        std::ifstream input( filename.c_str() );
 
969
        if ( input.is_open() )
 
970
        {
 
971
            input >> std::noskipws;
 
972
            std::stringstream buf;
 
973
            buf << input.rdbuf();
 
974
                        std::string bufStr;
 
975
                    bufStr = buf.str();
 
976
            output = bufStr;
 
977
        }
 
978
        else
 
979
        {
 
980
            result = RESULT_NOT_FOUND;
 
981
        }
 
982
    }
 
983
 
 
984
    return result;
 
985
}