Panda3D
Loading...
Searching...
No Matches
chunkedStreamBuf.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 chunkedStreamBuf.cxx
10 * @author drose
11 * @date 2002-09-25
12 */
13
14#include "chunkedStreamBuf.h"
15#include "config_downloader.h"
16#include <ctype.h>
17
18// This module is not compiled if OpenSSL is not available.
19#ifdef HAVE_OPENSSL
20
21/**
22 *
23 */
24ChunkedStreamBuf::
25ChunkedStreamBuf() {
26 _chunk_remaining = 0;
27 _done = true;
28 _wanted_nonblocking = false;
29 _read_state = ISocketStream::RS_initial;
30
31#ifdef PHAVE_IOSTREAM
32 _buffer = (char *)PANDA_MALLOC_ARRAY(4096);
33 char *ebuf = _buffer + 4096;
34 setg(_buffer, ebuf, ebuf);
35 setp(_buffer, ebuf);
36
37#else
38 allocate();
39 setg(base(), ebuf(), ebuf());
40 setp(base(), ebuf());
41#endif
42}
43
44/**
45 *
46 */
47ChunkedStreamBuf::
48~ChunkedStreamBuf() {
49 close_read();
50#ifdef PHAVE_IOSTREAM
51 PANDA_FREE_ARRAY(_buffer);
52#endif
53}
54
55/**
56 * If the document pointer is non-NULL, it will be updated with the length of
57 * the file as it is derived from the chunked encoding.
58 */
59void ChunkedStreamBuf::
60open_read(BioStreamPtr *source, HTTPChannel *doc) {
61 _source = source;
62 nassertv(!_source.is_null());
63 _chunk_remaining = 0;
64 _done = false;
65 _wanted_nonblocking = doc->_wanted_nonblocking;
66 _read_state = ISocketStream::RS_reading;
67 _doc = doc;
68
69 if (_doc != nullptr) {
70 _read_index = doc->_read_index;
71 _doc->_transfer_file_size = 0;
72 _doc->_got_transfer_file_size = true;
73
74 // Read a little bit from the file to get the first chunk (and therefore
75 // the file size, or at least the size of the first chunk).
76 underflow();
77 }
78}
79
80/**
81 *
82 */
83void ChunkedStreamBuf::
84close_read() {
85 _source.clear();
86}
87
88/**
89 * Called by the system istream implementation when its internal buffer needs
90 * more characters.
91 */
92int ChunkedStreamBuf::
93underflow() {
94 // Sometimes underflow() is called even if the buffer is not empty.
95 if (gptr() >= egptr()) {
96 size_t buffer_size = egptr() - eback();
97 gbump(-(int)buffer_size);
98
99 size_t num_bytes = buffer_size;
100 size_t read_count = read_chars(gptr(), buffer_size);
101
102 if (read_count != num_bytes) {
103 // Oops, we didn't read what we thought we would.
104 if (read_count == 0) {
105 gbump(num_bytes);
106 return EOF;
107 }
108
109 // Slide what we did read to the top of the buffer.
110 nassertr(read_count < num_bytes, EOF);
111 size_t delta = num_bytes - read_count;
112 memmove(gptr() + delta, gptr(), read_count);
113 gbump(delta);
114 }
115 }
116
117 return (unsigned char)*gptr();
118}
119
120
121/**
122 * Gets some characters from the source stream.
123 */
124size_t ChunkedStreamBuf::
125read_chars(char *start, size_t length) {
126 while (true) {
127 nassertr(!_source.is_null(), 0);
128 if (_done) {
129 return 0;
130 }
131
132 if (_chunk_remaining != 0) {
133 // Extract some of the bytes remaining in the chunk.
134 length = std::min(length, _chunk_remaining);
135 (*_source)->read(start, length);
136 size_t read_count = (*_source)->gcount();
137 if (!_wanted_nonblocking) {
138 while (read_count == 0 && !(*_source)->is_closed()) {
139 // Simulate blocking.
140 thread_yield();
141 (*_source)->read(start, length);
142 read_count = (*_source)->gcount();
143 }
144 }
145 _chunk_remaining -= read_count;
146
147 if (read_count == 0 && (*_source)->is_closed()) {
148 // Whoops, the socket closed while we were downloading.
149 _read_state = ISocketStream::RS_error;
150 }
151
152 return read_count;
153 }
154
155 // Read the next chunk.
156 std::string line;
157 bool got_line = http_getline(line);
158 while (got_line && line.empty()) {
159 // Skip blank lines. There really should be exactly one blank line, but
160 // who's counting? It's tricky to count and maintain reentry for
161 // nonblocking IO.
162 got_line = http_getline(line);
163 }
164 if (!got_line) {
165 // EOF (or data unavailable) while trying to read the chunk size.
166 if ((*_source)->is_closed()) {
167 // Whoops, the socket closed while we were downloading.
168 _read_state = ISocketStream::RS_error;
169 }
170
171 if (!_wanted_nonblocking) {
172 // Simulate blocking.
173 thread_yield();
174 continue; // back to the top.
175 }
176
177 return 0;
178 }
179 size_t chunk_size = (size_t)strtol(line.c_str(), nullptr, 16);
180 if (downloader_cat.is_spam()) {
181 downloader_cat.spam()
182 << "Got chunk of size " << chunk_size << " bytes.\n";
183 }
184
185 if (chunk_size == 0) {
186 // Last chunk; we're done.
187 _done = true;
188 if (_doc != nullptr && _read_index == _doc->_read_index) {
189 _doc->_file_size = _doc->_transfer_file_size;
190 _doc->_got_file_size = true;
191 }
192 _read_state = ISocketStream::RS_complete;
193 return 0;
194 }
195
196 if (_doc != nullptr && _read_index == _doc->_read_index) {
197 _doc->_transfer_file_size += chunk_size;
198 }
199
200 _chunk_remaining = chunk_size;
201
202 // Back to the top.
203 }
204
205 // Never gets here.
206 return 0;
207}
208
209/**
210 * Reads a single line from the stream. Returns true if the line is
211 * successfully retrieved, or false if a complete line has not yet been
212 * received or if the connection has been closed.
213 */
214bool ChunkedStreamBuf::
215http_getline(std::string &str) {
216 nassertr(!_source.is_null(), false);
217 int ch = (*_source)->get();
218 while (!(*_source)->eof() && !(*_source)->fail()) {
219 switch (ch) {
220 case '\n':
221 // end-of-line character, we're done.
222 str = _working_getline;
223 _working_getline = std::string();
224 {
225 // Trim trailing whitespace. We're not required to do this per the
226 // HTTP spec, but let's be generous.
227 size_t p = str.length();
228 while (p > 0 && isspace(str[p - 1])) {
229 --p;
230 }
231 str = str.substr(0, p);
232 }
233
234 return true;
235
236 case '\r':
237 // Ignore CR characters.
238 break;
239
240 default:
241 _working_getline += (char)ch;
242 }
243 ch = (*_source)->get();
244 }
245
246 return false;
247}
248
249#endif // HAVE_OPENSSL
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.