Panda3D
|
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