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
DocumentSpec
A descriptor that refers to a particular version of a document.
Definition: documentSpec.h:30
DocumentSpec::get_date
get_date
Returns the last-modified date associated with the DocumentSpec, if there is one.
Definition: documentSpec.h:82
DocumentSpec::has_date
has_date
Returns true if a last-modified date is associated with the DocumentSpec.
Definition: documentSpec.h:82
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Filename::get_fullpath
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
Ramfile
An in-memory buffer specifically designed for downloading files to memory.
Definition: ramfile.h:25
Ramfile::get_data
const std::string & get_data() const
Returns the entire buffer contents as a string, regardless of the current data pointer.
Definition: ramfile.I:45
StringStream
A bi-directional stream object that reads and writes data to an internal buffer, which can be retriev...
Definition: stringStream.h:27
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
URLSpec
A container for a URL, e.g.
Definition: urlSpec.h:28
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
ramfile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
stringStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileHTTP.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileMountHTTP.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
zStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.