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