Panda3D
 All Classes Functions Variables Enumerations
bioStreamBuf.cxx
1 // Filename: bioStreamBuf.cxx
2 // Created by: drose (25Sep02)
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 "bioStreamBuf.h"
16 #include "config_downloader.h"
17 #include "openSSLWrapper.h"
18 #include <errno.h>
19 
20 #ifdef HAVE_OPENSSL
21 
22 #if defined(WIN32_VC) || defined(WIN64_VC)
23  #include <WinSock2.h>
24  #include <windows.h> // for WSAGetLastError()
25  #undef X509_NAME
26 #endif // WIN32_VC
27 
28 #ifndef HAVE_STREAMSIZE
29 // Some compilers (notably SGI) don't define this for us
30 typedef int streamsize;
31 #endif /* HAVE_STREAMSIZE */
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: BioStreamBuf::Constructor
35 // Access: Public
36 // Description:
37 ////////////////////////////////////////////////////////////////////
38 BioStreamBuf::
39 BioStreamBuf() {
40  _read_open = false;
41  _write_open = false;
42 
43 #ifdef PHAVE_IOSTREAM
44  _buffer = (char *)PANDA_MALLOC_ARRAY(8192);
45  char *ebuf = _buffer + 8192;
46  char *mbuf = _buffer + 4096;
47  setg(_buffer, mbuf, mbuf);
48  setp(mbuf, ebuf);
49 
50 #else
51  allocate();
52  // Chop the buffer in half. The bottom half goes to the get buffer;
53  // the top half goes to the put buffer.
54  char *b = base();
55  char *t = ebuf();
56  char *m = b + (t - b) / 2;
57  setg(b, m, m);
58  setp(b, m);
59 #endif
60 }
61 
62 ////////////////////////////////////////////////////////////////////
63 // Function: BioStreamBuf::Destructor
64 // Access: Public, Virtual
65 // Description:
66 ////////////////////////////////////////////////////////////////////
67 BioStreamBuf::
68 ~BioStreamBuf() {
69  close();
70 #ifdef PHAVE_IOSTREAM
71  PANDA_FREE_ARRAY(_buffer);
72 #endif
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: BioStreamBuf::open
77 // Access: Public
78 // Description:
79 ////////////////////////////////////////////////////////////////////
80 void BioStreamBuf::
81 open(BioPtr *source) {
82  _source = source;
83  _read_open = true;
84  _write_open = true;
85 }
86 
87 ////////////////////////////////////////////////////////////////////
88 // Function: BioStreamBuf::close
89 // Access: Public
90 // Description:
91 ////////////////////////////////////////////////////////////////////
92 void BioStreamBuf::
93 close() {
94  sync();
95  _source.clear();
96  _read_open = false;
97  _write_open = false;
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: BioStreamBuf::overflow
102 // Access: Protected, Virtual
103 // Description: Called by the system ostream implementation when its
104 // internal buffer is filled, plus one character.
105 ////////////////////////////////////////////////////////////////////
106 int BioStreamBuf::
107 overflow(int ch) {
108  size_t n = pptr() - pbase();
109  if (downloader_cat.is_spam()) {
110  downloader_cat.spam()
111  << "BioStreamBuf::overflow, " << n << " bytes\n";
112  }
113  if (n != 0) {
114  size_t num_wrote = write_chars(pbase(), n);
115  pbump(-(int)n);
116  if (num_wrote != n) {
117  return EOF;
118  }
119  }
120 
121  if (ch != EOF) {
122  // Store the next character back in the buffer.
123  *pptr() = ch;
124  pbump(1);
125  }
126 
127  return 0;
128 }
129 
130 ////////////////////////////////////////////////////////////////////
131 // Function: BioStreamBuf::sync
132 // Access: Protected, Virtual
133 // Description: Called by the system iostream implementation to
134 // implement a flush operation.
135 ////////////////////////////////////////////////////////////////////
136 int BioStreamBuf::
137 sync() {
138  /*
139  size_t n = egptr() - gptr();
140  gbump(n);
141  */
142 
143  size_t n = pptr() - pbase();
144 
145  if (downloader_cat.is_spam() && n != 0) {
146  downloader_cat.spam()
147  << "BioStreamBuf::sync, " << n << " bytes\n";
148  }
149 
150  size_t num_wrote = write_chars(pbase(), n);
151  pbump(-(int)n);
152 
153  if (num_wrote != n) {
154  return EOF;
155  }
156 
157  return 0;
158 }
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: BioStreamBuf::underflow
162 // Access: Protected, Virtual
163 // Description: Called by the system istream implementation when its
164 // internal buffer needs more characters.
165 ////////////////////////////////////////////////////////////////////
166 int BioStreamBuf::
167 underflow() {
168  // Sometimes underflow() is called even if the buffer is not empty.
169  if (gptr() >= egptr()) {
170  size_t buffer_size = egptr() - eback();
171  gbump(-(int)buffer_size);
172 
173  size_t num_bytes = buffer_size;
174 
175  // BIO_read might return -1 or -2 on eof or error, so we have to
176  // allow for negative numbers.
177  int read_count = BIO_read(*_source, gptr(), buffer_size);
178  thread_consider_yield();
179 
180  if (read_count != (int)num_bytes) {
181  // Oops, we didn't read what we thought we would.
182  if (read_count <= 0) {
183  // Immediately save the os error in case we screw up and do
184  // something that will change its value before we can output
185  // it.
186 #if defined(WIN32_VC) || defined(WIN64_VC)
187  int os_error = WSAGetLastError();
188 #else
189  int os_error = errno;
190 #endif // WIN32_VC
191 
192  // Though BIO_eof() is tempting, it appears there are cases in
193  // which that never returns true, if the socket is closed by
194  // the server. But BIO_should_retry() *appears* to be
195  // reliable.
196  _read_open = (BIO_should_retry(*_source) != 0);
197 
198 #ifdef IS_OSX
199  // occassionally we get -1 on read_open on the mac
200  // the os_error is 35 which means "Resource temporarily unavailable".
201  if (!_read_open && os_error == 35) {
202  downloader_cat.warning() << "forcing retry to true again and _read_open to true\n";
203  BIO_set_retry_read(*_source);
204  _read_open = true;
205  }
206 #endif
207  if (!_read_open) {
208  downloader_cat.info()
209  << "Lost connection to "
210  << _source->get_server_name() << ":"
211  << _source->get_port() << " (" << read_count << ").\n";
212  OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
213 
214  SSL *ssl = NULL;
215  BIO_get_ssl(*_source, &ssl);
216  if (ssl != (SSL *)NULL) {
217  downloader_cat.warning()
218  << "OpenSSL error code: " << SSL_get_error(ssl, read_count)
219  << "\n";
220  }
221 
222 #if defined(WIN32_VC) || defined(WIN64_VC)
223  downloader_cat.warning()
224  << "Windows error code: " << os_error << "\n";
225 #else
226  downloader_cat.warning()
227  << "Unix error code: " << os_error << "\n";
228 #endif // WIN32_VC
229  }
230  gbump(num_bytes);
231  return EOF;
232  }
233 
234  // Slide what we did read to the top of the buffer.
235  nassertr(read_count < (int)num_bytes, EOF);
236  size_t delta = (int)num_bytes - read_count;
237  memmove(gptr() + delta, gptr(), read_count);
238  gbump(delta);
239  }
240 
241  if (downloader_cat.is_spam()) {
242  downloader_cat.spam()
243  << "read " << read_count << " bytes from " << _source << "\n";
244  }
245  }
246 
247  return (unsigned char)*gptr();
248 }
249 
250 ////////////////////////////////////////////////////////////////////
251 // Function: BioStreamBuf::write_chars
252 // Access: Private
253 // Description: Sends some characters to the dest stream. Does not
254 // return until all characters are sent or the socket is
255 // closed, even if the underlying BIO is non-blocking.
256 ////////////////////////////////////////////////////////////////////
257 size_t BioStreamBuf::
258 write_chars(const char *start, size_t length) {
259  if (length != 0) {
260  size_t wrote_so_far = 0;
261 
262  int write_count = BIO_write(*_source, start, length);
263  thread_consider_yield();
264  while (write_count != (int)(length - wrote_so_far)) {
265  if (write_count <= 0) {
266  // http://www.openssl.org/docs/crypto/BIO_s_bio.html
267  // "Calls to BIO_write() will place data in the buffer or
268  // request a retry if the buffer is full."
269  //
270  // when the server is terminated, this seems to be the best
271  // way of detecting that case on the client: a BIO write error
272  // without a retry request
273  //_write_open = BIO_should_retry(*_source);
274  //_write_open = !BIO_eof(*_source);
275  _write_open = (BIO_should_write(*_source) != 0 || BIO_should_retry(*_source) != 0);
276  if (!_write_open) {
277  return wrote_so_far;
278  }
279 
280  // Block on the underlying socket before we try to write some
281  // more.
282  int fd = -1;
283  BIO_get_fd(*_source, &fd);
284  if (fd < 0) {
285  downloader_cat.warning()
286  << "socket BIO has no file descriptor.\n";
287  } else {
288  if (downloader_cat.is_spam()) {
289  downloader_cat.spam()
290  << "waiting to write to BIO.\n";
291  }
292 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
293  // In SIMPLE_THREADS mode, instead of blocking, simply yield
294  // the thread.
295  thread_yield();
296 #else
297  // In any other threading mode, we actually want to block.
298  fd_set wset;
299  FD_ZERO(&wset);
300  FD_SET(fd, &wset);
301  select(fd + 1, NULL, &wset, NULL, NULL);
302 #endif // SIMPLE_THREADS
303  }
304 
305  } else {
306  // wrote some characters.
307  wrote_so_far += write_count;
308  if (downloader_cat.is_spam()) {
309  downloader_cat.spam()
310  << "wrote " << write_count << " bytes to " << _source << "\n";
311  }
312  }
313 
314  // Try to write some more.
315  write_count = BIO_write(*_source, start + wrote_so_far, length - wrote_so_far);
316  if (downloader_cat.is_spam()) {
317  downloader_cat.spam()
318  << "continued, wrote " << write_count << " bytes.\n";
319  }
320  thread_consider_yield();
321  }
322  if (downloader_cat.is_spam()) {
323  downloader_cat.spam()
324  << "wrote " << write_count << " bytes to " << _source << "\n";
325  }
326  }
327 
328  return length;
329 }
330 
331 #endif // HAVE_OPENSSL