// Filename: bioStreamBuf.cxx // Created by: drose (25Sep02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://www.panda3d.org/license.txt . // // To contact the maintainers of this program write to // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// #include "bioStreamBuf.h" #include "config_downloader.h" #include "ssl_utils.h" #ifdef HAVE_SSL #ifndef HAVE_STREAMSIZE // Some compilers (notably SGI) don't define this for us typedef int streamsize; #endif /* HAVE_STREAMSIZE */ //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// BioStreamBuf:: BioStreamBuf() { _is_closed = false; #ifdef HAVE_IOSTREAM char *buf = new char[8192]; char *ebuf = buf + 8192; char *mbuf = buf + 4096; setg(buf, mbuf, mbuf); setp(mbuf, ebuf); #else allocate(); // Chop the buffer in half. The bottom half goes to the get buffer; // the top half goes to the put buffer. char *b = base(); char *t = ebuf(); char *m = b + (t - b) / 2; setg(b, m, m); setp(b, m); #endif } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// BioStreamBuf:: ~BioStreamBuf() { close(); } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::open // Access: Public // Description: //////////////////////////////////////////////////////////////////// void BioStreamBuf:: open(BioPtr *source) { _source = source; } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::close // Access: Public // Description: //////////////////////////////////////////////////////////////////// void BioStreamBuf:: close() { sync(); _source.clear(); } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::overflow // Access: Protected, Virtual // Description: Called by the system ostream implementation when its // internal buffer is filled, plus one character. //////////////////////////////////////////////////////////////////// int BioStreamBuf:: overflow(int ch) { size_t n = pptr() - pbase(); if (n != 0) { size_t num_wrote = write_chars(pbase(), n); pbump(-(int)n); if (num_wrote != n) { return EOF; } } if (ch != EOF) { // Store the next character back in the buffer. *pptr() = ch; pbump(1); } return 0; } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::sync // Access: Protected, Virtual // Description: Called by the system iostream implementation to // implement a flush operation. //////////////////////////////////////////////////////////////////// int BioStreamBuf:: sync() { /* size_t n = egptr() - gptr(); gbump(n); */ size_t n = pptr() - pbase(); size_t num_wrote = write_chars(pbase(), n); pbump(-(int)n); if (num_wrote != n) { return EOF; } return 0; } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::underflow // Access: Protected, Virtual // Description: Called by the system istream implementation when its // internal buffer needs more characters. //////////////////////////////////////////////////////////////////// int BioStreamBuf:: underflow() { // Sometimes underflow() is called even if the buffer is not empty. if (gptr() >= egptr()) { size_t buffer_size = egptr() - eback(); gbump(-(int)buffer_size); size_t num_bytes = buffer_size; // BIO_read might return -1 or -2 on eof or error, so we have to // allow for negative numbers. int read_count = BIO_read(*_source, gptr(), buffer_size); if (read_count != (int)num_bytes) { // Oops, we didn't read what we thought we would. if (read_count <= 0) { _is_closed = !BIO_should_retry(*_source); if (_is_closed) { downloader_cat.info() << "Lost connection to " << _source->get_server_name() << ":" << _source->get_port() << " (" << read_count << ").\n"; notify_ssl_errors(); SSL *ssl; BIO_get_ssl(*_source, &ssl); if (ssl != (SSL *)NULL) { downloader_cat.warning() << "OpenSSL error code: " << SSL_get_error(ssl, read_count) << "\n"; } #ifdef WIN32_VC downloader_cat.warning() << "Windows error code: " << WSAGetLastError() << "\n"; #else downloader_cat.warning() << "Unix error code: " << errno << "\n"; #endif // WIN32_VC } gbump(num_bytes); return EOF; } // Slide what we did read to the top of the buffer. nassertr(read_count < (int)num_bytes, EOF); size_t delta = (int)num_bytes - read_count; memmove(gptr() + delta, gptr(), read_count); gbump(delta); } } return (unsigned char)*gptr(); } //////////////////////////////////////////////////////////////////// // Function: BioStreamBuf::write_chars // Access: Private // Description: Sends some characters to the dest stream. Does not // return until all characters are sent or the socket is // closed, even if the underlying BIO is non-blocking. //////////////////////////////////////////////////////////////////// size_t BioStreamBuf:: write_chars(const char *start, size_t length) { if (length != 0) { size_t wrote_so_far = 0; int write_count = BIO_write(*_source, start, length); while (write_count != (int)(length - wrote_so_far)) { if (write_count <= 0) { _is_closed = !BIO_should_retry(*_source); if (_is_closed) { return wrote_so_far; } // Block on the underlying socket before we try to write some // more. int fd = -1; BIO_get_fd(*_source, &fd); if (fd < 0) { downloader_cat.warning() << "socket BIO has no file descriptor.\n"; } else { downloader_cat.spam() << "waiting to write to BIO.\n"; fd_set wset; FD_ZERO(&wset); FD_SET(fd, &wset); select(fd + 1, NULL, &wset, NULL, NULL); } } else { // wrote some characters. wrote_so_far += write_count; } // Try to write some more. write_count = BIO_write(*_source, start + wrote_so_far, length - wrote_so_far); } } return length; } #endif // HAVE_SSL