Panda3D
virtualFileHTTP.cxx
1 // Filename: virtualFileHTTP.cxx
2 // Created by: drose (31Oct08)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "virtualFileHTTP.h"
16 #include "virtualFileMountHTTP.h"
17 #include "stringStream.h"
18 #include "zStream.h"
19 
20 #ifdef HAVE_OPENSSL
21 
22 TypeHandle VirtualFileHTTP::_type_handle;
23 
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: VirtualFileHTTP::Constructor
27 // Access: Public
28 // Description:
29 ////////////////////////////////////////////////////////////////////
30 VirtualFileHTTP::
31 VirtualFileHTTP(VirtualFileMountHTTP *mount, const Filename &local_filename,
32  bool implicit_pz_file, int open_flags) :
33  _mount(mount),
34  _local_filename(local_filename),
35  _implicit_pz_file(implicit_pz_file),
36  _status_only(open_flags != 0)
37 {
38  URLSpec url(_mount->get_root());
39  url.set_path(_mount->get_root().get_path() + _local_filename.c_str());
40  _channel = _mount->get_channel();
41  if (_status_only) {
42  _channel->get_header(url);
43  } else {
44  _channel->get_document(url);
45  }
46 }
47 
48 ////////////////////////////////////////////////////////////////////
49 // Function: VirtualFileHTTP::Destructor
50 // Access: Published, Virtual
51 // Description:
52 ////////////////////////////////////////////////////////////////////
53 VirtualFileHTTP::
54 ~VirtualFileHTTP() {
55  // Recycle the associated HTTPChannel, so we can use it again later
56  // without having to close the connection to the server.
57  _mount->recycle_channel(_channel);
58 }
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: VirtualFileHTTP::get_file_system
62 // Access: Published, Virtual
63 // Description: Returns the VirtualFileSystem this file is associated
64 // with.
65 ////////////////////////////////////////////////////////////////////
66 VirtualFileSystem *VirtualFileHTTP::
67 get_file_system() const {
68  return _mount->get_file_system();
69 }
70 
71 ////////////////////////////////////////////////////////////////////
72 // Function: VirtualFileHTTP::get_filename
73 // Access: Published, Virtual
74 // Description: Returns the full pathname to this file within the
75 // virtual file system.
76 ////////////////////////////////////////////////////////////////////
77 Filename VirtualFileHTTP::
78 get_filename() const {
79  string mount_point = _mount->get_mount_point();
80  if (_local_filename.empty()) {
81  if (mount_point.empty()) {
82  return "/";
83  } else {
84  return string("/") + mount_point;
85  }
86 
87  } else {
88  if (mount_point.empty()) {
89  return string("/") + _local_filename.get_fullpath();
90  } else {
91  return string("/") + mount_point + string("/") + _local_filename.get_fullpath();
92  }
93  }
94 }
95 
96 ////////////////////////////////////////////////////////////////////
97 // Function: VirtualFileHTTP::has_file
98 // Access: Published, Virtual
99 // Description: Returns true if this file exists, false otherwise.
100 ////////////////////////////////////////////////////////////////////
101 bool VirtualFileHTTP::
102 has_file() const {
103  return _channel->is_valid();
104 }
105 
106 ////////////////////////////////////////////////////////////////////
107 // Function: VirtualFileHTTP::is_directory
108 // Access: Published, Virtual
109 // Description: Returns true if this file represents a directory (and
110 // scan_directory() may be called), false otherwise.
111 ////////////////////////////////////////////////////////////////////
112 bool VirtualFileHTTP::
113 is_directory() const {
114  return false;
115 }
116 
117 ////////////////////////////////////////////////////////////////////
118 // Function: VirtualFileHTTP::is_regular_file
119 // Access: Published, Virtual
120 // Description: Returns true if this file represents a regular file
121 // (and read_file() may be called), false otherwise.
122 ////////////////////////////////////////////////////////////////////
123 bool VirtualFileHTTP::
124 is_regular_file() const {
125  return _channel->is_valid();
126 }
127 
128 ////////////////////////////////////////////////////////////////////
129 // Function: VirtualFileHTTP::open_read_file
130 // Access: Published, Virtual
131 // Description: Opens the file for reading. Returns a newly
132 // allocated istream on success (which you should
133 // eventually delete when you are done reading).
134 // Returns NULL on failure.
135 //
136 // If auto_unwrap is true, an explicitly-named .pz file
137 // is automatically decompressed and the decompressed
138 // contents are returned. This is different than
139 // vfs-implicit-pz, which will automatically decompress
140 // a file if the extension .pz is *not* given.
141 ////////////////////////////////////////////////////////////////////
142 istream *VirtualFileHTTP::
143 open_read_file(bool auto_unwrap) const {
144  if (_status_only) {
145  return NULL;
146  }
147 
148  // We pre-download the file into a StringStream, then return a
149  // buffer to that. It seems safer, since we can guarantee the file
150  // comes all at once without timeouts along the way.
151  StringStream *strstream = new StringStream;
152  if (!fetch_file(strstream)) {
153  delete strstream;
154  return NULL;
155  }
156 
157  return return_file(strstream, auto_unwrap);
158 }
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: VirtualFileHTTP::fetch_file
162 // Access: Private
163 // Description: Downloads the entire file from the web server into
164 // the indicated iostream. Returns true on success,
165 // false on failure.
166 //
167 // This seems to be safer than returning the socket
168 // stream directly, since this way we can better control
169 // timeouts and other internet hiccups. We can also
170 // offer seeking on the resulting stream.
171 ////////////////////////////////////////////////////////////////////
172 bool VirtualFileHTTP::
173 fetch_file(ostream *buffer_stream) const {
174  _channel->download_to_stream(buffer_stream, false);
175  if (!_channel->is_download_complete()) {
176  // Failure to download fully. Try again to download more.
177 
178  URLSpec url(_mount->get_root());
179  url.set_path(_mount->get_root().get_path() + _local_filename.c_str());
180 
181  size_t bytes_downloaded = _channel->get_bytes_downloaded();
182  size_t last_byte = bytes_downloaded;
183 
184  while (bytes_downloaded > 0 && !_channel->is_download_complete()) {
185  _channel->get_subdocument(url, last_byte, 0);
186  _channel->download_to_stream(buffer_stream, true);
187  bytes_downloaded = _channel->get_bytes_downloaded();
188  last_byte = _channel->get_last_byte_delivered();
189  }
190  }
191 
192  return _channel->is_download_complete() && _channel->is_valid();
193 }
194 
195 ////////////////////////////////////////////////////////////////////
196 // Function: VirtualFileHTTP::return_file
197 // Access: Private
198 // Description: After downloading the entire file via fetch_file(),
199 // rewinds the file stream and returns it as its own
200 // readable stream.
201 ////////////////////////////////////////////////////////////////////
202 istream *VirtualFileHTTP::
203 return_file(istream *buffer_stream, bool auto_unwrap) const {
204  // Will we be automatically unwrapping a .pz file?
205  bool do_unwrap = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
206 
207  istream *result = buffer_stream;
208 #ifdef HAVE_ZLIB
209  if (result != (istream *)NULL && do_unwrap) {
210  // We have to slip in a layer to decompress the file on the fly.
211  IDecompressStream *wrapper = new IDecompressStream(result, true);
212  result = wrapper;
213  }
214 #endif // HAVE_ZLIB
215 
216  return result;
217 }
218 
219 ////////////////////////////////////////////////////////////////////
220 // Function: VirtualFileHTTP::was_read_successful
221 // Access: Public
222 // Description: Call this method after a reading the istream returned
223 // by open_read_file() to completion. If it returns
224 // true, the file was read completely and without error;
225 // if it returns false, there may have been some errors
226 // or a truncated file read. This is particularly
227 // likely if the stream is a VirtualFileHTTP.
228 ////////////////////////////////////////////////////////////////////
229 bool VirtualFileHTTP::
230 was_read_successful() const {
231  return _channel->is_valid() && _channel->is_download_complete();
232 }
233 
234 ////////////////////////////////////////////////////////////////////
235 // Function: VirtualFileHTTP::get_file_size
236 // Access: Published, Virtual
237 // Description: Returns the current size on disk (or wherever it is)
238 // of the already-open file. Pass in the stream that
239 // was returned by open_read_file(); some
240 // implementations may require this stream to determine
241 // the size.
242 ////////////////////////////////////////////////////////////////////
243 streamsize VirtualFileHTTP::
244 get_file_size(istream *stream) const {
245  return _channel->get_file_size();
246 }
247 
248 ////////////////////////////////////////////////////////////////////
249 // Function: VirtualFileHTTP::get_file_size
250 // Access: Published, Virtual
251 // Description: Returns the current size on disk (or wherever it is)
252 // of the file before it has been opened.
253 ////////////////////////////////////////////////////////////////////
254 streamsize VirtualFileHTTP::
255 get_file_size() const {
256  return _channel->get_file_size();
257 }
258 
259 ////////////////////////////////////////////////////////////////////
260 // Function: VirtualFileHTTP::get_timestamp
261 // Access: Published, Virtual
262 // Description: Returns a time_t value that represents the time the
263 // file was last modified, to within whatever precision
264 // the operating system records this information (on a
265 // Windows95 system, for instance, this may only be
266 // accurate to within 2 seconds).
267 //
268 // If the timestamp cannot be determined, either because
269 // it is not supported by the operating system or
270 // because there is some error (such as file not found),
271 // returns 0.
272 ////////////////////////////////////////////////////////////////////
273 time_t VirtualFileHTTP::
274 get_timestamp() const {
275  const DocumentSpec &spec = _channel->get_document_spec();
276  if (spec.has_date()) {
277  return spec.get_date().get_time();
278  }
279  return 0;
280 }
281 
282 #endif // HAVE_OPENSSL
283 
A container for a URL, e.g.
Definition: urlSpec.h:29
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:398
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
bool has_date() const
Returns true if a last-modified date is associated with the DocumentSpec.
Definition: documentSpec.I:211
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
const HTTPDate & get_date() const
Returns the last-modified date associated with the DocumentSpec, if there is one. ...
Definition: documentSpec.I:223
time_t get_time() const
Returns the date as a C time_t value.
Definition: httpDate.I:82
A descriptor that refers to a particular version of a document.
Definition: documentSpec.h:33
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
void set_path(const string &path)
Replaces the path part of the URL specification.
Definition: urlSpec.cxx:416
A bi-directional stream object that reads and writes data to an internal buffer, which can be retriev...
Definition: stringStream.h:27