diff --git a/panda/src/downloader/bioPtr.cxx b/panda/src/downloader/bioPtr.cxx index d08de22c37..85e9480e61 100644 --- a/panda/src/downloader/bioPtr.cxx +++ b/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); + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/downloader/bioStreamBuf.cxx b/panda/src/downloader/bioStreamBuf.cxx index 51e1725796..c5a7b9e022 100644 --- a/panda/src/downloader/bioStreamBuf.cxx +++ b/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; } diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 861056c634..5405635db3 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -23,6 +23,8 @@ #include "virtualFileMountHTTP.h" #include "ramfile.h" +#include + #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; - } 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"; + // 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 { + // 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"; diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index 9964d0862b..cf0f84dd1e 100644 --- a/panda/src/downloader/httpChannel.h +++ b/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,