panda3d.exe should check mirrors too

This commit is contained in:
David Rose 2009-10-07 22:25:03 +00:00
parent 47b9e90b4c
commit 6c6f498ebb
11 changed files with 330 additions and 102 deletions

View File

@ -2700,15 +2700,11 @@ class Packager:
xcontents = doc.FirstChildElement('contents')
if xcontents:
xhost = xcontents.FirstChildElement('host')
while xhost:
if xhost:
he = self.HostEntry()
he.loadXml(xhost, self)
self.hosts[he.url] = he
xhost = xhost.NextSiblingElement('host')
host = xcontents.Attribute('host')
if host:
self.host = host
self.host = he.url
xpackage = xcontents.FirstChildElement('package')
while xpackage:
@ -2736,7 +2732,6 @@ class Packager:
xcontents = TiXmlElement('contents')
if self.host:
xcontents.SetAttribute('host', self.host)
he = self.hosts.get(self.host, None)
if he:
xhost = he.makeXml(packager = self)

View File

@ -125,7 +125,7 @@ static void unload_dso();
////////////////////////////////////////////////////////////////////
bool
load_plugin(const string &p3d_plugin_filename,
const string &contents_filename, const string &download_url,
const string &contents_filename, const string &host_url,
bool verify_contents, const string &platform,
const string &log_directory, const string &log_basename,
bool trusted_environment, bool console_environment,
@ -311,7 +311,7 @@ load_plugin(const string &p3d_plugin_filename,
plugin_loaded = true;
if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
download_url.c_str(), verify_contents, platform.c_str(),
host_url.c_str(), verify_contents, platform.c_str(),
log_directory.c_str(), log_basename.c_str(),
trusted_environment, console_environment)) {
// Oops, failure to initialize.

View File

@ -61,7 +61,7 @@ extern P3D_instance_handle_event_func *P3D_instance_handle_event;
string get_plugin_basename();
bool
load_plugin(const string &p3d_plugin_filename,
const string &contents_filename, const string &download_url,
const string &contents_filename, const string &host_url,
bool verify_contents, const string &platform,
const string &log_directory, const string &log_basename,
bool trusted_environment, bool console_environment,

View File

@ -169,7 +169,7 @@ P3DInstanceManager::
// redownloaded.
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
initialize(const string &contents_filename, const string &download_url,
initialize(const string &contents_filename, const string &host_url,
bool verify_contents,
const string &platform, const string &log_directory,
const string &log_basename, bool trusted_environment,
@ -312,7 +312,7 @@ initialize(const string &contents_filename, const string &download_url,
<< ", _temp_directory = " << _temp_directory
<< ", platform = " << _platform
<< ", contents_filename = " << contents_filename
<< ", download_url = " << download_url
<< ", host_url = " << host_url
<< ", verify_contents = " << verify_contents
<< "\n";
@ -330,10 +330,10 @@ initialize(const string &contents_filename, const string &download_url,
_is_initialized = true;
if (!_verify_contents &&
!download_url.empty() && !contents_filename.empty()) {
!host_url.empty() && !contents_filename.empty()) {
// Attempt to pre-read the supplied contents.xml file, to avoid an
// unnecessary download later.
P3DHost *host = get_host(download_url);
P3DHost *host = get_host(host_url);
if (!host->read_contents_file(contents_filename)) {
nout << "Couldn't read " << contents_filename << "\n";
}

View File

@ -50,7 +50,7 @@ private:
public:
bool initialize(const string &contents_filename,
const string &download_url,
const string &host_url,
bool verify_contents,
const string &platform,
const string &log_directory,

View File

@ -35,7 +35,7 @@ LOCK _api_lock;
bool
P3D_initialize(int api_version, const char *contents_filename,
const char *download_url, bool verify_contents,
const char *host_url, bool verify_contents,
const char *platform,
const char *log_directory, const char *log_basename,
bool trusted_environment, bool console_environment) {
@ -54,8 +54,8 @@ P3D_initialize(int api_version, const char *contents_filename,
contents_filename = "";
}
if (download_url == NULL){
download_url = "";
if (host_url == NULL){
host_url = "";
}
if (platform == NULL) {
@ -71,7 +71,7 @@ P3D_initialize(int api_version, const char *contents_filename,
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
bool result = inst_mgr->initialize(contents_filename, download_url,
bool result = inst_mgr->initialize(contents_filename, host_url,
verify_contents, platform,
log_directory, log_basename,
trusted_environment, console_environment);

View File

@ -95,7 +95,7 @@ extern "C" {
file that has already been downloaded and verified from the server.
If this is NULL, a new file will be downloaded as needed.
If download_url is not NULL or empty, it specifies the root URL of
If host_url is not NULL or empty, it specifies the root URL of
the download server that provided the contents_filename.
If verify_contents is true, it means that the download server will
@ -141,7 +141,7 @@ extern "C" {
immediately unload the DLL and (if possible) download a new one. */
typedef bool
P3D_initialize_func(int api_version, const char *contents_filename,
const char *download_url, bool verify_contents,
const char *host_url, bool verify_contents,
const char *platform,
const char *log_directory, const char *log_basename,
bool trusted_environment, bool console_environment);

View File

@ -686,6 +686,43 @@ variant_to_p3dobj(const NPVariant *variant) {
return P3D_new_none_object();
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::find_host
// Access: Private
// Description: Scans the <contents> element for the matching <host>
// element.
////////////////////////////////////////////////////////////////////
void PPInstance::
find_host(TiXmlElement *xcontents) {
string host_url = PANDA_PACKAGE_HOST_URL;
TiXmlElement *xhost = xcontents->FirstChildElement("host");
if (xhost != NULL) {
const char *url = xhost->Attribute("url");
if (url != NULL && host_url == string(url)) {
// We're the primary host. This is the normal case.
read_xhost(xhost);
return;
} else {
// We're not the primary host; perhaps we're an alternate host.
TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
while (xalthost != NULL) {
const char *url = xalthost->Attribute("url");
if (url != NULL && host_url == string(url)) {
// Yep, we're this alternate host.
read_xhost(xhost);
return;
}
xalthost = xalthost->NextSiblingElement("alt_host");
}
}
// Hmm, didn't find the URL we used mentioned. Assume we're the
// primary host.
read_xhost(xhost);
}
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::read_xhost
// Access: Private
@ -841,28 +878,7 @@ read_contents_file(const string &contents_filename) {
if (xcontents != NULL) {
// Look for the <host> entry; it might point us at a different
// download URL, and it might mention some mirrors.
string host_url = PANDA_PACKAGE_HOST_URL;
TiXmlElement *xhost = xcontents->FirstChildElement("host");
if (xhost != NULL) {
const char *url = xhost->Attribute("url");
if (url != NULL && host_url == string(url)) {
// We're the primary host. This is the normal case.
read_xhost(xhost);
} else {
// We're not the primary host; perhaps we're an alternate host.
TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
while (xalthost != NULL) {
const char *url = xalthost->Attribute("url");
if (url != NULL && host_url == string(url)) {
// Yep, we're this alternate host.
read_xhost(xhost);
break;
}
xalthost = xalthost->NextSiblingElement("alt_host");
}
}
}
find_host(xcontents);
// Now look for the core API package.
TiXmlElement *xpackage = xcontents->FirstChildElement("package");

View File

@ -64,6 +64,7 @@ public:
static void output_np_variant(ostream &out, const NPVariant &result);
private:
void find_host(TiXmlElement *xcontents);
void read_xhost(TiXmlElement *xhost);
void add_mirror(string mirror_url);
void choose_random_mirrors(vector<string> &result, int num_mirrors);

View File

@ -24,6 +24,7 @@
#include <ctype.h>
#include <sstream>
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
#else
@ -52,6 +53,10 @@ Panda3D() {
_root_dir = find_root_dir();
_reporting_download = false;
_enable_security = false;
// Seed the lame random number generator in rand(); we use it to
// select a mirror for downloading.
srand((unsigned int)time(NULL));
}
////////////////////////////////////////////////////////////////////
@ -71,10 +76,10 @@ run(int argc, char *argv[]) {
const char *optstr = "+mu:M:Sp:fw:t:s:o:l:ih";
bool allow_multiple = false;
string download_url = PANDA_PACKAGE_HOST_URL;
_host_url = PANDA_PACKAGE_HOST_URL;
string super_mirror_url;
string this_platform = DTOOL_PLATFORM;
bool verify_contents = false;
_this_platform = DTOOL_PLATFORM;
_verify_contents = false;
P3D_window_type window_type = P3D_WT_toplevel;
int win_x = 0, win_y = 0;
@ -89,7 +94,7 @@ run(int argc, char *argv[]) {
break;
case 'u':
download_url = optarg;
_host_url = optarg;
break;
case 'M':
@ -101,11 +106,11 @@ run(int argc, char *argv[]) {
break;
case 'p':
this_platform = optarg;
_this_platform = optarg;
break;
case 'f':
verify_contents = true;
_verify_contents = true;
break;
case 'w':
@ -192,10 +197,12 @@ run(int argc, char *argv[]) {
return 1;
}
// Make sure the download URL ends with a slash.
if (!download_url.empty() && download_url[download_url.length() - 1] != '/') {
download_url += '/';
// Set host_url_prefix to end with a slash.
_host_url_prefix = _host_url;
if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.length() - 1] != '/') {
_host_url_prefix += '/';
}
_download_url_prefix = _host_url_prefix;
// If the "super mirror" URL is a filename, convert it to a file:// url.
if (!super_mirror_url.empty()) {
@ -210,9 +217,15 @@ run(int argc, char *argv[]) {
}
super_mirror_url = "file://" + path;
}
// And make sure the super_mirror_url_prefix ends with a slash.
_super_mirror_url_prefix = super_mirror_url;
if (!_super_mirror_url_prefix.empty() && _super_mirror_url_prefix[_super_mirror_url_prefix.length() - 1] != '/') {
_super_mirror_url_prefix += '/';
}
}
if (!get_plugin(download_url, this_platform, verify_contents)) {
if (!get_plugin()) {
cerr << "Unable to load Panda3D plugin.\n";
return 1;
}
@ -403,18 +416,48 @@ run(int argc, char *argv[]) {
// true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool Panda3D::
get_plugin(const string &download_url, const string &this_platform,
bool verify_contents) {
get_plugin() {
// First, look for the existing contents.xml file.
Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
if (!verify_contents && read_contents_file(contents_filename, download_url, this_platform, verify_contents)) {
if (!_verify_contents && read_contents_file(contents_filename)) {
// Got the file, and it's good.
return true;
}
// Couldn't read it, so go get it.
HTTPClient *http = HTTPClient::get_global_ptr();
// Try the super_mirror first.
if (!_super_mirror_url_prefix.empty()) {
// We don't bother putting a uniquifying query string when we're
// downloading this file from the super_mirror. The super_mirror
// is by definition a cache, so it doesn't make sense to bust
// caches here.
string url = _super_mirror_url_prefix + "contents.xml";
PT(HTTPChannel) channel = http->make_channel(false);
channel->get_document(url);
Filename tempfile = Filename::temporary("", "p3d_");
if (!channel->download_to_file(tempfile)) {
cerr << "Unable to download " << url << "\n";
tempfile.unlink();
} else {
// Successfully downloaded from the super_mirror; move it into
// place and try to read it.
contents_filename.make_dir();
contents_filename.unlink();
tempfile.rename_to(contents_filename);
if (read_contents_file(contents_filename)) {
return true;
}
}
// Failed to download from the super_mirror.
}
// Go download contents.xml from the actual host.
ostringstream strm;
strm << download_url << "contents.xml";
strm << _host_url_prefix << "contents.xml";
// Append a uniquifying query string to the URL to force the
// download to go all the way through any caches. We use the time
// in seconds; that's unique enough.
@ -425,8 +468,7 @@ get_plugin(const string &download_url, const string &this_platform,
// since we have an interface for that via HTTPChannel.
DocumentSpec request(url);
request.set_cache_control(DocumentSpec::CC_no_cache);
HTTPClient *http = HTTPClient::get_global_ptr();
PT(HTTPChannel) channel = http->make_channel(false);
channel->get_document(request);
@ -447,9 +489,9 @@ get_plugin(const string &download_url, const string &this_platform,
// Since we had to download some of it, might as well ask the core
// API to check all of it.
verify_contents = true;
_verify_contents = true;
return read_contents_file(contents_filename, download_url, this_platform, verify_contents);
return read_contents_file(contents_filename);
}
////////////////////////////////////////////////////////////////////
@ -460,40 +502,158 @@ get_plugin(const string &download_url, const string &this_platform,
// possible. Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool Panda3D::
read_contents_file(Filename contents_filename, const string &download_url,
const string &this_platform, bool verify_contents) {
ifstream in;
contents_filename.set_text();
if (!contents_filename.open_read(in)) {
read_contents_file(const Filename &contents_filename) {
string os_contents_filename = contents_filename.to_os_specific();
TiXmlDocument doc(os_contents_filename.c_str());
if (!doc.LoadFile()) {
return false;
}
TiXmlDocument doc;
in >> doc;
TiXmlElement *xcontents = doc.FirstChildElement("contents");
if (xcontents != NULL) {
// Look for the <host> entry; it might point us at a different
// download URL, and it might mention some mirrors.
find_host(xcontents);
// Now look for the core API package.
TiXmlElement *xpackage = xcontents->FirstChildElement("package");
while (xpackage != NULL) {
const char *name = xpackage->Attribute("name");
if (name != NULL && strcmp(name, "coreapi") == 0) {
const char *xplatform = xpackage->Attribute("platform");
if (xplatform != NULL && strcmp(xplatform, this_platform.c_str()) == 0) {
return get_core_api(contents_filename, download_url,
this_platform, verify_contents, xpackage);
const char *platform = xpackage->Attribute("platform");
if (platform != NULL && _this_platform == string(platform)) {
return get_core_api(contents_filename, xpackage);
}
}
xpackage = xpackage->NextSiblingElement("package");
}
}
// Couldn't find the coreapi plugin description.
cerr << "No coreapi plugin defined in contents file for "
<< this_platform << "\n";
// Couldn't find the coreapi package description.
nout << "No coreapi package defined in contents file for "
<< _this_platform << "\n";
return false;
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::find_host
// Access: Private
// Description: Scans the <contents> element for the matching <host>
// element.
////////////////////////////////////////////////////////////////////
void Panda3D::
find_host(TiXmlElement *xcontents) {
TiXmlElement *xhost = xcontents->FirstChildElement("host");
if (xhost != NULL) {
const char *url = xhost->Attribute("url");
if (url != NULL && _host_url == string(url)) {
// We're the primary host. This is the normal case.
read_xhost(xhost);
return;
} else {
// We're not the primary host; perhaps we're an alternate host.
TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
while (xalthost != NULL) {
const char *url = xalthost->Attribute("url");
if (url != NULL && _host_url == string(url)) {
// Yep, we're this alternate host.
read_xhost(xhost);
return;
}
xalthost = xalthost->NextSiblingElement("alt_host");
}
}
// Hmm, didn't find the URL we used mentioned. Assume we're the
// primary host.
read_xhost(xhost);
}
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::read_xhost
// Access: Private
// Description: Reads the host data from the <host> (or <alt_host>)
// entry in the contents.xml file.
////////////////////////////////////////////////////////////////////
void Panda3D::
read_xhost(TiXmlElement *xhost) {
// Get the "download" URL, which is the source from which we
// download everything other than the contents.xml file.
const char *download_url = xhost->Attribute("download_url");
if (download_url == NULL) {
download_url = xhost->Attribute("url");
}
if (download_url != NULL) {
_download_url_prefix = download_url;
} else {
_download_url_prefix = _host_url_prefix;
}
if (!_download_url_prefix.empty()) {
if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') {
_download_url_prefix += "/";
}
}
TiXmlElement *xmirror = xhost->FirstChildElement("mirror");
while (xmirror != NULL) {
const char *url = xmirror->Attribute("url");
if (url != NULL) {
add_mirror(url);
}
xmirror = xmirror->NextSiblingElement("mirror");
}
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::add_mirror
// Access: Private
// Description: Adds a new URL to serve as a mirror for this host.
// The mirrors will be consulted first, before
// consulting the host directly.
////////////////////////////////////////////////////////////////////
void Panda3D::
add_mirror(string mirror_url) {
// Ensure the URL ends in a slash.
if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') {
mirror_url += '/';
}
// Add it to the _mirrors list, but only if it's not already
// there.
if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
_mirrors.push_back(mirror_url);
}
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::choose_random_mirrors
// Access: Public
// Description: Selects num_mirrors elements, chosen at random, from
// the _mirrors list. Adds the selected mirrors to
// result. If there are fewer than num_mirrors elements
// in the list, adds only as many mirrors as we can get.
////////////////////////////////////////////////////////////////////
void Panda3D::
choose_random_mirrors(vector_string &result, int num_mirrors) {
pvector<size_t> selected;
size_t num_to_select = min(_mirrors.size(), (size_t)num_mirrors);
while (num_to_select > 0) {
size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
while (find(selected.begin(), selected.end(), i) != selected.end()) {
// Already found this i, find a new one.
i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
}
selected.push_back(i);
result.push_back(_mirrors[i]);
--num_to_select;
}
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::get_core_api
@ -503,33 +663,79 @@ read_contents_file(Filename contents_filename, const string &download_url,
// if necessary.
////////////////////////////////////////////////////////////////////
bool Panda3D::
get_core_api(const Filename &contents_filename, const string &download_url,
const string &this_platform, bool verify_contents,
TiXmlElement *xpackage) {
get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
_core_api_dll.load_xml(xpackage);
if (!_core_api_dll.quick_verify(_root_dir)) {
// The DLL file needs to be downloaded. Go get it.
string url = download_url;
// The DLL file needs to be downloaded. Build up our list of
// URL's to attempt to download it from, in reverse order.
string url;
vector_string core_urls;
// Our last act of desperation: hit the original host, with a
// query uniquifier, to break through any caches.
ostringstream strm;
strm << _download_url_prefix << _core_api_dll.get_filename()
<< "?" << time(NULL);
url = strm.str();
core_urls.push_back(url);
// Before we try that, we'll hit the original host, without a
// uniquifier.
url = _download_url_prefix;
url += _core_api_dll.get_filename();
Filename pathname = Filename::from_os_specific(_core_api_dll.get_pathname(_root_dir));
HTTPClient *http = HTTPClient::get_global_ptr();
PT(HTTPChannel) channel = http->get_document(url);
pathname.make_dir();
if (!channel->download_to_file(pathname)) {
cerr << "Unable to download " << url << "\n";
return false;
core_urls.push_back(url);
// And before we try that, we'll try two mirrors, at random.
vector_string mirrors;
choose_random_mirrors(mirrors, 2);
for (vector_string::iterator si = mirrors.begin();
si != mirrors.end();
++si) {
url = (*si) + _core_api_dll.get_filename();
core_urls.push_back(url);
}
if (!_core_api_dll.full_verify(_root_dir)) {
cerr << "Mismatched download for " << url << "\n";
// The very first thing we'll try is the super_mirror, if we have
// one.
if (!_super_mirror_url_prefix.empty()) {
url = _super_mirror_url_prefix + _core_api_dll.get_filename();
core_urls.push_back(url);
}
// Now pick URL's off the list, and try them, until we have
// success.
Filename pathname = Filename::from_os_specific(_core_api_dll.get_pathname(_root_dir));
pathname.make_dir();
HTTPClient *http = HTTPClient::get_global_ptr();
bool success = false;
while (!core_urls.empty()) {
url = core_urls.back();
core_urls.pop_back();
PT(HTTPChannel) channel = http->get_document(url);
if (!channel->download_to_file(pathname)) {
cerr << "Unable to download " << url << "\n";
} else if (!_core_api_dll.full_verify(_root_dir)) {
cerr << "Mismatched download for " << url << "\n";
} else {
// successfully downloaded!
success = true;
break;
}
}
if (!success) {
cerr << "Couldn't download core API.\n";
return false;
}
// Since we had to download some of it, might as well ask the core
// API to check all of it.
verify_contents = true;
_verify_contents = true;
}
// Now we've got the DLL. Load it.
@ -556,7 +762,7 @@ get_core_api(const Filename &contents_filename, const string &download_url,
#endif
if (!load_plugin(pathname, contents_filename.to_os_specific(),
download_url, verify_contents, this_platform, _log_dirname,
_host_url, _verify_contents, _this_platform, _log_dirname,
_log_basename, trusted_environment, console_environment,
cerr)) {
cerr << "Unable to launch core API in " << pathname << "\n" << flush;

View File

@ -29,6 +29,7 @@
#include "ramfile.h"
#include "fileSpec.h"
#include "pset.h"
#include "vector_string.h"
////////////////////////////////////////////////////////////////////
// Class : Panda3D
@ -42,14 +43,13 @@ public:
int run(int argc, char *argv[]);
private:
bool get_plugin(const string &download_url, const string &this_platform,
bool verify_contents);
bool read_contents_file(Filename contents_filename,
const string &download_url,
const string &this_platform, bool verify_contents);
bool get_core_api(const Filename &contents_filename,
const string &download_url, const string &this_platform,
bool verify_contents, TiXmlElement *xplugin);
bool get_plugin();
bool read_contents_file(const Filename &contents_filename);
void find_host(TiXmlElement *xcontents);
void read_xhost(TiXmlElement *xhost);
void add_mirror(string mirror_url);
void choose_random_mirrors(vector_string &result, int num_mirrors);
bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
void run_getters();
void handle_request(P3D_request *request);
void make_parent_window(P3D_window_handle &parent_window,
@ -79,6 +79,16 @@ private:
string _root_dir;
string _log_dirname;
string _log_basename;
string _this_platform;
bool _verify_contents;
string _host_url;
string _super_mirror_url_prefix;
string _host_url_prefix;
string _download_url_prefix;
typedef pvector<string> Mirrors;
Mirrors _mirrors;
FileSpec _core_api_dll;
bool _reporting_download;
bool _enable_security;