Ver Fonte

fix caching issues, mirror issues

David Rose há 16 anos atrás
pai
commit
4545421f6e

+ 18 - 6
direct/src/p3d/Packager.py

@@ -208,14 +208,17 @@ class Packager:
             return xpackage
             return xpackage
 
 
     class HostEntry:
     class HostEntry:
-        def __init__(self, url = None, descriptiveName = None, mirrors = None):
+        def __init__(self, url = None, downloadUrl = None,
+                     descriptiveName = None, mirrors = None):
             self.url = url
             self.url = url
+            self.downloadUrl = downloadUrl
             self.descriptiveName = descriptiveName
             self.descriptiveName = descriptiveName
             self.mirrors = mirrors or []
             self.mirrors = mirrors or []
             self.altHosts = {}
             self.altHosts = {}
 
 
         def loadXml(self, xhost, packager):
         def loadXml(self, xhost, packager):
             self.url = xhost.Attribute('url')
             self.url = xhost.Attribute('url')
+            self.downloadUrl = xhost.Attribute('download_url')
             self.descriptiveName = xhost.Attribute('descriptive_name')
             self.descriptiveName = xhost.Attribute('descriptive_name')
             self.mirrors = []
             self.mirrors = []
             xmirror = xhost.FirstChildElement('mirror')
             xmirror = xhost.FirstChildElement('mirror')
@@ -235,6 +238,8 @@ class Packager:
             """ Returns a new TiXmlElement. """
             """ Returns a new TiXmlElement. """
             xhost = TiXmlElement('host')
             xhost = TiXmlElement('host')
             xhost.SetAttribute('url', self.url)
             xhost.SetAttribute('url', self.url)
+            if self.downloadUrl and self.downloadUrl != self.url:
+                xhost.SetAttribute('download_url', self.downloadUrl)
             if self.descriptiveName:
             if self.descriptiveName:
                 xhost.SetAttribute('descriptive_name', self.descriptiveName)
                 xhost.SetAttribute('descriptive_name', self.descriptiveName)
 
 
@@ -1704,14 +1709,17 @@ class Packager:
         # file.
         # file.
         self.contents = {}
         self.contents = {}
 
 
-    def setHost(self, host, descriptiveName = None, mirrors = None):
+    def setHost(self, host, downloadUrl = None,
+                descriptiveName = None, mirrors = None):
         """ Specifies the URL that will ultimately host these
         """ Specifies the URL that will ultimately host these
         contents. """
         contents. """
 
 
         self.host = host
         self.host = host
-        self.addHost(host, descriptiveName, mirrors)
+        self.addHost(host, downloadUrl = downloadUrl,
+                     descriptiveName = descriptiveName, mirrors = mirrors)
 
 
-    def addHost(self, host, descriptiveName = None, mirrors = None):
+    def addHost(self, host, downloadUrl = None, descriptiveName = None,
+                mirrors = None):
         """ Adds a host to the list of known download hosts.  This
         """ Adds a host to the list of known download hosts.  This
         information will be written into any p3d files that reference
         information will be written into any p3d files that reference
         this host; this can be used to pre-define the possible mirrors
         this host; this can be used to pre-define the possible mirrors
@@ -1721,11 +1729,15 @@ class Packager:
         he = self.hosts.get(host, None)
         he = self.hosts.get(host, None)
         if he is None:
         if he is None:
             # Define a new host entry
             # Define a new host entry
-            he = self.HostEntry(host, descriptiveName, mirrors)
+            he = self.HostEntry(host, downloadUrl = downloadUrl,
+                                descriptiveName = descriptiveName,
+                                mirrors = mirrors)
             self.hosts[host] = he
             self.hosts[host] = he
         else:
         else:
             # Update an existing host entry
             # Update an existing host entry
-            if descriptiveName:
+            if downloadUrl is not None:
+                he.downloadUrl = downloadUrl
+            if descriptiveName is not None:
                 he.descriptiveName = descriptiveName
                 he.descriptiveName = descriptiveName
             if mirrors is not None:
             if mirrors is not None:
                 he.mirrors = mirrors
                 he.mirrors = mirrors

+ 0 - 1
direct/src/p3d/coreapi.pdef

@@ -77,4 +77,3 @@ class p3dcert(package):
     filename = Filename(cvar.getValue())
     filename = Filename(cvar.getValue())
     if not filename.empty():
     if not filename.empty():
         file(filename, newName = 'ca-bundle.crt', extract = True)
         file(filename, newName = 'ca-bundle.crt', extract = True)
-        

+ 12 - 1
direct/src/plugin/fileSpec.cxx

@@ -136,12 +136,23 @@ load_xml(TiXmlElement *xelement) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool FileSpec::
 bool FileSpec::
 quick_verify(const string &package_dir) {
 quick_verify(const string &package_dir) {
+  return quick_verify_pathname(get_pathname(package_dir));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::quick_verify_pathname
+//       Access: Public
+//  Description: Works like quick_verify(), above, with an explicit
+//               pathname.  Useful for verifying the copy of a file in
+//               a temporary location.
+////////////////////////////////////////////////////////////////////
+bool FileSpec::
+quick_verify_pathname(const string &pathname) {
   if (_actual_file != NULL) {
   if (_actual_file != NULL) {
     delete _actual_file;
     delete _actual_file;
     _actual_file = NULL;
     _actual_file = NULL;
   }
   }
 
 
-  string pathname = get_pathname(package_dir);
   struct stat st;
   struct stat st;
   if (stat(pathname.c_str(), &st) != 0) {
   if (stat(pathname.c_str(), &st) != 0) {
     //cerr << "file not found: " << _filename << "\n";
     //cerr << "file not found: " << _filename << "\n";

+ 1 - 0
direct/src/plugin/fileSpec.h

@@ -41,6 +41,7 @@ public:
   inline size_t get_size() const;
   inline size_t get_size() const;
   
   
   bool quick_verify(const string &package_dir);
   bool quick_verify(const string &package_dir);
+  bool quick_verify_pathname(const string &pathname);
   bool full_verify(const string &package_dir);
   bool full_verify(const string &package_dir);
   inline const FileSpec *get_actual_file() const;
   inline const FileSpec *get_actual_file() const;
   
   

+ 17 - 0
direct/src/plugin/p3dDownload.cxx

@@ -84,6 +84,23 @@ cancel() {
   }    
   }    
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DDownload::clear
+//       Access: Public
+//  Description: Resets the download to its initial state, for
+//               re-trying the same download.
+////////////////////////////////////////////////////////////////////
+void P3DDownload::
+clear() {
+  _status = P3D_RC_in_progress;
+  _http_status_code = 0;
+  _total_data = 0;
+  _last_reported_time = 0;
+  
+  _canceled = false;
+  _download_id = 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DDownload::feed_url_stream
 //     Function: P3DDownload::feed_url_stream
 //       Access: Public
 //       Access: Public

+ 1 - 0
direct/src/plugin/p3dDownload.h

@@ -43,6 +43,7 @@ public:
   inline size_t get_total_data() const;
   inline size_t get_total_data() const;
 
 
   void cancel();
   void cancel();
+  void clear();
 
 
 public:
 public:
   // These are intended to be called only by P3DInstance.
   // These are intended to be called only by P3DInstance.

+ 5 - 6
direct/src/plugin/p3dPackage.cxx

@@ -1172,14 +1172,13 @@ resume_download_finished(bool success) {
     string url = _try_urls.back();
     string url = _try_urls.back();
     _try_urls.pop_back();
     _try_urls.pop_back();
 
 
-    Download *new_download = new Download(*this);
-    new_download->set_filename(get_filename());
-    new_download->set_url(url);
-
-    _package->_active_download = new_download;
+    clear();
+    set_url(url);
+    set_filename(get_filename());
+    _package->_active_download = this;
 
 
     assert(!_package->_instances.empty());
     assert(!_package->_instances.empty());
-    _package->_instances[0]->start_download(new_download);
+    _package->_instances[0]->start_download(this);
     return;
     return;
   }
   }
 
 

+ 178 - 30
direct/src/plugin_npapi/ppInstance.cxx

@@ -26,6 +26,7 @@
 #include "dtool_platform.h"
 #include "dtool_platform.h"
 
 
 #include <fstream>
 #include <fstream>
+#include <algorithm>
 #include <string.h>  // strcmp()
 #include <string.h>  // strcmp()
 #include <time.h>
 #include <time.h>
 
 
@@ -113,6 +114,7 @@ begin() {
     if (!url.empty() && url[url.length() - 1] != '/') {
     if (!url.empty() && url[url.length() - 1] != '/') {
       url += '/';
       url += '/';
     }
     }
+    _download_url_prefix = url;
     ostringstream strm;
     ostringstream strm;
     strm << url << "contents.xml";
     strm << url << "contents.xml";
 
 
@@ -362,6 +364,20 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
     }
     }
     break;
     break;
     
     
+  case PPDownloadRequest::RT_core_dll:
+    if (reason != NPRES_DONE) {
+      nout << "Failure downloading " << url << "\n";
+      // Couldn't download from this mirror.  Try the next one.
+      if (!_core_urls.empty()) {
+        string url = _core_urls.back();
+        _core_urls.pop_back();
+        
+        PPDownloadRequest *req2 = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
+        start_download(url, req2);
+      }
+    }
+    break;
+
   default:
   default:
     nout << "Unexpected url_notify on stream type " << req->_rtype << "\n";
     nout << "Unexpected url_notify on stream type " << req->_rtype << "\n";
     break;
     break;
@@ -670,6 +686,84 @@ variant_to_p3dobj(const NPVariant *variant) {
   return P3D_new_none_object();
   return P3D_new_none_object();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::read_xhost
+//       Access: Private
+//  Description: Reads the host data from the <host> (or <alt_host>)
+//               entry in the contents.xml file.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+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_prefix = download_url;
+  } else {
+    _download_url_prefix = PANDA_PACKAGE_HOST_URL;
+  }
+  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: PPInstance::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 PPInstance::
+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: PPInstance::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 PPInstance::
+choose_random_mirrors(vector<string> &result, int num_mirrors) {
+  vector<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: PPInstance::request_ready
 //     Function: PPInstance::request_ready
 //       Access: Private, Static
 //       Access: Private, Static
@@ -719,6 +813,7 @@ request_ready(P3D_instance *instance) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 void PPInstance::
 start_download(const string &url, PPDownloadRequest *req) {
 start_download(const string &url, PPDownloadRequest *req) {
+  nout << "start_download: " << url << "\n";
   if (url.substr(0, 7) == "file://") {
   if (url.substr(0, 7) == "file://") {
     // If we're "downloading" a file URL, just go read the file directly.
     // If we're "downloading" a file URL, just go read the file directly.
     downloaded_file(req, get_filename_from_url(url));
     downloaded_file(req, get_filename_from_url(url));
@@ -744,6 +839,32 @@ read_contents_file(const string &contents_filename) {
 
 
   TiXmlElement *xcontents = doc.FirstChildElement("contents");
   TiXmlElement *xcontents = doc.FirstChildElement("contents");
   if (xcontents != NULL) {
   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");
+        }
+      }
+    }
+
+    // Now look for the core API package.
     TiXmlElement *xpackage = xcontents->FirstChildElement("package");
     TiXmlElement *xpackage = xcontents->FirstChildElement("package");
     while (xpackage != NULL) {
     while (xpackage != NULL) {
       const char *name = xpackage->Attribute("name");
       const char *name = xpackage->Attribute("name");
@@ -874,13 +995,39 @@ get_core_api(TiXmlElement *xpackage) {
     do_load_plugin();
     do_load_plugin();
 
 
   } else {
   } else {
-    // The DLL file needs to be downloaded.  Go get it.
-    string url = PANDA_PACKAGE_HOST_URL;
-    if (!url.empty() && url[url.length() - 1] != '/') {
-      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;
+
+    // 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();
     url += _core_api_dll.get_filename();
-    
+    _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);
+    }
+
+    // Now pick the first URL off the list, and try it.
+    assert(!_core_urls.empty());
+    url = _core_urls.back();
+    _core_urls.pop_back();
+
     PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
     PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
     start_download(url, req);
     start_download(url, req);
   }
   }
@@ -906,40 +1053,40 @@ downloaded_plugin(const string &filename) {
     return;
     return;
   }
   }
 
 
-  // Copy the file onto the target.
-  string pathname = _core_api_dll.get_pathname(_root_dir);
-  mkfile_complete(pathname, nout);
-
-  ifstream in(filename.c_str(), ios::in | ios::binary);
-  ofstream out(pathname.c_str(), ios::out | ios::binary);
+  // Make sure the DLL was correctly downloaded before continuing.
+  if (!_core_api_dll.quick_verify_pathname(filename)) {
+    nout << "After download, " << _core_api_dll.get_filename() << " is no good.\n";
 
 
-  static const size_t buffer_size = 4096;
-  static char buffer[buffer_size];
+    // That DLL came out wrong.  Try the next URL.
+    if (!_core_urls.empty()) {
+      string url = _core_urls.back();
+      _core_urls.pop_back();
+      
+      PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
+      start_download(url, req);
+      return;
+    }
 
 
-  in.read(buffer, buffer_size);
-  size_t count = in.gcount();
-  while (count != 0) {
-    out.write(buffer, count);
-    in.read(buffer, buffer_size);
-    count = in.gcount();
+    // TODO: fail
+    return;
   }
   }
 
 
-  if (!out) {
-    nout << "Could not write " << pathname << "\n";
-    // TODO: fail
+  // Copy the file onto the target.
+  string pathname = _core_api_dll.get_pathname(_root_dir);
+  if (!copy_file(filename, pathname)) {
+    nout << "Couldn't copy " << pathname << "\n";
     return;
     return;
   }
   }
-  in.close();
-  out.close();
 
 
-  if (_core_api_dll.quick_verify(_root_dir)) {
-    // We downloaded and installed it successfully.  Now load it.
-    do_load_plugin();
+  if (!_core_api_dll.quick_verify(_root_dir)) {
+    nout << "After copying, " << pathname << " is no good.\n";
+    // TODO: fail
     return;
     return;
   }
   }
 
 
-  nout << "After download, " << pathname << " is no good.\n";
-  // TODO: fail
+  // We downloaded and installed it successfully.  Now load it.
+  do_load_plugin();
+  return;
 }
 }
 
 
 
 
@@ -1141,6 +1288,7 @@ cleanup_window() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPInstance::
 bool PPInstance::
 copy_file(const string &from_filename, const string &to_filename) {
 copy_file(const string &from_filename, const string &to_filename) {
+  mkfile_complete(to_filename, nout);
   ifstream in(from_filename.c_str(), ios::in | ios::binary);
   ifstream in(from_filename.c_str(), ios::in | ios::binary);
   ofstream out(to_filename.c_str(), ios::out | ios::binary);
   ofstream out(to_filename.c_str(), ios::out | ios::binary);
         
         

+ 13 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -64,6 +64,10 @@ public:
   static void output_np_variant(ostream &out, const NPVariant &result);
   static void output_np_variant(ostream &out, const NPVariant &result);
 
 
 private:
 private:
+  void read_xhost(TiXmlElement *xhost);
+  void add_mirror(string mirror_url);
+  void choose_random_mirrors(vector<string> &result, int num_mirrors);
+
   static void request_ready(P3D_instance *instance);
   static void request_ready(P3D_instance *instance);
 
 
   void start_download(const string &url, PPDownloadRequest *req);
   void start_download(const string &url, PPDownloadRequest *req);
@@ -96,6 +100,15 @@ private:
   Tokens _tokens;
   Tokens _tokens;
 
 
   string _root_dir;
   string _root_dir;
+  string _download_url_prefix;
+  typedef vector<string> Mirrors;
+  Mirrors _mirrors;
+
+  // A list of URL's that we will attempt to download the core API
+  // from.
+  typedef vector<string> CoreUrls;
+  CoreUrls _core_urls;
+
   FileSpec _core_api_dll;
   FileSpec _core_api_dll;
 
 
   bool _got_instance_url;
   bool _got_instance_url;