osgEarth 2.1.1
|
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 }