osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/HTTPClient.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00020 #include <curl/curl.h>
00021 //#include <curl/types.h>
00022 #include <osgEarth/HTTPClient>
00023 #include <osgEarth/Registry>
00024 #include <osgEarth/Version>
00025 #include <osgDB/Registry>
00026 #include <osgDB/FileNameUtils>
00027 #include <osg/Notify>
00028 #include <string.h>
00029 #include <sstream>
00030 #include <fstream>
00031 #include <iterator>
00032 #include <iostream>
00033 #include <algorithm>
00034 
00035 #define LC "[HTTPClient] "
00036 
00037 #undef  OE_DEBUG
00038 #define OE_DEBUG OE_NULL
00039 
00040 using namespace osgEarth;
00041 
00042 //----------------------------------------------------------------------------
00043 
00044 ProxySettings::ProxySettings( const Config& conf )
00045 {
00046     mergeConfig( conf );
00047 }
00048 
00049 ProxySettings::ProxySettings( const std::string& host, int port ) :
00050 _hostName(host),
00051 _port(port)
00052 {
00053     //nop
00054 }
00055 
00056 void
00057 ProxySettings::mergeConfig( const Config& conf )
00058 {
00059     _hostName = conf.value<std::string>( "host", "" );
00060     _port = conf.value<int>( "port", 8080 );
00061         _userName = conf.value<std::string>( "username", "" );
00062         _password = conf.value<std::string>( "password", "" );
00063 }
00064 
00065 Config
00066 ProxySettings::getConfig() const
00067 {
00068     Config conf( "proxy" );
00069     conf.add( "host", _hostName );
00070     conf.add( "port", toString(_port) );
00071         conf.add( "username", _userName);
00072         conf.add( "password", _password);
00073 
00074     return conf;
00075 }
00076 
00077 /****************************************************************************/
00078    
00079 namespace osgEarth
00080 {
00081     struct StreamObject
00082     {
00083         StreamObject(std::ostream* stream) : _stream(stream) { }
00084 
00085         void write(const char* ptr, size_t realsize)
00086         {
00087             if (_stream) _stream->write(ptr, realsize);
00088         }
00089 
00090         std::ostream* _stream;
00091         std::string     _resultMimeType;
00092     };
00093 
00094     static size_t
00095     StreamObjectReadCallback(void* ptr, size_t size, size_t nmemb, void* data)
00096     {
00097         size_t realsize = size* nmemb;
00098         StreamObject* sp = (StreamObject*)data;
00099         sp->write((const char*)ptr, realsize);
00100         return realsize;
00101     }
00102 }
00103 
00104 static int CurlProgressCallback(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
00105 {
00106     ProgressCallback* callback = (ProgressCallback*)clientp;
00107     bool cancelled = false;
00108     if (callback)
00109     {
00110         cancelled = callback->isCanceled() || callback->reportProgress(dlnow, dltotal);
00111     }
00112     return cancelled;
00113 }
00114 
00115 /****************************************************************************/
00116 
00117 HTTPRequest::HTTPRequest( const std::string& url )
00118 : _url( url )
00119 {
00120     //NOP
00121 }
00122 
00123 HTTPRequest::HTTPRequest( const HTTPRequest& rhs ) :
00124 _parameters( rhs._parameters ),
00125 _url( rhs._url )
00126 {
00127     //nop
00128 }
00129 
00130 void
00131 HTTPRequest::addParameter( const std::string& name, const std::string& value )
00132 {
00133     _parameters[name] = value;
00134 }
00135 
00136 void
00137 HTTPRequest::addParameter( const std::string& name, int value )
00138 {
00139     std::stringstream buf;
00140     buf << value;
00141         std::string bufStr;
00142     bufStr = buf.str();
00143     _parameters[name] = bufStr;
00144 }
00145 
00146 void
00147 HTTPRequest::addParameter( const std::string& name, double value )
00148 {
00149     std::stringstream buf;
00150     buf << value;
00151         std::string bufStr;
00152     bufStr = buf.str();
00153     _parameters[name] = bufStr;
00154 }
00155 
00156 const HTTPRequest::Parameters&
00157 HTTPRequest::getParameters() const
00158 {
00159     return _parameters; 
00160 }
00161 
00162 std::string
00163 HTTPRequest::getURL() const
00164 {
00165     if ( _parameters.size() == 0 )
00166     {
00167         return _url;
00168     }
00169     else
00170     {
00171         std::stringstream buf;
00172         buf << _url;
00173         for( Parameters::const_iterator i = _parameters.begin(); i != _parameters.end(); i++ )
00174         {
00175             buf << ( i == _parameters.begin() && _url.find( "?" ) == std::string::npos? "?" : "&" );
00176             buf << i->first << "=" << i->second;
00177         }
00178                 std::string bufStr;
00179                 bufStr = buf.str();
00180         return bufStr;
00181     }
00182 }
00183 
00184 /****************************************************************************/
00185 
00186 HTTPResponse::HTTPResponse( long _code )
00187 : _response_code( _code ),
00188   _cancelled(false)
00189 {
00190     _parts.reserve(1);
00191 }
00192 
00193 HTTPResponse::HTTPResponse( const HTTPResponse& rhs ) :
00194 _response_code( rhs._response_code ),
00195 _parts( rhs._parts ),
00196 _mimeType( rhs._mimeType ),
00197 _cancelled( rhs._cancelled )
00198 {
00199     //nop
00200 }
00201 
00202 long
00203 HTTPResponse::getCode() const {
00204     return _response_code;
00205 }
00206 
00207 bool
00208 HTTPResponse::isOK() const {
00209     return _response_code == 200L && !isCancelled();
00210 }
00211 
00212 bool
00213 HTTPResponse::isCancelled() const {
00214     return _cancelled;
00215 }
00216 
00217 unsigned int
00218 HTTPResponse::getNumParts() const {
00219     return _parts.size();
00220 }
00221 
00222 unsigned int
00223 HTTPResponse::getPartSize( unsigned int n ) const {
00224     return _parts[n]->_size;
00225 }
00226 
00227 const std::string&
00228 HTTPResponse::getPartHeader( unsigned int n, const std::string& name ) const {
00229     return _parts[n]->_headers[name];
00230 }
00231 
00232 std::istream&
00233 HTTPResponse::getPartStream( unsigned int n ) const {
00234     return _parts[n]->_stream;
00235 }
00236 
00237 std::string
00238 HTTPResponse::getPartAsString( unsigned int n ) const {
00239         std::string streamStr;
00240         streamStr = _parts[n]->_stream.str();
00241     return streamStr;
00242 }
00243 
00244 const std::string&
00245 HTTPResponse::getMimeType() const {
00246     return _mimeType;
00247 }
00248 
00249 /****************************************************************************/
00250 
00251 #define QUOTE_(X) #X
00252 #define QUOTE(X) QUOTE_(X)
00253 #define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION)
00254 
00255 
00256 static optional<ProxySettings>     _proxySettings;
00257 static std::string                 _userAgent = USER_AGENT;
00258 
00259 
00260 HTTPClient&
00261 HTTPClient::getClient()
00262 {
00263 #if 1
00264     static Threading::PerThread< osg::ref_ptr<HTTPClient> > s_clientPerThread;
00265 
00266     osg::ref_ptr<HTTPClient>& client = s_clientPerThread.get();
00267     if ( !client.valid() )
00268         client = new HTTPClient();
00269 
00270     return *client.get();
00271 #else
00272     typedef std::map< OpenThreads::Thread*, osg::ref_ptr<HTTPClient> > ThreadClientMap;        
00273     static Threading::ReadWriteMutex   _threadClientMapMutex;
00274     static ThreadClientMap             _threadClientMap;
00275 
00276     OpenThreads::Thread* current = OpenThreads::Thread::CurrentThread();
00277 
00278     // first try the map:
00279     {
00280         Threading::ScopedReadLock sharedLock(_threadClientMapMutex);
00281         ThreadClientMap::iterator i = _threadClientMap.find(current);
00282         if ( i != _threadClientMap.end() )
00283             return *i->second.get();
00284     }
00285 
00286     // not there; add it.
00287     {
00288         Threading::ScopedWriteLock exclusiveLock(_threadClientMapMutex);
00289 
00290         // normally, we'd double check b/c of the race condition, but since the map is being 
00291         // indexed by the actual thread pointer, there's no chance of a race.
00292         HTTPClient* client = new HTTPClient();
00293         _threadClientMap[current] = client;
00294         return *client;
00295     }
00296 #endif
00297 }
00298 
00299 HTTPClient::HTTPClient()
00300 {
00301     _previousHttpAuthentication = 0;
00302     _curl_handle = curl_easy_init();
00303 
00304 
00305         //Get the user agent
00306         std::string userAgent = _userAgent;
00307         const char* userAgentEnv = getenv("OSGEARTH_USERAGENT");
00308     if (userAgentEnv)
00309     {
00310                 userAgent = std::string(userAgentEnv);        
00311     }
00312 
00313         OE_DEBUG << LC << "HTTPClient setting userAgent=" << userAgent << std::endl;
00314 
00315     curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, userAgent.c_str() );
00316     curl_easy_setopt( _curl_handle, CURLOPT_WRITEFUNCTION, osgEarth::StreamObjectReadCallback );
00317     curl_easy_setopt( _curl_handle, CURLOPT_FOLLOWLOCATION, (void*)1 );
00318     curl_easy_setopt( _curl_handle, CURLOPT_MAXREDIRS, (void*)5 );
00319     curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSFUNCTION, &CurlProgressCallback);
00320     curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //FALSE);
00321     //curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, 1L );
00322 }
00323 
00324 HTTPClient::~HTTPClient()
00325 {
00326     if (_curl_handle) curl_easy_cleanup( _curl_handle );
00327     _curl_handle = 0;
00328 }
00329 
00330 void
00331 HTTPClient::setProxySettings( const ProxySettings &proxySettings )
00332 {
00333         _proxySettings = proxySettings;
00334 }
00335 
00336 const std::string& HTTPClient::getUserAgent()
00337 {
00338         return _userAgent;
00339 }
00340 
00341 void  HTTPClient::setUserAgent(const std::string& userAgent)
00342 {
00343         _userAgent = userAgent;
00344 }
00345 
00346 void
00347 HTTPClient::readOptions( const osgDB::ReaderWriter::Options* options, std::string& proxy_host, std::string& proxy_port) const
00348 {
00349     // try to set proxy host/port by reading the CURL proxy options
00350     if ( options )
00351     {
00352         std::istringstream iss( options->getOptionString() );
00353         std::string opt;
00354         while( iss >> opt )
00355         {
00356             int index = opt.find( "=" );
00357             if( opt.substr( 0, index ) == "OSG_CURL_PROXY" )
00358             {
00359                 proxy_host = opt.substr( index+1 );
00360             }
00361             else if ( opt.substr( 0, index ) == "OSG_CURL_PROXYPORT" )
00362             {
00363                 proxy_port = opt.substr( index+1 );
00364             }
00365         }
00366     }
00367 }
00368 
00369 // from: http://www.rosettacode.org/wiki/Tokenizing_A_String#C.2B.2B
00370 static std::vector<std::string> 
00371 tokenize_str(const std::string & str, const std::string & delims=", \t")
00372 {
00373   using namespace std;
00374   // Skip delims at beginning, find start of first token
00375   string::size_type lastPos = str.find_first_not_of(delims, 0);
00376   // Find next delimiter @ end of token
00377   string::size_type pos     = str.find_first_of(delims, lastPos);
00378 
00379   // output vector
00380   vector<string> tokens;
00381 
00382   while (string::npos != pos || string::npos != lastPos)
00383     {
00384       // Found a token, add it to the vector.
00385       tokens.push_back(str.substr(lastPos, pos - lastPos));
00386       // Skip delims.  Note the "not_of". this is beginning of token
00387       lastPos = str.find_first_not_of(delims, pos);
00388       // Find next delimiter at end of token.
00389       pos     = str.find_first_of(delims, lastPos);
00390     }
00391 
00392   return tokens;
00393 }
00394 
00395 
00396 void
00397 HTTPClient::decodeMultipartStream(const std::string&   boundary,
00398                                   HTTPResponse::Part*  input,
00399                                   HTTPResponse::Parts& output) const
00400 {
00401     std::string bstr = std::string("--") + boundary;
00402     std::string line;
00403     char tempbuf[256];
00404 
00405     // first thing in the stream should be the boundary.
00406     input->_stream.read( tempbuf, bstr.length() );
00407     tempbuf[bstr.length()] = 0;
00408     line = tempbuf;
00409     if ( line != bstr )
00410     {
00411         OE_WARN << LC 
00412             << "decodeMultipartStream: protocol violation; "
00413             << "expecting boundary; instead got: \"" 
00414             << line
00415             << "\"" << std::endl;
00416         return;
00417     }
00418 
00419     for( bool done=false; !done; )
00420     {
00421         osg::ref_ptr<HTTPResponse::Part> next_part = new HTTPResponse::Part();
00422 
00423         // first finish off the boundary.
00424         std::getline( input->_stream, line );
00425         if ( line == "--" )
00426         {
00427             done = true;
00428         }
00429         else
00430         {
00431             // read all headers. this ends with a blank line.
00432             line = " ";
00433             while( line.length() > 0 && !done )
00434             {
00435                 std::getline( input->_stream, line );
00436 
00437                 // check for EOS:
00438                 if ( line == "--" )
00439                 {
00440                     done = true;
00441                 }
00442                 else
00443                 {
00444                     std::vector<std::string> tized = tokenize_str( line, ":" );
00445                     if ( tized.size() >= 2 )
00446                         next_part->_headers[tized[0]] = tized[1];
00447                 }
00448             }
00449         }
00450 
00451         if ( !done )
00452         {
00453             // read data until we reach the boundary
00454             unsigned int bstr_ptr = 0;
00455             std::string temp;
00456             //unsigned int c = 0;
00457             while( bstr_ptr < bstr.length() )
00458             {
00459                 char b;
00460                 input->_stream.read( &b, 1 );
00461                 if ( b == bstr[bstr_ptr] )
00462                 {
00463                     bstr_ptr++;
00464                 }
00465                 else
00466                 {
00467                     for( unsigned int i=0; i<bstr_ptr; i++ )
00468                     {
00469                         next_part->_stream << bstr[i];
00470                     }
00471                     next_part->_stream << b;
00472                     next_part->_size += bstr_ptr + 1;
00473                     bstr_ptr = 0;
00474                 }
00475             }
00476             output.push_back( next_part.get() );
00477         }
00478     }
00479 }
00480 
00481 HTTPResponse
00482 HTTPClient::get( const HTTPRequest& request,
00483                  const osgDB::ReaderWriter::Options* options,
00484                  ProgressCallback* callback)
00485 {
00486     return getClient().doGet( request, options, callback );
00487 }
00488 
00489 HTTPResponse 
00490 HTTPClient::get( const std::string &url,
00491                  const osgDB::ReaderWriter::Options* options,
00492                  ProgressCallback* callback)
00493 {
00494     return getClient().doGet( url, options, callback);
00495 }
00496 
00497 HTTPClient::ResultCode
00498 HTTPClient::readImageFile(const std::string &filename,
00499                           osg::ref_ptr<osg::Image>& output,
00500                           const osgDB::ReaderWriter::Options *options,
00501                           osgEarth::ProgressCallback *callback)
00502 {
00503     return getClient().doReadImageFile( filename, output, options, callback );
00504 }
00505 
00506 HTTPClient::ResultCode
00507 HTTPClient::readNodeFile(const std::string& filename,
00508                          osg::ref_ptr<osg::Node>& output,
00509                          const osgDB::ReaderWriter::Options *options,
00510                          osgEarth::ProgressCallback *callback)
00511 {
00512     return getClient().doReadNodeFile( filename, output, options, callback );
00513 }
00514 
00515 HTTPClient::ResultCode
00516 HTTPClient::readObjectFile(const std::string&                  url,
00517                            osg::ref_ptr<osg::Object>&          output,
00518                            const osgDB::ReaderWriter::Options* options,
00519                            ProgressCallback*                   callback )
00520 {
00521     return getClient().doReadObjectFile( url, output, options, callback );
00522 }
00523 
00524 HTTPClient::ResultCode
00525 HTTPClient::readString(const std::string& filename,
00526                        std::string& output,
00527                        osgEarth::ProgressCallback* callback)
00528 {
00529     return getClient().doReadString( filename, output, callback );
00530 }
00531 
00532 bool
00533 HTTPClient::download(const std::string& uri,
00534                      const std::string& localPath)
00535 {
00536     return getClient().doDownload( uri, localPath );
00537 }
00538 
00539 HTTPResponse
00540 HTTPClient::doGet( const HTTPRequest& request, const osgDB::ReaderWriter::Options* options, ProgressCallback* callback) const
00541 {
00542     OE_DEBUG << LC << "doGet " << request.getURL() << std::endl;
00543 
00544     const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ? 
00545             options->getAuthenticationMap() :
00546             osgDB::Registry::instance()->getAuthenticationMap();
00547 
00548     std::string proxy_host;
00549     std::string proxy_port = "8080";
00550 
00551         std::string proxy_auth;
00552 
00553         //Try to get the proxy settings from the global settings
00554         if (_proxySettings.isSet())
00555         {
00556                 proxy_host = _proxySettings.get().hostName();
00557                 std::stringstream buf;
00558                 buf << _proxySettings.get().port();
00559                 proxy_port = buf.str();
00560 
00561                 std::string proxy_username = _proxySettings.get().userName();
00562                 std::string proxy_password = _proxySettings.get().password();
00563                 if (!proxy_username.empty() && !proxy_password.empty())
00564                 {
00565                         proxy_auth = proxy_username + ":" + proxy_password;
00566                 }
00567         }
00568 
00569         //Try to get the proxy settings from the local options that are passed in.
00570     readOptions( options, proxy_host, proxy_port );
00571 
00572         //Try to get the proxy settings from the environment variable
00573     const char* proxyEnvAddress = getenv("OSG_CURL_PROXY");
00574     if (proxyEnvAddress) //Env Proxy Settings
00575     {
00576                 proxy_host = std::string(proxyEnvAddress);
00577 
00578         const char* proxyEnvPort = getenv("OSG_CURL_PROXYPORT"); //Searching Proxy Port on Env
00579                 if (proxyEnvPort)
00580                 {
00581                         proxy_port = std::string( proxyEnvPort );
00582                 }
00583     }
00584 
00585         const char* proxyEnvAuth = getenv("OSGEARTH_CURL_PROXYAUTH");   
00586         if (proxyEnvAuth)
00587         {
00588                 proxy_auth = std::string(proxyEnvAuth);
00589         }
00590 
00591     // Set up proxy server:
00592     std::string proxy_addr;
00593     if ( !proxy_host.empty() )
00594     {
00595         std::stringstream buf;
00596         buf << proxy_host << ":" << proxy_port;
00597                 std::string bufStr;
00598                 bufStr = buf.str();
00599         proxy_addr = bufStr;
00600     
00601         OE_DEBUG << LC << "setting proxy: " << proxy_addr << std::endl;
00602                 //curl_easy_setopt( _curl_handle, CURLOPT_HTTPPROXYTUNNEL, 1 ); 
00603         curl_easy_setopt( _curl_handle, CURLOPT_PROXY, proxy_addr.c_str() );
00604 
00605                 //Setup the proxy authentication if setup
00606                 if (!proxy_auth.empty())
00607                 {
00608                         OE_DEBUG << LC << "Setting up proxy authentication " << proxy_auth << std::endl;
00609                         curl_easy_setopt( _curl_handle, CURLOPT_PROXYUSERPWD, proxy_auth.c_str());
00610                 }
00611     }
00612 
00613     const osgDB::AuthenticationDetails* details = authenticationMap ?
00614         authenticationMap->getAuthenticationDetails(request.getURL()) :
00615         0;
00616 
00617         if (details)
00618         {
00619             const std::string colon(":");
00620             std::string password(details->username + colon + details->password);
00621             curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, password.c_str());
00622             const_cast<HTTPClient*>(this)->_previousPassword = password;
00623 
00624             // use for https.
00625             // curl_easy_setopt(_curl, CURLOPT_KEYPASSWD, password.c_str());
00626 
00627 #if LIBCURL_VERSION_NUM >= 0x070a07
00628             if (details->httpAuthentication != _previousHttpAuthentication)
00629             { 
00630                 curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, details->httpAuthentication); 
00631                 const_cast<HTTPClient*>(this)->_previousHttpAuthentication = details->httpAuthentication;
00632             }
00633 #endif
00634     }
00635     else
00636     {
00637         if (!_previousPassword.empty())
00638         {
00639             curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, 0);
00640             const_cast<HTTPClient*>(this)->_previousPassword.clear();
00641         }
00642 
00643 #if LIBCURL_VERSION_NUM >= 0x070a07
00644         // need to reset if previously set.
00645         if (_previousHttpAuthentication!=0)
00646         {
00647             curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, 0); 
00648             const_cast<HTTPClient*>(this)->_previousHttpAuthentication = 0;
00649         }
00650 #endif
00651     }
00652 
00653     osg::ref_ptr<HTTPResponse::Part> part = new HTTPResponse::Part();
00654     StreamObject sp( &part->_stream );
00655 
00656     //Take a temporary ref to the callback
00657     osg::ref_ptr<ProgressCallback> progressCallback = callback;
00658     curl_easy_setopt( _curl_handle, CURLOPT_URL, request.getURL().c_str() );
00659     if (callback)
00660     {
00661         curl_easy_setopt(_curl_handle, CURLOPT_PROGRESSDATA, progressCallback.get());
00662     }
00663 
00664     char errorBuf[CURL_ERROR_SIZE];
00665     errorBuf[0] = 0;
00666     curl_easy_setopt( _curl_handle, CURLOPT_ERRORBUFFER, (void*)errorBuf );
00667 
00668     curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)&sp);
00669     CURLcode res = curl_easy_perform( _curl_handle );
00670     curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)0 );
00671     curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSDATA, (void*)0);
00672 
00673     long response_code = 0L;
00674         if (!proxy_addr.empty())
00675         {
00676                 long connect_code = 0L;
00677         curl_easy_getinfo( _curl_handle, CURLINFO_HTTP_CONNECTCODE, &connect_code );
00678                 OE_DEBUG << LC << "proxy connect code " << connect_code << std::endl;
00679         }
00680         
00681     curl_easy_getinfo( _curl_handle, CURLINFO_RESPONSE_CODE, &response_code );     
00682 
00683         OE_DEBUG << LC << "got response, code = " << response_code << std::endl;
00684 
00685     HTTPResponse response( response_code );
00686    
00687     if ( response_code == 200L && res != CURLE_ABORTED_BY_CALLBACK && res != CURLE_OPERATION_TIMEDOUT ) //res == 0 )
00688     {
00689         // check for multipart content:
00690         char* content_type_cp;
00691         curl_easy_getinfo( _curl_handle, CURLINFO_CONTENT_TYPE, &content_type_cp );
00692         if ( content_type_cp == NULL )
00693         {
00694             OE_NOTICE << LC
00695                 << "NULL Content-Type (protocol violation) " 
00696                 << "URL=" << request.getURL() << std::endl;
00697             return NULL;
00698         }
00699 
00700         // NOTE:
00701         //   WCS 1.1 specified a "multipart/mixed" response, but ArcGIS Server gives a "multipart/related"
00702         //   content type ...
00703 
00704         std::string content_type( content_type_cp );
00705         //OE_NOTICE << "[osgEarth.HTTPClient] content-type = \"" << content_type << "\"" << std::endl;
00706         if ( content_type.length() > 9 && ::strstr( content_type.c_str(), "multipart" ) == content_type.c_str() )
00707         //if ( content_type == "multipart/mixed; boundary=wcs" ) //todo: parse this.
00708         {
00709             //OE_NOTICE << "[osgEarth.HTTPClient] detected multipart data; decoding..." << std::endl;
00710             //TODO: parse out the "wcs" -- this is WCS-specific
00711             decodeMultipartStream( "wcs", part.get(), response._parts );
00712         }
00713         else
00714         {
00715             //OE_NOTICE << "[osgEarth.HTTPClient] detected single part data" << std::endl;
00716             response._parts.push_back( part.get() );
00717         }
00718     }
00719     else if (res == CURLE_ABORTED_BY_CALLBACK || res == CURLE_OPERATION_TIMEDOUT)
00720     {
00721         //If we were aborted by a callback, then it was cancelled by a user
00722         response._cancelled = true;
00723     }
00724     else
00725     {        
00726         //if ( callback )
00727         //{
00728         //    if ( errorBuf[0] ) {
00729         //        callback->message() = errorBuf;
00730         //    }
00731         //    else {
00732         //        std::stringstream buf;
00733         //        buf << "HTTP Code " << response.getCode();
00734         //        callback->message() = buf.str();
00735         //    }
00736         //}
00737         //else {
00738         //    OE_NOTICE << "[osgEarth] [HTTP] error, code = " << code << std::endl;
00739         //}
00740     }
00741 
00742     // Store the mime-type, if any. (Note: CURL manages the buffer returned by
00743     // this call.)
00744     char* ctbuf = NULL;
00745     if ( curl_easy_getinfo(_curl_handle, CURLINFO_CONTENT_TYPE, &ctbuf) == 0 && ctbuf )
00746     {
00747         response._mimeType = ctbuf;
00748     }
00749 
00750     return response;
00751 }
00752 
00753 
00754 HTTPResponse
00755 HTTPClient::doGet( const std::string& url, const osgDB::ReaderWriter::Options* options, ProgressCallback* callback) const
00756 {
00757     return doGet( HTTPRequest( url ), options, callback );
00758 }
00759 
00760 bool
00761 HTTPClient::doDownload(const std::string &url, const std::string &filename)
00762 {
00763     // download the data
00764     HTTPResponse response = this->doGet( HTTPRequest(url) );
00765 
00766     if ( response.isOK() )
00767     {
00768         unsigned int part_num = response.getNumParts() > 1? 1 : 0;
00769         std::istream& input_stream = response.getPartStream( part_num );
00770 
00771         std::ofstream fout;
00772         fout.open(filename.c_str(), std::ios::out | std::ios::binary);
00773 
00774         input_stream.seekg (0, std::ios::end);
00775         int length = input_stream.tellg();
00776         input_stream.seekg (0, std::ios::beg);
00777 
00778         char *buffer = new char[length];
00779         input_stream.read(buffer, length);
00780         fout.write(buffer, length);
00781         delete[] buffer;
00782         fout.close();
00783         return true;
00784     }
00785     else
00786     {
00787         OE_WARN << LC << "Error downloading file " << filename << std::endl;
00788         return false;
00789     } 
00790 }
00791 
00792 namespace
00793 {
00794     osgDB::ReaderWriter*
00795     getReader( const std::string& url, const HTTPResponse& response )
00796     {
00797         osgDB::ReaderWriter* reader = 0L;
00798 
00799         // try to look up a reader by mime-type first:
00800         std::string mimeType = response.getMimeType();
00801         if ( !mimeType.empty() )
00802         {
00803             reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType);
00804         }
00805 
00806         if ( !reader )
00807         {
00808             // Try to find a reader by file extension.
00809             std::string ext = osgDB::getFileExtension( url );
00810             reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext );
00811         }
00812 
00813         return reader;
00814     }
00815 }
00816 
00817 HTTPClient::ResultCode
00818 HTTPClient::doReadImageFile(const std::string& filename, 
00819                             osg::ref_ptr<osg::Image>& output,
00820                             const osgDB::ReaderWriter::Options *options,
00821                             osgEarth::ProgressCallback *callback)
00822 {
00823     ResultCode result = RESULT_OK;
00824 
00825     if ( osgDB::containsServerAddress( filename ) )
00826     {
00827         HTTPResponse response = this->doGet(filename, options, callback);
00828 
00829         if (response.isOK())
00830         {
00831             osgDB::ReaderWriter* reader = getReader(filename, response);
00832             if (!reader)
00833             {
00834                 OE_WARN << LC << "Can't find an OSG plugin to read "<<filename<<std::endl;
00835                 result = RESULT_NO_READER;
00836             }
00837 
00838             else 
00839             {
00840                 osgDB::ReaderWriter::ReadResult rr = reader->readImage(response.getPartStream(0), options);
00841                 if ( rr.validImage() )
00842                 {
00843                     output = rr.takeImage();
00844                 }
00845                 else 
00846                 {
00847                     if ( !rr.message().empty() )
00848                     {
00849                         OE_WARN << LC << "HTTP error: " << rr.message() << std::endl;
00850                     }
00851                     OE_WARN << LC << reader->className() << " failed to read image from " << filename << std::endl;
00852                     result = RESULT_READER_ERROR;
00853                 }
00854             }
00855         }
00856         else
00857         {
00858             result =
00859                 response.isCancelled() ? RESULT_CANCELED :
00860                 response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
00861                 response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
00862                 RESULT_UNKNOWN_ERROR;
00863 
00864             //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
00865             if (HTTPClient::isRecoverable( result ) )
00866             {
00867                 if (callback)
00868                 {
00869                     OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
00870                     callback->setNeedsRetry( true );
00871                 }
00872             }
00873 
00874             //if ( response.isCancelled() )
00875             //    OE_NOTICE << "HTTP cancel: " << filename << std::endl;
00876             //else
00877             //    OE_NOTICE << "HTTP ERROR " << response.getCode() << ": " << filename << std::endl;
00878 
00879             /*if (response.isCancelled())
00880                 OE_NOTICE << "Request for " << filename << " was cancelled " << std::endl;*/
00881         }
00882     }
00883     else
00884     {
00885         output = osgDB::readImageFile( filename, options );
00886         if ( !output.valid() )
00887             result = RESULT_NOT_FOUND;
00888     }
00889 
00890     return result;
00891 }
00892 
00893 HTTPClient::ResultCode
00894 HTTPClient::doReadNodeFile(const std::string& filename,
00895                            osg::ref_ptr<osg::Node>& output,
00896                            const osgDB::ReaderWriter::Options *options,
00897                            osgEarth::ProgressCallback *callback)
00898 {
00899     ResultCode result = RESULT_OK;
00900 
00901     if ( osgDB::containsServerAddress( filename ) )
00902     {
00903         HTTPResponse response = this->doGet(filename, options, callback);
00904         if (response.isOK())
00905         {
00906             osgDB::ReaderWriter* reader = getReader(filename, response);
00907             if (!reader)
00908             {
00909                 OE_NOTICE<<LC<<"Error: No ReaderWriter for file "<<filename<<std::endl;
00910                 result = RESULT_NO_READER;
00911             }
00912 
00913             else
00914             {
00915                 osgDB::ReaderWriter::ReadResult rr = reader->readNode(response.getPartStream(0), options);
00916                 if ( rr.validNode() )
00917                 {
00918                     output = rr.takeNode();
00919                 }
00920                 else
00921                 {
00922                     if ( rr.error() )
00923                     {
00924                         OE_WARN << LC << "HTTP Reader Error: " << rr.message() << std::endl;
00925                     }
00926                     result = RESULT_READER_ERROR;
00927                 }
00928             }
00929         }
00930         else
00931         {
00932             result =
00933                 response.isCancelled() ? RESULT_CANCELED :
00934                 response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
00935                 response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
00936                 RESULT_UNKNOWN_ERROR;
00937 
00938             //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
00939             if (HTTPClient::isRecoverable( result ) )
00940             {
00941                 if (callback)
00942                 {
00943                     OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
00944                     callback->setNeedsRetry( true );
00945                 }
00946             }
00947                
00948             /*if (response.isCancelled())
00949                 OE_NOTICE << "Request for " << filename << " was cancelled " << std::endl;*/
00950         }
00951     }
00952     else
00953     {
00954         output = osgDB::readNodeFile( filename, options );
00955         if ( !output.valid() )
00956             result = RESULT_NOT_FOUND;
00957     }
00958 
00959     return result;
00960 }
00961 
00962 HTTPClient::ResultCode
00963 HTTPClient::doReadObjectFile(const std::string&                  url,
00964                              osg::ref_ptr<osg::Object>&          output,
00965                              const osgDB::ReaderWriter::Options* options,
00966                              osgEarth::ProgressCallback*         callback)
00967 {
00968     ResultCode result = RESULT_OK;
00969 
00970     if ( osgDB::containsServerAddress( url ) )
00971     {
00972         HTTPResponse response = this->doGet(url, options, callback);
00973         if ( response.isOK() )
00974         {
00975             osgDB::ReaderWriter* reader = getReader( url, response );
00976             if ( !reader )
00977             {
00978                 OE_WARN << LC << "Error: No ReaderWriter for file " << url << std::endl;
00979                 result = RESULT_NO_READER;
00980             }
00981             else
00982             {
00983                 osgDB::ReaderWriter::ReadResult rr = reader->readObject( response.getPartStream(0), options );
00984                 if ( rr.validNode() )
00985                 {
00986                     output = rr.takeObject();
00987                 }
00988                 else
00989                 {
00990                     if ( rr.error() )
00991                     {
00992                         OE_WARN << LC << "HTTP Reader Error: " << rr.message() << std::endl;
00993                     }
00994                     result = RESULT_READER_ERROR;
00995                 }
00996             }
00997         }
00998         else
00999         {
01000             result =
01001                 response.isCancelled() ? RESULT_CANCELED :
01002                 response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
01003                 response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
01004                 RESULT_UNKNOWN_ERROR;
01005 
01006             //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
01007             if (HTTPClient::isRecoverable( result ) )
01008             {
01009                 if (callback)
01010                 {
01011                     OE_DEBUG << "Error in HTTPClient for " << url << " but it's recoverable" << std::endl;
01012                     callback->setNeedsRetry( true );
01013                 }
01014             }
01015         }
01016     }
01017     else
01018     {
01019         output = osgDB::readObjectFile( url, options );
01020         if ( !output.valid() )
01021             result = RESULT_NOT_FOUND;
01022     }
01023 
01024     return result;
01025 }
01026 
01027 
01028 HTTPClient::ResultCode 
01029 HTTPClient::doReadString(const std::string& filename,
01030                          std::string& output,
01031                          osgEarth::ProgressCallback* callback )
01032 {
01033     ResultCode result = RESULT_OK;
01034 
01035     if ( osgDB::containsServerAddress( filename ) )
01036     {
01037         HTTPResponse response = this->doGet( filename, NULL, callback );
01038         if ( response.isOK() )
01039         {
01040             output = response.getPartAsString( 0 );
01041         }
01042         else
01043         {
01044             result =
01045                 response.isCancelled() ? RESULT_CANCELED :
01046                 response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND :
01047                 response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR :
01048                 RESULT_UNKNOWN_ERROR;
01049 
01050             //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
01051             if (HTTPClient::isRecoverable( result ) )
01052             {
01053                 if (callback)
01054                 {
01055                     OE_DEBUG << "Error in HTTPClient for " << filename << " but it's recoverable" << std::endl;
01056                     callback->setNeedsRetry( true );
01057                 }
01058             }
01059         }
01060     }
01061     else
01062     {
01063         std::ifstream input( filename.c_str() );
01064         if ( input.is_open() )
01065         {
01066             input >> std::noskipws;
01067             std::stringstream buf;
01068             buf << input.rdbuf();
01069                         std::string bufStr;
01070                     bufStr = buf.str();
01071             output = bufStr;
01072         }
01073         else
01074         {
01075             result = RESULT_NOT_FOUND;
01076         }
01077     }
01078 
01079     return result;
01080 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines