Panda3D
identityStreamBuf.cxx
1 // Filename: identityStreamBuf.cxx
2 // Created by: drose (09Oct02)
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 "identityStreamBuf.h"
16 
17 // This module is not compiled if OpenSSL is not available.
18 #ifdef HAVE_OPENSSL
19 #include "httpChannel.h"
20 
21 #ifndef HAVE_STREAMSIZE
22 // Some compilers (notably SGI) don't define this for us
23 typedef int streamsize;
24 #endif /* HAVE_STREAMSIZE */
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: IdentityStreamBuf::Constructor
28 // Access: Public
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 IdentityStreamBuf::
32 IdentityStreamBuf() {
33  _has_content_length = true;
34  _bytes_remaining = 0;
35  _wanted_nonblocking = false;
36  _read_state = ISocketStream::RS_initial;
37 
38 #ifdef PHAVE_IOSTREAM
39  _buffer = (char *)PANDA_MALLOC_ARRAY(4096);
40  char *ebuf = _buffer + 4096;
41  setg(_buffer, ebuf, ebuf);
42  setp(_buffer, ebuf);
43 
44 #else
45  allocate();
46  setg(base(), ebuf(), ebuf());
47  setp(base(), ebuf());
48 #endif
49 }
50 
51 ////////////////////////////////////////////////////////////////////
52 // Function: IdentityStreamBuf::Destructor
53 // Access: Public, Virtual
54 // Description:
55 ////////////////////////////////////////////////////////////////////
56 IdentityStreamBuf::
57 ~IdentityStreamBuf() {
58  close_read();
59 #ifdef PHAVE_IOSTREAM
60  PANDA_FREE_ARRAY(_buffer);
61 #endif
62 }
63 
64 ////////////////////////////////////////////////////////////////////
65 // Function: IdentityStreamBuf::open_read
66 // Access: Public
67 // Description: If the document pointer is non-NULL, it will be
68 // updated with the length of the file as it is derived
69 // from the identity encoding.
70 ////////////////////////////////////////////////////////////////////
71 void IdentityStreamBuf::
72 open_read(BioStreamPtr *source, HTTPChannel *doc,
73  bool has_content_length, size_t content_length) {
74  _source = source;
75  _has_content_length = has_content_length;
76  _wanted_nonblocking = doc->_wanted_nonblocking;
77  _bytes_remaining = content_length;
78  _read_state = ISocketStream::RS_reading;
79 }
80 
81 ////////////////////////////////////////////////////////////////////
82 // Function: IdentityStreamBuf::close_read
83 // Access: Public
84 // Description:
85 ////////////////////////////////////////////////////////////////////
86 void IdentityStreamBuf::
87 close_read() {
88  _source.clear();
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: IdentityStreamBuf::underflow
93 // Access: Protected, Virtual
94 // Description: Called by the system istream implementation when its
95 // internal buffer needs more characters.
96 ////////////////////////////////////////////////////////////////////
97 int IdentityStreamBuf::
98 underflow() {
99  // Sometimes underflow() is called even if the buffer is not empty.
100  if (gptr() >= egptr()) {
101  size_t buffer_size = egptr() - eback();
102  gbump(-(int)buffer_size);
103 
104  size_t num_bytes = buffer_size;
105  size_t read_count = read_chars(gptr(), buffer_size);
106 
107  if (read_count != num_bytes) {
108  // Oops, we didn't read what we thought we would.
109  if (read_count == 0) {
110  gbump(num_bytes);
111  return EOF;
112  }
113 
114  // Slide what we did read to the top of the buffer.
115  nassertr(read_count < num_bytes, EOF);
116  size_t delta = num_bytes - read_count;
117  memmove(gptr() + delta, gptr(), read_count);
118  gbump(delta);
119  }
120  }
121 
122  return (unsigned char)*gptr();
123 }
124 
125 
126 ////////////////////////////////////////////////////////////////////
127 // Function: IdentityStreamBuf::read_chars
128 // Access: Private
129 // Description: Gets some characters from the source stream.
130 ////////////////////////////////////////////////////////////////////
131 size_t IdentityStreamBuf::
132 read_chars(char *start, size_t length) {
133  size_t read_count = 0;
134 
135  if (!_has_content_length) {
136  // If we have no restrictions on content length, read till end of
137  // file.
138  (*_source)->read(start, length);
139  read_count = (*_source)->gcount();
140 
141  if (!_wanted_nonblocking) {
142  while (read_count == 0 && !(*_source)->is_closed()) {
143  // Simulate blocking.
144  thread_yield();
145  (*_source)->read(start, length);
146  read_count = (*_source)->gcount();
147  }
148  }
149 
150  if (read_count == 0) {
151  if ((*_source)->is_closed()) {
152  // socket closed; we're done.
153  _read_state = ISocketStream::RS_complete;
154  }
155  return 0;
156  }
157 
158  } else {
159  // Extract some of the remaining bytes, but do not read past the
160  // content_length restriction.
161 
162  if (_bytes_remaining != 0) {
163  length = min(length, _bytes_remaining);
164  (*_source)->read(start, length);
165  read_count = (*_source)->gcount();
166  if (!_wanted_nonblocking) {
167  while (read_count == 0 && !(*_source)->is_closed()) {
168  // Simulate blocking.
169  thread_yield();
170  (*_source)->read(start, length);
171  read_count = (*_source)->gcount();
172  }
173  }
174  nassertr(read_count <= _bytes_remaining, 0);
175  _bytes_remaining -= read_count;
176 
177  if (read_count == 0) {
178  if ((*_source)->is_closed()) {
179  // socket closed unexpectedly; problem.
180  _read_state = ISocketStream::RS_error;
181  }
182  return 0;
183  }
184  }
185 
186  if (_bytes_remaining == 0) {
187  // We're done.
188  _read_state = ISocketStream::RS_complete;
189  }
190  }
191 
192  return read_count;
193 }
194 
195 #endif // HAVE_OPENSSL