This patch correct the databasepath assignment from previous version
submitted.
It allow the usign of ProxyNode from http.
Cheers Carlo
#include <iostream>
#include <fstream>
#include <sstream>
#include <osg/Notify>
#include <osgDB/Input>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/FileUtils>
#include <osgDB/Archive>
#include <osg/MatrixTransform>
#include <osg/Group>
#include <osg/Timer>
#include "sockinet.h"
/*
* Semantics:
* Two methods for using the .net loader.
* 1) Add a hostname prefix and a '.net' suffix on a model when passing
* to osgDB::readNodeFile()
* e.g: osgDB::readNodeFile( "openscenegraph.org:cow.osg.net" );
*
* 2) Explicitely load the .net plugin and pass the plugin options
including
* hostname=<hostname>
*
* Method #1 takes precedence. SO, if the hostname option is passed the
* plugin, but the name also contains a hostname prefix, the hostname
* prefix on the file name will override the option
*
* Plugin options:
* root_address=<url> - Specify the web database path for an
iterative
* call of the net plugin
* local_cache_dir=<dir> - Specify a directory in which to cache
files
* on the local machine once they've been
fetched.
* This directory is also searched before
fetching
* the file from the server when Read mode
is
* enabled on the cache.
*
* cache_mode=<mode> - Set the mode for the local cache if
local_cache
* was specified. If local_cache was not
specified
* this directive is ignored. <mode> may
* be specified with ReadOnly, WriteOnly,
or
* ReadWrite. Behavior for the different
modes is
* defined as:
*
* ReadOnly - When retrieving files,
cache is
* searched first, and if
the file is
* not present, it is
fetched from the
* server. If it is
fetched from the
* server it is not stored
in local cache
*
* WriteOnly - When retrieving files,
cache is not
* searched, file is always
retrieved
* from the server and
always written to
* cache.
*
* ReadWrite - (the default). When
retrieving files
* cache is searched first,
if file is
* not present in cache, it
is fetched from
* the server. If fetched,
it is written
* to cache.
*
* proxy_host=<hostname> - Specify the name of a Proxy host.
Proxies are not
* used by default
*
* proxy_port=<portnumber> - When using a Proxy host, specify the
port. The
* internal variable defaults to 8080.
*
* Environmental Variables:
*
* OSG_PROXY_HOST Specifies the name of a Proxy host.
Overrides options.
*
* OSG_PROXY_PORT When using a Proxy, sets the proxy port
deliberately.
* The internal variable defaults to 8080.
*/
class NetReader : public osgDB::ReaderWriter
{
public:
NetReader() {}
virtual const char* className() const { return "HTTP Protocol Model
Reader"; }
virtual bool acceptsExtension(const std::string& extension) const
{
return osgDB::equalCaseInsensitive(extension,"net");
}
enum ObjectType
{
OBJECT,
ARCHIVE,
IMAGE,
HEIGHTFIELD,
NODE
};
virtual ReadResult openArchive(const std::string&
fileName,ArchiveStatus status, unsigned int , const Options* options) const
{
if (status!=READ) return ReadResult(ReadResult::FILE_NOT_HANDLED);
else return readFile(ARCHIVE,fileName,options);
}
virtual ReadResult readObject(const std::string& fileName, const
Options* options) const
{
return readFile(OBJECT,fileName,options);
}
virtual ReadResult readImage(const std::string& fileName, const Options
*options) const
{
return readFile(IMAGE,fileName,options);
}
virtual ReadResult readHeightField(const std::string& fileName, const
Options *options) const
{
return readFile(HEIGHTFIELD,fileName,options);
}
virtual ReadResult readNode(const std::string& fileName, const Options
*options) const
{
return readFile(NODE,fileName,options);
}
ReadResult readFile(ObjectType objectType, osgDB::ReaderWriter* rw,
std::istream& fin, const Options *options) const
{
switch(objectType)
{
case(OBJECT): return rw->readObject(fin,options);
case(ARCHIVE): return rw->openArchive(fin,options);
case(IMAGE): return rw->readImage(fin,options);
case(HEIGHTFIELD): return rw->readHeightField(fin,options);
case(NODE): return rw->readNode(fin,options);
default: break;
}
return ReadResult::FILE_NOT_HANDLED;
}
virtual ReadResult readFile(ObjectType objectType, const std::string&
inFileName, const Options *options) const
{
ReadResult readResult = ReadResult::FILE_NOT_HANDLED;
osg::Timer_t start = osg::Timer::instance()->tick();
osg::notify(osg::DEBUG_INFO) << "osgPlugin .net: readFile " <<
inFileName << std::endl;
std::string hostname;
std::string rootaddress;
std::string relativeaddress;
std::string localCacheDir;
int port = 80;
std::string proxyHost;
int proxyPort = 8080;
enum CacheMode {
Read = 1,
Write = 2,
ReadWrite = 3
};
std::string cacheFile;
CacheMode cacheMode = ReadWrite;
bool set_master_root = false;
bool set_rootaddress_in_localopt = false;
if (options)
{
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
int index = opt.find( "=" );
if( opt.substr( 0, index ) ==
"root_address" ||
opt.substr( 0, index ) == "ROOT_ADDRESS" )
{
rootaddress = opt.substr( index+1 );
set_master_root = true;
}
else if( opt.substr( 0, index ) == "proxy_hostname" ||
opt.substr( 0, index ) == "PROXY_HOSTNAME" )
//CARLO: proxy modf
{
proxyHost = opt.substr(index+1);
osg::notify(osg::WARN) << "proxy host " << proxyHost <<
std::endl;
}
else if( opt.substr( 0, index ) == "proxy_port" ||
opt.substr( 0, index ) == "PROXY_PORT" )
{
proxyPort = atoi( opt.substr(index+1).c_str() );
}
else if( opt.substr( 0, index ) == "local_cache_dir" ||
opt.substr( 0, index ) == "LOCAL_CACHE_DIR" )
{
localCacheDir = opt.substr(index+1);
}
else if( opt.substr( 0, index ) == "cache_mode" ||
opt.substr( 0, index ) == "CACHE_MODE" )
{
if( opt.substr(index+1) == "ReadOnly" )
cacheMode = Read;
else if( opt.substr(index+1) == "WriteOnly" )
cacheMode = Write;
else if( opt.substr(index+1) == "ReadWrite" )
cacheMode = ReadWrite;
else
osg::notify(osg::WARN) << "NET plug-in warning:
cache_mode " << opt.substr(index+1) << " not understood. Defaulting to
ReadWrite." << std::endl;
}
}
}
// Env variables should override plug-in options.
{
char * env_proxyHost = getenv("OSG_PROXY_HOST"); //Checking
proxy environment variables
char * env_proxyPort = getenv("OSG_PROXY_PORT");
if( env_proxyHost )
{
proxyHost = std::string(env_proxyHost);
osg::notify(osg::DEBUG_INFO) <<
"osgPlugin .net reader: Proxy HostName Environment Variable sets " << proxyHost
<< std::endl;
if( env_proxyPort )
{
proxyPort = atoi(std::string(env_proxyPort).c_str());
osg::notify(osg::DEBUG_INFO) <<
"osgPlugin .net reader: Proxy Port Environment Variable sets " << proxyPort <<
std::endl;
}
}
}
//We use the entire address instead the division with ":" marker
//This issue resolve the confusion between window path
and old OSG address subdivision
std::string fileName;
bool saddress =
osgDB::containsServerAddress(inFileName);
if( saddress == true )
{
//Root Address
rootaddress = osgDB::getFilePath( inFileName );
//Relative Address
relativeaddress = osgDB::getSimpleFileName(
inFileName );
// hostname
hostname = osgDB::getServerAddress(inFileName);
// need to strip the inFileName of the hostname prefix
fileName = osgDB::getServerFileName(inFileName);
if(!set_master_root) //If the hostname isn't
setted explicitly
set_rootaddress_in_localopt = true;
//It sets the hostname for the recursive calls
}
else
{
if( rootaddress.empty() )
{
return readResult; //Return ReadResult::FILE_NOT_HANDLED;
}
else
{
//Relative Address
relativeaddress =
osgDB::convertFileNameToUnixStyle(inFileName);
// hostname
hostname =
osgDB::getServerAddress(rootaddress + '/' + relativeaddress);
// need to strip
fileName =
osgDB::getServerFileName(rootaddress + '/' + relativeaddress);
}
}
//Find the Port Number specified in URL
int index = hostname.find_last_of(":");
if( index != -1 )
{
port = atoi( hostname.substr( index + 1 ).c_str() );
hostname = hostname.substr( 0, index );
}
// Let's also strip the possible .net extension
if( osgDB::getFileExtension( fileName ) == "net" )
{
int rindex = fileName.rfind( "." );
fileName = fileName.substr( 0, rindex );
}
// Invoke the reader corresponding to the extension
osgDB::ReaderWriter *reader =
osgDB::Registry::instance()->getReaderWriterForExtension(
osgDB::getFileExtension(fileName) );
if( reader == 0L )
return ReadResult::FILE_NOT_HANDLED;
// code for setting up the database path so that any
paged
// databases can be automatically located.
osg::ref_ptr<Options> local_opt = const_cast<Options*>(options);
if (!local_opt)
local_opt = new Options;
if(set_rootaddress_in_localopt)
{
bool present = false;
std::string loptstr =
local_opt->getOptionString();
loptstr += " ROOT_ADDRESS=" + rootaddress;
local_opt->setOptionString(loptstr);
//Sets databasepath
osgDB::FilePathList dbfplist =
local_opt->getDatabasePathList();
osgDB::FilePathList::iterator itr =
dbfplist.begin();
for( ; itr != dbfplist.end(); itr++)
{
if((*itr) == rootaddress)
present = true;
}
if(!present)
local_opt->setDatabasePath(rootaddress);
}
// Before we go to the network, lets see if it is in local cache,
if cache
// was specified and Read bit is set
if( !localCacheDir.empty() && (cacheMode & Read) )
{
std::string cacheFile =
osgDB::convertFileNameToNativeStyle( localCacheDir + '/' + relativeaddress );
if( osgDB::fileExists( cacheFile ))
{
std::ifstream in(cacheFile.c_str());
readResult = readFile(objectType, reader, in,
local_opt.get() );
in.close();
osg::notify(osg::DEBUG_INFO) << "osgPlugin .net: " <<
cacheFile << " fetched from local cache." << std::endl;
return readResult;
}
}
// Fetch from the network
osg::ref_ptr<iosockinet> sio = new iosockinet(sockbuf::sock_stream);
std::string requestAdd;
if(!proxyHost.empty())
{
try {
sio->rdbuf()->connect( proxyHost.c_str(), proxyPort );
osg::notify(osg::DEBUG_FP) << "osgPlugin .net reader:
connected to Proxy " << proxyHost << " with Port " << proxyPort << std::endl;
}
catch( sockerr e )
{
osg::notify(osg::WARN) << "osgPlugin .net reader: Unable to
connect to Proxy " << proxyHost << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
if(port != 80) //Default HTTP Port
{
std::ostringstream portstream;
portstream << port << std::flush;
requestAdd = std::string("http://") + hostname +
std::string(":") + portstream.str() + std::string("/") + fileName;
}
else
requestAdd = std::string("http://") + hostname +
std::string("/") + fileName;
}
else
{
try {
sio->rdbuf()->connect( hostname.c_str(), port );
osg::notify(osg::DEBUG_FP) << "osgPlugin .net reader:
connected to Hostname " << hostname << " with Port " << port << std::endl;
}
catch( sockerr e )
{
osg::notify(osg::WARN) << "osgPlugin .net reader: Unable to
connect to host " << hostname << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
requestAdd = "/" + fileName;
}
*sio <<
"GET " << requestAdd << " HTTP/1.1\n"
"User-Agent: OSGNetPlugin/1.0\n"
"Accept: */*\n"
"Host: " << hostname << "\n"
"Connection: close\n"
"\n";
sio->flush();
char linebuff[512];
do
{
sio->getline( linebuff, sizeof( linebuff ));
std::istringstream iss(linebuff);
std::string directive;
iss >> directive;
if( directive.substr(0,4) == "HTTP" )
{
iss >> directive;
// Code 200. We be ok.
if( directive == "200" )
;
// Code 400 Bad Request
else if( directive == "400" )
{
osg::notify(osg::WARN) <<
"osgPlugin .net: http server response 400 - Bad
Request" << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
// Code 401 Bad Request
else if( directive == "401" )
{
osg::notify(osg::WARN) <<
"osgPlugin .net: http server response 401 -
Unauthorized Access" << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
// Code 403 Bad Request
else if( directive == "403" )
{
osg::notify(osg::WARN) <<
"osgPlugin .net: http server response 403 - Access
Forbidden" << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
// Code 404 File not found
else if( directive == "404" )
{
osg::notify(osg::WARN) <<
"osgPlugin .net: http server response 404 - File
Not Found" << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
// Code 405 Method not allowed
else if( directive == "405" )
{
osg::notify(osg::WARN) <<
"osgPlugin .net: http server response 405 - Method
Not Allowed" << std::endl;
return ReadResult::FILE_NOT_FOUND;
}
// There's more....
}
} while( linebuff[0] != '\r' );
//Start Reading
if( reader != 0L )
readResult = readFile(objectType, reader, *sio, local_opt.get()
);
double ms =
osg::Timer::instance()->delta_m(start,osg::Timer::instance()->tick());
osg::notify(osg::DEBUG_INFO) << "osgPlugin .net: " << fileName << "
fetched from server. in " << ms <<" ms"<< std::endl;
if (objectType == ARCHIVE && readResult.validArchive())
{
// attach the socket istream to the archive to keep it alive.
osgDB::Archive* archive = readResult.getArchive();
archive->setUserData(sio.get());
}
if( !localCacheDir.empty() && cacheMode & Write )
{
std::string cacheFile = osgDB::convertFileNameToNativeStyle(
localCacheDir + '/' + fileName );
if( osgDB::makeDirectoryForFile( cacheFile ) )
{
switch(objectType)
{
case(OBJECT): osgDB::writeObjectFile(
*(readResult.getObject()), cacheFile );
case(IMAGE): osgDB::writeImageFile(
*(readResult.getImage()), cacheFile );
case(HEIGHTFIELD): osgDB::writeHeightFieldFile(
*(readResult.getHeightField()), cacheFile );
case(NODE): osgDB::writeNodeFile( *(readResult.getNode()),
cacheFile );;
default: break;
}
osg::notify(osg::DEBUG_INFO) << "osgPlugin .net: " <<
fileName <<
" stored to local cache." << std::endl;
}
}
return readResult;
}
};
REGISTER_OSGPLUGIN(net, NetReader)
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org