Browse Source

support file:// urls

David Rose 16 years ago
parent
commit
7406014cbc

+ 29 - 4
panda/src/downloader/bioPtr.cxx

@@ -29,10 +29,35 @@
 ////////////////////////////////////////////////////////////////////
 BioPtr::
 BioPtr(const URLSpec &url) {
-  _server_name = url.get_server();
-  _port = url.get_port();
-  _bio = BIO_new_connect((char *)_server_name.c_str());
-  BIO_set_conn_int_port(_bio, &_port);
+  if (url.get_scheme() == "file") {
+    // We're just reading a disk file.
+    string filename = url.get_path();
+#ifdef _WIN32 
+    // On Windows, we have to munge the filename specially, because it's
+    // been URL-munged.  It might begin with a leading slash as well as
+    // a drive letter.  Clean up that nonsense.
+    if (!filename.empty()) {
+      if (filename[0] == '/' || filename[0] == '\\') {
+        Filename fname = Filename::from_os_specific(filename.substr(1));
+        if (fname.is_local()) {
+          // Put the slash back on.
+          fname = string("/") + fname.get_fullpath();
+        }
+        filename = fname.to_os_specific();
+      }
+    }
+#endif  // _WIN32
+    _server_name = "";
+    _port = 0;
+    _bio = BIO_new_file(filename.c_str(), "rb");
+
+  } else {
+    // A normal network-based URL.
+    _server_name = url.get_server();
+    _port = url.get_port();
+    _bio = BIO_new_connect((char *)_server_name.c_str());
+    BIO_set_conn_int_port(_bio, &_port);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/downloader/bioStreamBuf.cxx

@@ -193,7 +193,7 @@ underflow() {
         // which that never returns true, if the socket is closed by
         // the server.  But BIO_should_retry() *appears* to be
         // reliable.
-        _read_open = (bool)BIO_should_retry(*_source);
+        _read_open = (BIO_should_retry(*_source) != 0);
         if (!_read_open) {
           downloader_cat.info()
             << "Lost connection to "
@@ -262,7 +262,7 @@ write_chars(const char *start, size_t length) {
         // without a retry request
         //_write_open = BIO_should_retry(*_source);
         //_write_open = !BIO_eof(*_source);
-        _write_open = (bool)BIO_should_write(*_source);
+        _write_open = (BIO_should_write(*_source) != 0);
         if (!_write_open) {
           return wrote_so_far;
         }

+ 72 - 18
panda/src/downloader/httpChannel.cxx

@@ -23,6 +23,8 @@
 #include "virtualFileMountHTTP.h"
 #include "ramfile.h"
 
+#include <stdio.h>
+
 #ifdef HAVE_OPENSSL
 #include "openssl/x509.h"
 
@@ -462,6 +464,10 @@ run() {
       repeat_later = run_reading_header();
       break;
       
+    case S_start_direct_file_read:
+      repeat_later = run_start_direct_file_read();
+      break;
+      
     case S_read_header:
       repeat_later = run_read_header();
       break;
@@ -912,7 +918,7 @@ reached_done_state() {
     if (_body_stream == (ISocketStream *)NULL) {
       if (downloader_cat.is_debug()) {
         downloader_cat.debug()
-          << "Unable to download body.\n";
+          << "Unable to download body: " << _request.get_url() << "\n";
       }
       return false;
 
@@ -1981,6 +1987,18 @@ run_reading_header() {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPChannel::run_start_direct_file_read
+//       Access: Private
+//  Description: This is the first state when reading a file:// URL.
+//               All it does is skip past the non-existent "header".
+////////////////////////////////////////////////////////////////////
+bool HTTPChannel::
+run_start_direct_file_read() {
+  _state = S_read_header;
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HTTPChannel::run_read_header
 //       Access: Private
@@ -2436,27 +2454,60 @@ begin_request(HTTPEnum::Method method, const DocumentSpec &url,
 
   reconsider_proxy();
 
-  // Also, reset from whatever previous request might still be pending.
-  if (_state == S_failure || (_state < S_read_header && _state != S_ready)) {
-    if (downloader_cat.is_debug()) {
-      downloader_cat.debug()
-        << "resetting to clear previous request.\n";
-    }
+  // Reset from whatever previous request might still be pending.
+  if (_request.get_url().get_scheme() == "file") {
+    // A "file" URL just means we're reading a raw file.  This only
+    // supports actual disk files, not the VFS, because we use a
+    // BIO_new_file() underneath this.
     reset_to_new();
+    _bio = new BioPtr(_request.get_url());
+    if (_bio->get_bio() != NULL) {
+      // Successfully opened the file.
+      _source = new BioStreamPtr(new BioStream(_bio));
+      _status_entry._status_code = 200;
+      _state = S_start_direct_file_read;
+
+      // Get the file size.
+      FILE *fp = NULL;
+      BIO_get_fp(_bio->get_bio(), &fp);
+      if (fp != NULL) {
+        if (fseek(fp, 0, SEEK_END) == 0) {
+          _file_size = ftell(fp);
+          _got_file_size = true;
+          fseek(fp, 0, SEEK_SET);
+        }
+      }
 
-  } else if (TrueClock::get_global_ptr()->get_short_time() - _last_run_time >= _idle_timeout) {
-    if (downloader_cat.is_debug()) {
-      downloader_cat.debug()
-        << "resetting old connection: " 
-        << TrueClock::get_global_ptr()->get_short_time() - _last_run_time
-        << " s old.\n";
+    } else {
+      // Couldn't read the file.
+      notify_ssl_errors();
+      _status_entry._status_code = SC_no_connection;
+      _state = S_failure;
     }
-    reset_to_new();
 
-  } else if (_state == S_read_header) {
-    // Roll one step forwards to start skipping past the previous
-    // body.
-    _state = S_begin_body;
+  } else {
+    // We're reading a normal network URL.
+    if (_state == S_failure || (_state < S_read_header && _state != S_ready)) {
+      if (downloader_cat.is_debug()) {
+        downloader_cat.debug()
+          << "resetting to clear previous request.\n";
+      }
+      reset_to_new();
+      
+    } else if (TrueClock::get_global_ptr()->get_short_time() - _last_run_time >= _idle_timeout) {
+      if (downloader_cat.is_debug()) {
+        downloader_cat.debug()
+          << "resetting old connection: " 
+          << TrueClock::get_global_ptr()->get_short_time() - _last_run_time
+          << " s old.\n";
+      }
+      reset_to_new();
+      
+    } else if (_state == S_read_header) {
+      // Roll one step forwards to start skipping past the previous
+      // body.
+      _state = S_begin_body;
+    }
   }
 
   if (_method == HTTPEnum::M_connect) {
@@ -3823,6 +3874,9 @@ operator << (ostream &out, HTTPChannel::State state) {
   case HTTPChannel::S_reading_header:
     return out << "reading_header";
 
+  case HTTPChannel::S_start_direct_file_read:
+    return out << "start_direct_file_read";
+
   case HTTPChannel::S_read_header:
     return out << "read_header";
 

+ 2 - 0
panda/src/downloader/httpChannel.h

@@ -215,6 +215,7 @@ private:
   bool run_ready();
   bool run_request_sent();
   bool run_reading_header();
+  bool run_start_direct_file_read();
   bool run_read_header();
   bool run_begin_body();
   bool run_reading_body();
@@ -287,6 +288,7 @@ public:
     S_ready,
     S_request_sent,
     S_reading_header,
+    S_start_direct_file_read,
     S_read_header,
     S_begin_body,
     S_reading_body,