Panda3D

virtualFileHTTP.cxx

00001 // Filename: virtualFileHTTP.cxx
00002 // Created by:  drose (31Oct08)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "virtualFileHTTP.h"
00016 #include "virtualFileMountHTTP.h"
00017 #include "stringStream.h"
00018 #include "zStream.h"
00019 
00020 #ifdef HAVE_OPENSSL
00021 
00022 TypeHandle VirtualFileHTTP::_type_handle;
00023 
00024 
00025 ////////////////////////////////////////////////////////////////////
00026 //     Function: VirtualFileHTTP::Constructor
00027 //       Access: Public
00028 //  Description: 
00029 ////////////////////////////////////////////////////////////////////
00030 VirtualFileHTTP::
00031 VirtualFileHTTP(VirtualFileMountHTTP *mount, const Filename &local_filename,
00032                 bool implicit_pz_file, int open_flags) :
00033   _mount(mount),
00034   _local_filename(local_filename),
00035   _implicit_pz_file(implicit_pz_file),
00036   _status_only(open_flags != 0)
00037 {
00038   URLSpec url(_mount->get_root());
00039   url.set_path(_mount->get_root().get_path() + _local_filename.c_str());
00040   _channel = _mount->get_channel();
00041   if (_status_only) {
00042     _channel->get_header(url);
00043   } else {
00044     _channel->get_document(url);
00045   }
00046 }
00047 
00048 ////////////////////////////////////////////////////////////////////
00049 //     Function: VirtualFileHTTP::Destructor
00050 //       Access: Published, Virtual
00051 //  Description: 
00052 ////////////////////////////////////////////////////////////////////
00053 VirtualFileHTTP::
00054 ~VirtualFileHTTP() {
00055   // Recycle the associated HTTPChannel, so we can use it again later
00056   // without having to close the connection to the server.
00057   _mount->recycle_channel(_channel);
00058 }
00059 
00060 ////////////////////////////////////////////////////////////////////
00061 //     Function: VirtualFileHTTP::get_file_system
00062 //       Access: Published, Virtual
00063 //  Description: Returns the VirtualFileSystem this file is associated
00064 //               with.
00065 ////////////////////////////////////////////////////////////////////
00066 VirtualFileSystem *VirtualFileHTTP::
00067 get_file_system() const {
00068   return _mount->get_file_system();
00069 }
00070 
00071 ////////////////////////////////////////////////////////////////////
00072 //     Function: VirtualFileHTTP::get_filename
00073 //       Access: Published, Virtual
00074 //  Description: Returns the full pathname to this file within the
00075 //               virtual file system.
00076 ////////////////////////////////////////////////////////////////////
00077 Filename VirtualFileHTTP::
00078 get_filename() const {
00079   string mount_point = _mount->get_mount_point();
00080   if (_local_filename.empty()) {
00081     if (mount_point.empty()) {
00082       return "/";
00083     } else {
00084       return string("/") + mount_point;
00085     }
00086 
00087   } else {
00088     if (mount_point.empty()) {
00089       return string("/") + _local_filename.get_fullpath();
00090     } else {
00091       return string("/") + mount_point + string("/") + _local_filename.get_fullpath();
00092     }
00093   }
00094 }
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: VirtualFileHTTP::has_file
00098 //       Access: Published, Virtual
00099 //  Description: Returns true if this file exists, false otherwise.
00100 ////////////////////////////////////////////////////////////////////
00101 bool VirtualFileHTTP::
00102 has_file() const {
00103   return _channel->is_valid();
00104 }
00105 
00106 ////////////////////////////////////////////////////////////////////
00107 //     Function: VirtualFileHTTP::is_directory
00108 //       Access: Published, Virtual
00109 //  Description: Returns true if this file represents a directory (and
00110 //               scan_directory() may be called), false otherwise.
00111 ////////////////////////////////////////////////////////////////////
00112 bool VirtualFileHTTP::
00113 is_directory() const {
00114   return false;
00115 }
00116 
00117 ////////////////////////////////////////////////////////////////////
00118 //     Function: VirtualFileHTTP::is_regular_file
00119 //       Access: Published, Virtual
00120 //  Description: Returns true if this file represents a regular file
00121 //               (and read_file() may be called), false otherwise.
00122 ////////////////////////////////////////////////////////////////////
00123 bool VirtualFileHTTP::
00124 is_regular_file() const {
00125   return _channel->is_valid();
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: VirtualFileHTTP::open_read_file
00130 //       Access: Published, Virtual
00131 //  Description: Opens the file for reading.  Returns a newly
00132 //               allocated istream on success (which you should
00133 //               eventually delete when you are done reading).
00134 //               Returns NULL on failure.
00135 //
00136 //               If auto_unwrap is true, an explicitly-named .pz file
00137 //               is automatically decompressed and the decompressed
00138 //               contents are returned.  This is different than
00139 //               vfs-implicit-pz, which will automatically decompress
00140 //               a file if the extension .pz is *not* given.
00141 ////////////////////////////////////////////////////////////////////
00142 istream *VirtualFileHTTP::
00143 open_read_file(bool auto_unwrap) const {
00144   if (_status_only) {
00145     return NULL;
00146   }
00147 
00148   // We pre-download the file into a StringStream, then return a
00149   // buffer to that.  It seems safer, since we can guarantee the file
00150   // comes all at once without timeouts along the way.
00151   StringStream *strstream = new StringStream;
00152   if (!fetch_file(strstream)) {
00153     delete strstream;
00154     return NULL;
00155   }
00156   
00157   return return_file(strstream, auto_unwrap);
00158 }
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: VirtualFileHTTP::fetch_file
00162 //       Access: Private
00163 //  Description: Downloads the entire file from the web server into
00164 //               the indicated iostream.  Returns true on success,
00165 //               false on failure.
00166 //
00167 //               This seems to be safer than returning the socket
00168 //               stream directly, since this way we can better control
00169 //               timeouts and other internet hiccups.  We can also
00170 //               offer seeking on the resulting stream.
00171 ////////////////////////////////////////////////////////////////////
00172 bool VirtualFileHTTP::
00173 fetch_file(ostream *buffer_stream) const {
00174   _channel->download_to_stream(buffer_stream, false);
00175   if (!_channel->is_download_complete()) {
00176     // Failure to download fully.  Try again to download more.
00177 
00178     URLSpec url(_mount->get_root());
00179     url.set_path(_mount->get_root().get_path() + _local_filename.c_str());
00180 
00181     size_t bytes_downloaded = _channel->get_bytes_downloaded();
00182     size_t last_byte = bytes_downloaded;
00183 
00184     while (bytes_downloaded > 0 && !_channel->is_download_complete()) {
00185       _channel->get_subdocument(url, last_byte, 0);
00186       _channel->download_to_stream(buffer_stream, true);
00187       bytes_downloaded = _channel->get_bytes_downloaded();
00188       last_byte = _channel->get_last_byte_delivered();
00189     }
00190   }
00191 
00192   return _channel->is_download_complete() && _channel->is_valid();
00193 }
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: VirtualFileHTTP::return_file
00197 //       Access: Private
00198 //  Description: After downloading the entire file via fetch_file(),
00199 //               rewinds the file stream and returns it as its own
00200 //               readable stream.
00201 ////////////////////////////////////////////////////////////////////
00202 istream *VirtualFileHTTP::
00203 return_file(istream *buffer_stream, bool auto_unwrap) const {
00204   // Will we be automatically unwrapping a .pz file?
00205   bool do_unwrap = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
00206 
00207   istream *result = buffer_stream;
00208 #ifdef HAVE_ZLIB
00209   if (result != (istream *)NULL && do_unwrap) {
00210     // We have to slip in a layer to decompress the file on the fly.
00211     IDecompressStream *wrapper = new IDecompressStream(result, true);
00212     result = wrapper;
00213   }
00214 #endif  // HAVE_ZLIB
00215 
00216   return result;
00217 }
00218 
00219 ////////////////////////////////////////////////////////////////////
00220 //     Function: VirtualFileHTTP::was_read_successful
00221 //       Access: Public
00222 //  Description: Call this method after a reading the istream returned
00223 //               by open_read_file() to completion.  If it returns
00224 //               true, the file was read completely and without error;
00225 //               if it returns false, there may have been some errors
00226 //               or a truncated file read.  This is particularly
00227 //               likely if the stream is a VirtualFileHTTP.
00228 ////////////////////////////////////////////////////////////////////
00229 bool VirtualFileHTTP::
00230 was_read_successful() const {
00231   return _channel->is_valid() && _channel->is_download_complete();
00232 }
00233 
00234 ////////////////////////////////////////////////////////////////////
00235 //     Function: VirtualFileHTTP::get_file_size
00236 //       Access: Published, Virtual
00237 //  Description: Returns the current size on disk (or wherever it is)
00238 //               of the already-open file.  Pass in the stream that
00239 //               was returned by open_read_file(); some
00240 //               implementations may require this stream to determine
00241 //               the size.
00242 ////////////////////////////////////////////////////////////////////
00243 off_t VirtualFileHTTP::
00244 get_file_size(istream *stream) const {
00245   return _channel->get_file_size();
00246 }
00247 
00248 ////////////////////////////////////////////////////////////////////
00249 //     Function: VirtualFileHTTP::get_file_size
00250 //       Access: Published, Virtual
00251 //  Description: Returns the current size on disk (or wherever it is)
00252 //               of the file before it has been opened.
00253 ////////////////////////////////////////////////////////////////////
00254 off_t VirtualFileHTTP::
00255 get_file_size() const {
00256   return _channel->get_file_size();
00257 }
00258 
00259 ////////////////////////////////////////////////////////////////////
00260 //     Function: VirtualFileHTTP::get_timestamp
00261 //       Access: Published, Virtual
00262 //  Description: Returns a time_t value that represents the time the
00263 //               file was last modified, to within whatever precision
00264 //               the operating system records this information (on a
00265 //               Windows95 system, for instance, this may only be
00266 //               accurate to within 2 seconds).
00267 //
00268 //               If the timestamp cannot be determined, either because
00269 //               it is not supported by the operating system or
00270 //               because there is some error (such as file not found),
00271 //               returns 0.
00272 ////////////////////////////////////////////////////////////////////
00273 time_t VirtualFileHTTP::
00274 get_timestamp() const {
00275   const DocumentSpec &spec = _channel->get_document_spec();
00276   if (spec.has_date()) {
00277     return spec.get_date().get_time();
00278   }
00279   return 0;
00280 }
00281 
00282 #endif // HAVE_OPENSSL
00283 
 All Classes Functions Variables Enumerations