From cd7815bb8842c753861c88bd7677f66cdf46d879 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 16 Oct 2002 01:05:04 +0000 Subject: [PATCH] reference-count sockets, prepare for non-blocking i/o --- panda/src/downloader/Sources.pp | 8 + panda/src/downloader/bioPtr.I | 77 +++++ panda/src/downloader/bioPtr.cxx | 84 +++++ panda/src/downloader/bioPtr.h | 69 ++++ panda/src/downloader/bioStream.I | 10 +- panda/src/downloader/bioStream.cxx | 16 + panda/src/downloader/bioStream.h | 9 +- panda/src/downloader/bioStreamBuf.cxx | 17 +- panda/src/downloader/bioStreamBuf.h | 10 +- panda/src/downloader/bioStreamPtr.I | 77 +++++ panda/src/downloader/bioStreamPtr.cxx | 36 ++ panda/src/downloader/bioStreamPtr.h | 61 ++++ panda/src/downloader/chunkedStream.I | 11 +- panda/src/downloader/chunkedStream.cxx | 21 ++ panda/src/downloader/chunkedStream.h | 17 +- panda/src/downloader/chunkedStreamBuf.cxx | 24 +- panda/src/downloader/chunkedStreamBuf.h | 14 +- .../src/downloader/downloader_composite1.cxx | 2 + panda/src/downloader/httpClient.cxx | 34 +- panda/src/downloader/httpClient.h | 3 +- panda/src/downloader/httpDocument.I | 36 +- panda/src/downloader/httpDocument.cxx | 316 ++++++------------ panda/src/downloader/httpDocument.h | 22 +- panda/src/downloader/identityStream.I | 16 +- panda/src/downloader/identityStream.cxx | 22 ++ panda/src/downloader/identityStream.h | 19 +- panda/src/downloader/identityStreamBuf.cxx | 68 ++-- panda/src/downloader/identityStreamBuf.h | 17 +- panda/src/downloader/socketStream.I | 27 ++ panda/src/downloader/socketStream.h | 54 +++ 30 files changed, 854 insertions(+), 343 deletions(-) create mode 100644 panda/src/downloader/bioPtr.I create mode 100644 panda/src/downloader/bioPtr.cxx create mode 100644 panda/src/downloader/bioPtr.h create mode 100644 panda/src/downloader/bioStreamPtr.I create mode 100644 panda/src/downloader/bioStreamPtr.cxx create mode 100644 panda/src/downloader/bioStreamPtr.h create mode 100644 panda/src/downloader/socketStream.I create mode 100644 panda/src/downloader/socketStream.h diff --git a/panda/src/downloader/Sources.pp b/panda/src/downloader/Sources.pp index 87876f993b..7fbeb9f9af 100644 --- a/panda/src/downloader/Sources.pp +++ b/panda/src/downloader/Sources.pp @@ -13,6 +13,8 @@ #define SOURCES \ config_downloader.h \ asyncUtility.I asyncUtility.h \ + bioPtr.I bioPtr.h \ + bioStreamPtr.I bioStreamPtr.h \ bioStream.I bioStream.h bioStreamBuf.h \ chunkedStream.I chunkedStream.h chunkedStreamBuf.h \ extractor.h \ @@ -21,6 +23,7 @@ identityStream.I identityStream.h identityStreamBuf.h \ multiplexStream.I multiplexStream.h \ multiplexStreamBuf.I multiplexStreamBuf.h \ + socketStream.h socketStream.I \ urlSpec.I urlSpec.h \ $[if $[HAVE_NET], downloadDb.I downloadDb.h downloader.I downloader.h] \ $[if $[HAVE_ZLIB], decompressor.h zcompressor.I zcompressor.h download_utils.h] \ @@ -29,6 +32,8 @@ #define INCLUDED_SOURCES \ config_downloader.cxx \ asyncUtility.cxx \ + bioPtr.cxx \ + bioStreamPtr.cxx \ bioStream.cxx bioStreamBuf.cxx \ chunkedStream.cxx chunkedStreamBuf.cxx \ extractor.cxx \ @@ -42,6 +47,8 @@ #define INSTALL_HEADERS \ asyncUtility.h asyncUtility.I \ + bioPtr.I bioPtr.h \ + bioStreamPtr.I bioStreamPtr.h \ bioStream.I bioStream.h bioStreamBuf.h \ chunkedStream.I chunkedStream.h chunkedStreamBuf.h \ config_downloader.h \ @@ -55,6 +62,7 @@ multiplexStream.I multiplexStream.h \ multiplexStreamBuf.I multiplexStreamBuf.I \ patcher.h patcher.I \ + socketStream.h socketStream.I \ urlSpec.h urlSpec.I \ zcompressor.I zcompressor.h diff --git a/panda/src/downloader/bioPtr.I b/panda/src/downloader/bioPtr.I new file mode 100644 index 0000000000..d8414fc312 --- /dev/null +++ b/panda/src/downloader/bioPtr.I @@ -0,0 +1,77 @@ +// Filename: bioPtr.I +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BioPtr:: +BioPtr(BIO *bio) : _bio(bio) { +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::operator * +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BIO &BioPtr:: +operator *() const { + return *_bio; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::operator -> +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BIO *BioPtr:: +operator ->() const { + return _bio; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::operator typecast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BioPtr:: +operator BIO * () const { + return _bio; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::get_bio +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void BioPtr:: +set_bio(BIO *bio) { + _bio = bio; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::get_bio +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BIO *BioPtr:: +get_bio() const { + return _bio; +} diff --git a/panda/src/downloader/bioPtr.cxx b/panda/src/downloader/bioPtr.cxx new file mode 100644 index 0000000000..2d709403f5 --- /dev/null +++ b/panda/src/downloader/bioPtr.cxx @@ -0,0 +1,84 @@ +// Filename: bioPtr.cxx +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "bioPtr.h" + +#ifdef HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::Constructor +// Access: Public +// Description: This flavor of the constructor automatically creates +// a socket BIO and feeds it the server and port name +// from the indicated URL. It doesn't call +// BIO_do_connect(), though. +//////////////////////////////////////////////////////////////////// +BioPtr:: +BioPtr(const URLSpec &url) { + _server_name = url.get_server(); + _port = url.get_port(); + _bio = BIO_new_connect((char *)_server_name.c_str()); + BIO_set_conn_int_port(_bio, &_port); +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BioPtr:: +~BioPtr() { + if (_bio != (BIO *)NULL) { + if (downloader_cat.is_debug() && !_server_name.empty()) { + downloader_cat.debug() + << "Dropping connection to " << _server_name << ":" << _port << "\n"; + } + + BIO_free_all(_bio); + _bio = (BIO *)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BioPtr::connect +// Access: Public +// Description: Calls BIO_do_connect() to establish a connection on +// the previously-named server and port. Returns true +// if successful, false otherwise. +//////////////////////////////////////////////////////////////////// +bool BioPtr:: +connect() const { + nassertr(_bio != (BIO *)NULL && !_server_name.empty(), false); + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Connecting to " << _server_name << ":" << _port << "\n"; + } + + if (BIO_do_connect(_bio) <= 0) { + downloader_cat.info() + << "Could not connect to " << _server_name << ":" << _port << "\n"; +#ifdef REPORT_SSL_ERRORS + ERR_print_errors_fp(stderr); +#endif + return false; + } + + return true; +} + +#endif // HAVE_SSL diff --git a/panda/src/downloader/bioPtr.h b/panda/src/downloader/bioPtr.h new file mode 100644 index 0000000000..299cab767a --- /dev/null +++ b/panda/src/downloader/bioPtr.h @@ -0,0 +1,69 @@ +// Filename: bioPtr.h +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + +#ifndef BIOPTR_H +#define BIOPTR_H + +#include "pandabase.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +#include "referenceCount.h" +#include + +class URLSpec; + +//////////////////////////////////////////////////////////////////// +// Class : BioPtr +// Description : A wrapper around an OpenSSL BIO object to make a +// reference-counting pointer to it. It appears that +// the OpenSSL library already uses reference counts on +// these things internally, but the interface doesn't +// appear to be public; so we might as well wrap the +// whole thing at the high level. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS BioPtr : public ReferenceCount { +public: + INLINE BioPtr(BIO *bio); + BioPtr(const URLSpec &url); + virtual ~BioPtr(); + + INLINE BIO &operator *() const; + INLINE BIO *operator -> () const; + INLINE operator BIO * () const; + + INLINE void set_bio(BIO *bio); + INLINE BIO *get_bio() const; + + bool connect() const; + +private: + BIO *_bio; + string _server_name; + int _port; +}; + +#include "bioPtr.I" + +#endif // HAVE_SSL + + +#endif + + diff --git a/panda/src/downloader/bioStream.I b/panda/src/downloader/bioStream.I index 661d672fa3..b48a8aafeb 100644 --- a/panda/src/downloader/bioStream.I +++ b/panda/src/downloader/bioStream.I @@ -23,7 +23,7 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE IBioStream:: -IBioStream() : istream(&_buf) { +IBioStream() : ISocketStream(&_buf) { } //////////////////////////////////////////////////////////////////// @@ -32,8 +32,8 @@ IBioStream() : istream(&_buf) { // Description: //////////////////////////////////////////////////////////////////// INLINE IBioStream:: -IBioStream(BIO *source, bool owns_source) : istream(&_buf) { - open(source, owns_source); +IBioStream(BioPtr *source) : ISocketStream(&_buf) { + open(source); } //////////////////////////////////////////////////////////////////// @@ -42,9 +42,9 @@ IBioStream(BIO *source, bool owns_source) : istream(&_buf) { // Description: //////////////////////////////////////////////////////////////////// INLINE IBioStream &IBioStream:: -open(BIO *source, bool owns_source) { +open(BioPtr *source) { clear(0); - _buf.open_read(source, owns_source); + _buf.open_read(source); return *this; } diff --git a/panda/src/downloader/bioStream.cxx b/panda/src/downloader/bioStream.cxx index ab87fa3319..09bca3ac5f 100644 --- a/panda/src/downloader/bioStream.cxx +++ b/panda/src/downloader/bioStream.cxx @@ -17,3 +17,19 @@ //////////////////////////////////////////////////////////////////// #include "bioStream.h" + +//////////////////////////////////////////////////////////////////// +// Function: IBioStream::is_closed +// Access: Public, Virtual +// Description: Returns true if the last eof condition was triggered +// because the socket has genuinely closed, or false if +// we can expect more data to come along shortly. +//////////////////////////////////////////////////////////////////// +INLINE bool IBioStream:: +is_closed() { + if (_buf._is_closed) { + return true; + } + clear(); + return false; +} diff --git a/panda/src/downloader/bioStream.h b/panda/src/downloader/bioStream.h index 6a10e6f390..e22dd50723 100644 --- a/panda/src/downloader/bioStream.h +++ b/panda/src/downloader/bioStream.h @@ -24,6 +24,7 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_SSL +#include "socketStream.h" #include "bioStreamBuf.h" //////////////////////////////////////////////////////////////////// @@ -35,14 +36,16 @@ // // Seeking is not supported. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDAEXPRESS IBioStream : public istream { +class EXPCL_PANDAEXPRESS IBioStream : public ISocketStream { public: INLINE IBioStream(); - INLINE IBioStream(BIO *source, bool owns_source); + INLINE IBioStream(BioPtr *source); - INLINE IBioStream &open(BIO *source, bool owns_source); + INLINE IBioStream &open(BioPtr *source); INLINE IBioStream &close(); + virtual bool is_closed(); + private: BioStreamBuf _buf; }; diff --git a/panda/src/downloader/bioStreamBuf.cxx b/panda/src/downloader/bioStreamBuf.cxx index 3e230eeaa9..bac868129a 100644 --- a/panda/src/downloader/bioStreamBuf.cxx +++ b/panda/src/downloader/bioStreamBuf.cxx @@ -32,8 +32,7 @@ typedef int streamsize; //////////////////////////////////////////////////////////////////// BioStreamBuf:: BioStreamBuf() { - _source = (BIO *)NULL; - _owns_source = false; + _is_closed = false; #ifdef WIN32_VC // In spite of the claims of the MSDN Library to the contrary, @@ -67,9 +66,8 @@ BioStreamBuf:: // Description: //////////////////////////////////////////////////////////////////// void BioStreamBuf:: -open_read(BIO *source, bool owns_source) { +open_read(BioPtr *source) { _source = source; - _owns_source = owns_source; } //////////////////////////////////////////////////////////////////// @@ -79,13 +77,7 @@ open_read(BIO *source, bool owns_source) { //////////////////////////////////////////////////////////////////// void BioStreamBuf:: close_read() { - if (_source != (BIO *)NULL) { - if (_owns_source) { - BIO_free_all(_source); - _owns_source = false; - } - _source = (BIO *)NULL; - } + _source.clear(); } //////////////////////////////////////////////////////////////////// @@ -105,11 +97,12 @@ underflow() { // 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); + 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); return EOF; } diff --git a/panda/src/downloader/bioStreamBuf.h b/panda/src/downloader/bioStreamBuf.h index e0b93fc989..7f605afa01 100644 --- a/panda/src/downloader/bioStreamBuf.h +++ b/panda/src/downloader/bioStreamBuf.h @@ -24,6 +24,8 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_SSL +#include "bioPtr.h" +#include "pointerTo.h" #include //////////////////////////////////////////////////////////////////// @@ -36,15 +38,17 @@ public: BioStreamBuf(); virtual ~BioStreamBuf(); - void open_read(BIO *source, bool owns_source); + void open_read(BioPtr *source); void close_read(); protected: virtual int underflow(void); private: - BIO *_source; - bool _owns_source; + PT(BioPtr) _source; + bool _is_closed; + + friend class IBioStream; }; #endif // HAVE_SSL diff --git a/panda/src/downloader/bioStreamPtr.I b/panda/src/downloader/bioStreamPtr.I new file mode 100644 index 0000000000..180ae2be1a --- /dev/null +++ b/panda/src/downloader/bioStreamPtr.I @@ -0,0 +1,77 @@ +// Filename: bioStreamPtr.I +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BioStreamPtr:: +BioStreamPtr(IBioStream *stream) : _stream(stream) { +} + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::operator * +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IBioStream &BioStreamPtr:: +operator *() const { + return *_stream; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::operator -> +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IBioStream *BioStreamPtr:: +operator ->() const { + return _stream; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::operator typecast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BioStreamPtr:: +operator IBioStream * () const { + return _stream; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::get_stream +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void BioStreamPtr:: +set_stream(IBioStream *stream) { + _stream = stream; +} + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::get_stream +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IBioStream *BioStreamPtr:: +get_stream() const { + return _stream; +} diff --git a/panda/src/downloader/bioStreamPtr.cxx b/panda/src/downloader/bioStreamPtr.cxx new file mode 100644 index 0000000000..d68586a488 --- /dev/null +++ b/panda/src/downloader/bioStreamPtr.cxx @@ -0,0 +1,36 @@ +// Filename: bioStreamPtr.cxx +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "bioPtr.h" + +#ifdef HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Function: BioStreamPtr::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BioStreamPtr:: +~BioStreamPtr() { + if (_stream != (IBioStream *)NULL) { + delete _stream; + _stream = (IBioStream *)NULL; + } +} + +#endif // HAVE_SSL diff --git a/panda/src/downloader/bioStreamPtr.h b/panda/src/downloader/bioStreamPtr.h new file mode 100644 index 0000000000..44946e642f --- /dev/null +++ b/panda/src/downloader/bioStreamPtr.h @@ -0,0 +1,61 @@ +// Filename: bioStreamPtr.h +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + +#ifndef BIOSTREAMPTR_H +#define BIOSTREAMPTR_H + +#include "pandabase.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +#include "bioStream.h" +#include "referenceCount.h" +#include + +//////////////////////////////////////////////////////////////////// +// Class : BioStreamPtr +// Description : A wrapper around an IBioStream object to make a +// reference-counting pointer to it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS BioStreamPtr : public ReferenceCount { +public: + INLINE BioStreamPtr(IBioStream *stream); + virtual ~BioStreamPtr(); + + INLINE IBioStream &operator *() const; + INLINE IBioStream *operator -> () const; + INLINE operator IBioStream * () const; + + INLINE void set_stream(IBioStream *stream); + INLINE IBioStream *get_stream() const; + + bool connect() const; + +private: + IBioStream *_stream; +}; + +#include "bioStreamPtr.I" + +#endif // HAVE_SSL + + +#endif + + diff --git a/panda/src/downloader/chunkedStream.I b/panda/src/downloader/chunkedStream.I index 9915dda94c..a74206236b 100644 --- a/panda/src/downloader/chunkedStream.I +++ b/panda/src/downloader/chunkedStream.I @@ -23,7 +23,7 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE IChunkedStream:: -IChunkedStream() : istream(&_buf) { +IChunkedStream() : ISocketStream(&_buf) { } //////////////////////////////////////////////////////////////////// @@ -32,9 +32,8 @@ IChunkedStream() : istream(&_buf) { // Description: //////////////////////////////////////////////////////////////////// INLINE IChunkedStream:: -IChunkedStream(istream *source, bool owns_source, - HTTPDocument *doc) : istream(&_buf) { - open(source, owns_source, doc); +IChunkedStream(BioStreamPtr *source, HTTPDocument *doc) : ISocketStream(&_buf) { + open(source, doc); } //////////////////////////////////////////////////////////////////// @@ -43,9 +42,9 @@ IChunkedStream(istream *source, bool owns_source, // Description: //////////////////////////////////////////////////////////////////// INLINE IChunkedStream &IChunkedStream:: -open(istream *source, bool owns_source, HTTPDocument *doc) { +open(BioStreamPtr *source, HTTPDocument *doc) { clear(0); - _buf.open_read(source, owns_source, doc); + _buf.open_read(source, doc); return *this; } diff --git a/panda/src/downloader/chunkedStream.cxx b/panda/src/downloader/chunkedStream.cxx index 7667efbf30..cfa3f1d002 100644 --- a/panda/src/downloader/chunkedStream.cxx +++ b/panda/src/downloader/chunkedStream.cxx @@ -17,3 +17,24 @@ //////////////////////////////////////////////////////////////////// #include "chunkedStream.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Function: IChunkedStream::is_closed +// Access: Public, Virtual +// Description: Returns true if the last eof condition was triggered +// because the socket has genuinely closed, or false if +// we can expect more data to come along shortly. +//////////////////////////////////////////////////////////////////// +INLINE bool IChunkedStream:: +is_closed() { + if (_buf._done || (*_buf._source)->is_closed()) { + return true; + } + clear(); + return false; +} + +#endif // HAVE_SSL diff --git a/panda/src/downloader/chunkedStream.h b/panda/src/downloader/chunkedStream.h index 3cae0aa543..37b658fb39 100644 --- a/panda/src/downloader/chunkedStream.h +++ b/panda/src/downloader/chunkedStream.h @@ -21,9 +21,14 @@ #include "pandabase.h" +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +#include "socketStream.h" #include "chunkedStreamBuf.h" class HTTPDocument; +class BioStreamPtr; //////////////////////////////////////////////////////////////////// // Class : IChunkedStream @@ -34,22 +39,24 @@ class HTTPDocument; // Seeking is not supported. //////////////////////////////////////////////////////////////////// // No need to export from DLL. -class IChunkedStream : public istream { +class IChunkedStream : public ISocketStream { public: INLINE IChunkedStream(); - INLINE IChunkedStream(istream *source, bool owns_source, - HTTPDocument *doc); + INLINE IChunkedStream(BioStreamPtr *source, HTTPDocument *doc); - INLINE IChunkedStream &open(istream *source, bool owns_source, - HTTPDocument *doc); + INLINE IChunkedStream &open(BioStreamPtr *source, HTTPDocument *doc); INLINE IChunkedStream &close(); + virtual bool is_closed(); + private: ChunkedStreamBuf _buf; }; #include "chunkedStream.I" +#endif // HAVE_SSL + #endif diff --git a/panda/src/downloader/chunkedStreamBuf.cxx b/panda/src/downloader/chunkedStreamBuf.cxx index 557f6b7b8d..01cb682ab8 100644 --- a/panda/src/downloader/chunkedStreamBuf.cxx +++ b/panda/src/downloader/chunkedStreamBuf.cxx @@ -18,6 +18,9 @@ #include "chunkedStreamBuf.h" +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + #ifndef HAVE_STREAMSIZE // Some compilers (notably SGI) don't define this for us typedef int streamsize; @@ -30,8 +33,6 @@ typedef int streamsize; //////////////////////////////////////////////////////////////////// ChunkedStreamBuf:: ChunkedStreamBuf() { - _source = (istream *)NULL; - _owns_source = false; _chunk_remaining = 0; _done = true; @@ -69,9 +70,8 @@ ChunkedStreamBuf:: // from the chunked encoding. //////////////////////////////////////////////////////////////////// void ChunkedStreamBuf:: -open_read(istream *source, bool owns_source, HTTPDocument *doc) { +open_read(BioStreamPtr *source, HTTPDocument *doc) { _source = source; - _owns_source = owns_source; _chunk_remaining = 0; _done = false; _doc = doc; @@ -94,13 +94,7 @@ open_read(istream *source, bool owns_source, HTTPDocument *doc) { //////////////////////////////////////////////////////////////////// void ChunkedStreamBuf:: close_read() { - if (_source != (istream *)NULL) { - if (_owns_source) { - delete _source; - _owns_source = false; - } - _source = (istream *)NULL; - } + _source.clear(); } //////////////////////////////////////////////////////////////////// @@ -151,15 +145,15 @@ read_chars(char *start, size_t length) { if (_chunk_remaining != 0) { // Extract some of the bytes remaining in the chunk. length = min(length, _chunk_remaining); - _source->read(start, length); - length = _source->gcount(); + (*_source)->read(start, length); + length = (*_source)->gcount(); _chunk_remaining -= length; return length; } // Read the next chunk. string line; - getline(*_source, line); + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } @@ -180,3 +174,5 @@ read_chars(char *start, size_t length) { _chunk_remaining = chunk_size; return read_chars(start, length); } + +#endif // HAVE_SSL diff --git a/panda/src/downloader/chunkedStreamBuf.h b/panda/src/downloader/chunkedStreamBuf.h index 6f7a1c2e93..a6d12d7540 100644 --- a/panda/src/downloader/chunkedStreamBuf.h +++ b/panda/src/downloader/chunkedStreamBuf.h @@ -20,7 +20,12 @@ #define CHUNKEDSTREAMBUF_H #include "pandabase.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + #include "httpDocument.h" +#include "bioStreamPtr.h" #include "pointerTo.h" //////////////////////////////////////////////////////////////////// @@ -34,7 +39,7 @@ public: ChunkedStreamBuf(); virtual ~ChunkedStreamBuf(); - void open_read(istream *source, bool owns_source, HTTPDocument *doc); + void open_read(BioStreamPtr *source, HTTPDocument *doc); void close_read(); protected: @@ -43,13 +48,16 @@ protected: private: size_t read_chars(char *start, size_t length); - istream *_source; - bool _owns_source; + PT(BioStreamPtr) _source; size_t _chunk_remaining; bool _done; PT(HTTPDocument) _doc; int _read_index; + + friend class IChunkedStream; }; +#endif // HAVE_SSL + #endif diff --git a/panda/src/downloader/downloader_composite1.cxx b/panda/src/downloader/downloader_composite1.cxx index f70d25b31b..019c7bc546 100644 --- a/panda/src/downloader/downloader_composite1.cxx +++ b/panda/src/downloader/downloader_composite1.cxx @@ -1,5 +1,7 @@ #include "config_downloader.cxx" #include "asyncUtility.cxx" +#include "bioPtr.cxx" +#include "bioStreamPtr.cxx" #include "bioStream.cxx" #include "bioStreamBuf.cxx" #include "chunkedStream.cxx" diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 68663d28d0..5a04f5d1b7 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -286,7 +286,7 @@ clear_expected_servers() { } //////////////////////////////////////////////////////////////////// -// Function: HTTPClient::get_documents +// Function: HTTPClient::make_channel // Access: Published // Description: Returns a new HTTPDocument object that may be used // for reading multiple documents using the same @@ -295,12 +295,28 @@ clear_expected_servers() { // thus forcing a new connection for each document). //////////////////////////////////////////////////////////////////// PT(HTTPDocument) HTTPClient:: -get_documents() { +make_channel() { PT(HTTPDocument) doc = new HTTPDocument(this); doc->set_persistent_connection(true); return doc; } +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::post_form +// Access: Published +// Description: Posts form data to a particular URL and retrieves the +// response. Returns a new HTTPDocument object whether +// the document is successfully read or not; you can +// test is_valid() and get_return_code() to determine +// whether the document was retrieved. +//////////////////////////////////////////////////////////////////// +PT(HTTPDocument) HTTPClient:: +post_form(const URLSpec &url, const string &body) { + PT(HTTPDocument) doc = new HTTPDocument(this); + doc->post_form(url, body); + return doc; +} + //////////////////////////////////////////////////////////////////// // Function: HTTPClient::get_document // Access: Published @@ -314,7 +330,11 @@ get_documents() { PT(HTTPDocument) HTTPClient:: get_document(const URLSpec &url, const string &body) { PT(HTTPDocument) doc = new HTTPDocument(this); - doc->get_document(url, body); + if (body.empty()) { + doc->get_document(url); + } else { + doc->post_form(url, body); + } return doc; } @@ -322,10 +342,10 @@ get_document(const URLSpec &url, const string &body) { // Function: HTTPClient::get_header // Access: Published // Description: Like get_document(), except only the header -// associated with the file is retrieved. This may be -// used to test for existence of the file; it might also -// return the size of the file (if the server gives us -// this information). +// associated with the document is retrieved. This may +// be used to test for existence of the document; it +// might also return the size of the document (if the +// server gives us this information). //////////////////////////////////////////////////////////////////// PT(HTTPDocument) HTTPClient:: get_header(const URLSpec &url) { diff --git a/panda/src/downloader/httpClient.h b/panda/src/downloader/httpClient.h index 3dd199d9b3..b0d346d82a 100644 --- a/panda/src/downloader/httpClient.h +++ b/panda/src/downloader/httpClient.h @@ -89,7 +89,8 @@ PUBLISHED: bool add_expected_server(const string &server_attributes); void clear_expected_servers(); - PT(HTTPDocument) get_documents(); + PT(HTTPDocument) make_channel(); + PT(HTTPDocument) post_form(const URLSpec &url, const string &body); PT(HTTPDocument) get_document(const URLSpec &url, const string &body = string()); PT(HTTPDocument) get_header(const URLSpec &url); diff --git a/panda/src/downloader/httpDocument.I b/panda/src/downloader/httpDocument.I index 46d5dd015e..954b663f89 100644 --- a/panda/src/downloader/httpDocument.I +++ b/panda/src/downloader/httpDocument.I @@ -25,8 +25,7 @@ //////////////////////////////////////////////////////////////////// INLINE bool HTTPDocument:: is_valid() const { - return (_source != (IBioStream *)NULL && - (_status_code / 100) == 2); + return (!_source.is_null() && (_status_code / 100) == 2); } //////////////////////////////////////////////////////////////////// @@ -183,30 +182,35 @@ get_file_size() const { } //////////////////////////////////////////////////////////////////// -// Function: HTTPDocument::get_document +// Function: HTTPDocument::post_form // Access: Published -// Description: Opens the named document for reading, or if body is -// nonempty, posts data for a particular URL and -// retrieves the response. +// Description: Posts form data to a particular URL and retrieves the +// response. //////////////////////////////////////////////////////////////////// INLINE bool HTTPDocument:: -get_document(const URLSpec &url, const string &body) { - const char *method = "GET"; - if (!body.empty()) { - method = "POST"; - } +post_form(const URLSpec &url, const string &body) { + return send_request("POST", url, body); +} - return send_request(method, url, body); +//////////////////////////////////////////////////////////////////// +// Function: HTTPDocument::get_document +// Access: Published +// Description: Opens the named document for reading, if available. +// Returns true if successful, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool HTTPDocument:: +get_document(const URLSpec &url) { + return send_request("GET", url, string()); } //////////////////////////////////////////////////////////////////// // Function: HTTPDocument::get_header // Access: Published // Description: Like get_document(), except only the header -// associated with the file is retrieved. This may be -// used to test for existence of the file; it might also -// return the size of the file (if the server gives us -// this information). +// associated with the document is retrieved. This may +// be used to test for existence of the document; it +// might also return the size of the document (if the +// server gives us this information). //////////////////////////////////////////////////////////////////// INLINE bool HTTPDocument:: get_header(const URLSpec &url) { diff --git a/panda/src/downloader/httpDocument.cxx b/panda/src/downloader/httpDocument.cxx index f0fa242341..a4c3518039 100644 --- a/panda/src/downloader/httpDocument.cxx +++ b/panda/src/downloader/httpDocument.cxx @@ -43,12 +43,9 @@ static const char base64_table[64] = { // Description: //////////////////////////////////////////////////////////////////// HTTPDocument:: -HTTPDocument(HTTPClient *client, BIO *bio) : - _client(client), - _bio(bio) +HTTPDocument(HTTPClient *client) : + _client(client) { - _owns_bio = false; - _source = (IBioStream *)NULL; _persistent_connection = false; _state = S_new; _read_index = 0; @@ -77,13 +74,13 @@ send_request(const string &method, const URLSpec &url, const string &body) { // Let's call this before we call make_header(), so we'll get the // right HTTP version and proxy information etc. set_url(url); - if (!prepare_for_next()) { + if (!prepare_for_next(true)) { return false; } string header; make_header(header, method, url, body); - send_request(header, body); + send_request(header, body, true); if ((get_status_code() / 100) == 3 && get_status_code() != 305) { // Redirect. Should we handle it automatically? @@ -103,9 +100,9 @@ send_request(const string &method, const URLSpec &url, const string &body) { new_url.set_username(url.get_username()); } set_url(new_url); - if (prepare_for_next()) { + if (prepare_for_next(true)) { make_header(header, method, new_url, body); - send_request(header, body); + send_request(header, body, true); keep_going = ((get_status_code() / 100) == 3 && get_status_code() != 305); } @@ -125,8 +122,8 @@ send_request(const string &method, const URLSpec &url, const string &body) { // that have already been defined. //////////////////////////////////////////////////////////////////// bool HTTPDocument:: -send_request(const string &header, const string &body) { - if (prepare_for_next()) { +send_request(const string &header, const string &body, bool allow_reconnect) { + if (prepare_for_next(allow_reconnect)) { // Tack on a proxy authorization if it is called for. Assume we // can use the same authorization we used last time. string proxy_auth_header = header; @@ -149,7 +146,7 @@ send_request(const string &header, const string &body) { proxy_auth_header += "Proxy-Authorization: "; proxy_auth_header += _client->_proxy_authorization; proxy_auth_header += "\r\n"; - if (prepare_for_next()) { + if (prepare_for_next(allow_reconnect)) { issue_request(proxy_auth_header, body); } } @@ -165,7 +162,7 @@ send_request(const string &header, const string &body) { web_auth_header += "Authorization: "; web_auth_header += authorization; web_auth_header += "\r\n"; - if (prepare_for_next()) { + if (prepare_for_next(allow_reconnect)) { issue_request(web_auth_header, body); } } @@ -254,11 +251,7 @@ will_close_connection() const { //////////////////////////////////////////////////////////////////// istream *HTTPDocument:: open_read_file() const { - // TODO: make this smarter about reference-counting the source - // stream properly so we can return an istream and not worry about - // future interference to or from the HTTPDocument. - bool persist = (get_persistent_connection() && !will_close_connection()); - return ((HTTPDocument *)this)->read_body(!persist); + return ((HTTPDocument *)this)->read_body(); } //////////////////////////////////////////////////////////////////// @@ -291,6 +284,45 @@ write_headers(ostream &out) const { } } +//////////////////////////////////////////////////////////////////// +// Function: HTTPDocument::read_body +// Access: Published +// Description: Returns a newly-allocated istream suitable for +// reading the body of the document. +//////////////////////////////////////////////////////////////////// +ISocketStream *HTTPDocument:: +read_body() { + if (_state != S_read_header) { + return NULL; + } + + string transfer_coding = downcase(get_header_value("Transfer-Encoding")); + string content_length = get_header_value("Content-Length"); + + ISocketStream *result; + if (transfer_coding == "chunked") { + // "chunked" transfer encoding. This means we will have to decode + // the length of the file as we read it in chunks. The + // IChunkedStream does this. + _file_size = 0; + _state = S_started_body; + _read_index++; + result = new IChunkedStream(_source, (HTTPDocument *)this); + + } else { + // If the transfer encoding is anything else, assume "identity". + // This is just the literal characters following the header, up + // until _file_size bytes have been read (if content-length was + // specified), or till end of file otherwise. + _state = S_started_body; + _read_index++; + result = new IIdentityStream(_source, (HTTPDocument *)this, + !content_length.empty(), _file_size); + } + + return result; +} + //////////////////////////////////////////////////////////////////// // Function: HTTPDocument::establish_connection // Access: Private @@ -302,8 +334,6 @@ write_headers(ostream &out) const { //////////////////////////////////////////////////////////////////// bool HTTPDocument:: establish_connection() { - nassertr(_bio == (BIO *)NULL, false); - bool result; if (_proxy.empty()) { if (_url.get_scheme() == "https") { @@ -330,25 +360,9 @@ establish_connection() { //////////////////////////////////////////////////////////////////// bool HTTPDocument:: establish_http() { - ostringstream server; - server << _url.get_server() << ":" << _url.get_port(); - string server_str = server.str(); - - _bio = BIO_new_connect((char *)server_str.c_str()); - - if (downloader_cat.is_debug()) { - downloader_cat.debug() << "connecting to " << server_str << "\n"; - } - if (BIO_do_connect(_bio) <= 0) { - downloader_cat.info() - << "Could not contact server " << server_str << "\n"; -#ifdef REPORT_SSL_ERRORS - ERR_print_errors_fp(stderr); -#endif - return false; - } - - return true; + _bio = new BioPtr(_url); + _source = new BioStreamPtr(new IBioStream(_bio)); + return _bio->connect(); } //////////////////////////////////////////////////////////////////// @@ -359,21 +373,9 @@ establish_http() { //////////////////////////////////////////////////////////////////// bool HTTPDocument:: establish_https() { - ostringstream server; - server << _url.get_server() << ":" << _url.get_port(); - string server_str = server.str(); - - _bio = BIO_new_connect((char *)server_str.c_str()); - - if (downloader_cat.is_debug()) { - downloader_cat.debug() << "connecting to " << server_str << "\n"; - } - if (BIO_do_connect(_bio) <= 0) { - downloader_cat.info() - << "Could not contact server " << server_str << "\n"; -#ifdef REPORT_SSL_ERRORS - ERR_print_errors_fp(stderr); -#endif + _bio = new BioPtr(_url); + _source = new BioStreamPtr(new IBioStream(_bio)); + if (!_bio->connect()) { return false; } @@ -388,26 +390,9 @@ establish_https() { //////////////////////////////////////////////////////////////////// bool HTTPDocument:: establish_http_proxy() { - ostringstream proxy_server; - proxy_server << _proxy.get_server() << ":" << _proxy.get_port(); - string proxy_server_str = proxy_server.str(); - - _bio = BIO_new_connect((char *)proxy_server_str.c_str()); - - if (downloader_cat.is_debug()) { - downloader_cat.debug() - << "connecting to proxy " << proxy_server_str << "\n"; - } - if (BIO_do_connect(_bio) <= 0) { - downloader_cat.info() - << "Could not contact proxy " << proxy_server_str << "\n"; -#ifdef REPORT_SSL_ERRORS - ERR_print_errors_fp(stderr); -#endif - return false; - } - - return true; + _bio = new BioPtr(_proxy); + _source = new BioStreamPtr(new IBioStream(_bio)); + return _bio->connect(); } //////////////////////////////////////////////////////////////////// @@ -419,13 +404,10 @@ establish_http_proxy() { bool HTTPDocument:: establish_https_proxy() { // First, ask the proxy to open a connection for us. - ostringstream proxy_server; - proxy_server << _proxy.get_server() << ":" << _proxy.get_port(); - string proxy_server_str = proxy_server.str(); - - if (downloader_cat.is_debug()) { - downloader_cat.debug() - << "connecting to proxy " << proxy_server_str << "\n"; + _bio = new BioPtr(_proxy); + _source = new BioStreamPtr(new IBioStream(_bio)); + if (!_bio->connect()) { + return false; } ostringstream request; @@ -438,54 +420,28 @@ establish_https_proxy() { } string connect_header = request.str(); - _bio = BIO_new_connect((char *)proxy_server_str.c_str()); - if (BIO_do_connect(_bio) <= 0) { - downloader_cat.info() - << "Could not contact proxy " << proxy_server_str << ".\n"; -#ifdef REPORT_SSL_ERRORS - ERR_print_errors_fp(stderr); -#endif - return false; - } - // Now issue the request and read the response from the proxy. - // Temporarily flag our bio as being owned externally, so our call - // to send_request() won't end up with a recursive call back to - // establish_connection(). - _owns_bio = false; - string old_proxy_authorization = _client->_proxy_authorization; - bool connected = send_request(connect_header, string()); + bool connected = send_request(connect_header, string(), false); if (!connected && get_status_code() == 407 && _client->_proxy_authorization != old_proxy_authorization) { // If we ended up with a 407 (proxy authorization required), and // we changed authorization strings recently, then try the new // authorization string, once. (Normally, send_request() would // have tried it again automatically, but we may have prevented - // that by setting _owns_bio to false.) - if (!prepare_for_next()) { + // that by passing false allow_reconnect as false.) + if (!prepare_for_next(true)) { free_bio(); - _bio = BIO_new_connect((char *)proxy_server_str.c_str()); - if (BIO_do_connect(_bio) <= 0) { - downloader_cat.info() - << "Could not contact proxy " << proxy_server_str - << " a second time.\n"; -#ifdef REPORT_SSL_ERRORS - ERR_print_errors_fp(stderr); -#endif - _owns_bio = true; + _bio = new BioPtr(_proxy); + _source = new BioStreamPtr(new IBioStream(_bio)); + if (!_bio->connect()) { return false; } } - connected = send_request(connect_header, string()); + connected = send_request(connect_header, string(), false); } - // Now that we've connected, be honest with the _owns_bio flag: if - // we're here, we know we really do own the BIO pointer (we just - // created it, after all.) - _owns_bio = true; - if (!connected) { downloader_cat.info() << "proxy would not open connection to " << _url.get_authority() @@ -538,7 +494,7 @@ establish_https_proxy() { bool HTTPDocument:: make_https_connection() { BIO *sbio = BIO_new_ssl(_client->_ssl_ctx, true); - BIO_push(sbio, _bio); + BIO_push(sbio, *_bio); SSL *ssl; BIO_get_ssl(sbio, &ssl); @@ -563,7 +519,7 @@ make_https_connection() { // Now that we've made an SSL handshake, we can use the SSL bio to // do all of our communication henceforth. - _bio = sbio; + _bio->set_bio(sbio); long verify_result = SSL_get_verify_result(ssl); if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) { @@ -995,7 +951,7 @@ set_url(const URLSpec &url) { //////////////////////////////////////////////////////////////////// void HTTPDocument:: issue_request(const string &header, const string &body) { - if (_bio != (BIO *)NULL) { + if (!_bio.is_null()) { string request = header; request += "\r\n"; request += body; @@ -1004,21 +960,21 @@ issue_request(const string &header, const string &body) { show_send(request); } #endif - BIO_puts(_bio, request.c_str()); + BIO_puts(*_bio, request.c_str()); read_http_response(); - if (_source->eof() || _source->fail()) { + if ((*_source)->eof() || (*_source)->fail()) { if (downloader_cat.is_debug()) { downloader_cat.debug() << "Whoops, socket closed.\n"; free_bio(); - if (prepare_for_next()) { + if (prepare_for_next(true)) { #ifndef NDEBUG if (downloader_cat.is_spam()) { show_send(request); } #endif - BIO_puts(_bio, request.c_str()); + BIO_puts(*_bio, request.c_str()); read_http_response(); } } @@ -1035,14 +991,13 @@ issue_request(const string &header, const string &body) { //////////////////////////////////////////////////////////////////// void HTTPDocument:: read_http_response() { - nassertv(_source != (IBioStream *)NULL); _headers.clear(); _realm = string(); // The first line back should include the HTTP version and the // result code. string line; - getline(*_source, line); + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } @@ -1050,7 +1005,7 @@ read_http_response() { downloader_cat.spam() << "recv: " << line << "\n"; } - if (!(*_source) || line.length() < 5 || line.substr(0, 5) != "HTTP/") { + if (!(**_source) || line.length() < 5 || line.substr(0, 5) != "HTTP/") { // Not an HTTP response. _status_code = 0; _status_string = "Not an HTTP response"; @@ -1085,7 +1040,7 @@ read_http_response() { string field_name; string field_value; - getline(*_source, line); + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } @@ -1093,7 +1048,7 @@ read_http_response() { downloader_cat.spam() << "recv: " << line << "\n"; } - while (!_source->eof() && !_source->fail() && !line.empty()) { + while (!(*_source)->eof() && !(*_source)->fail() && !line.empty()) { if (isspace(line[0])) { // If the line begins with a space, that continues the previous // field. @@ -1122,7 +1077,7 @@ read_http_response() { } } - getline(*_source, line); + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } @@ -1411,59 +1366,6 @@ parse_authentication_schemes(HTTPDocument::AuthenticationSchemes &schemes, } } -//////////////////////////////////////////////////////////////////// -// Function: HTTPDocument::read_body -// Access: Private -// Description: Returns a newly-allocated istream suitable for -// reading the body of the document. If owns_source is -// true, the ownership of the _source pointer will be -// passed to the istream; otherwise, it will be -// retained. (owns_source must be true in order to read -// "identity" encoded documents.) -//////////////////////////////////////////////////////////////////// -istream *HTTPDocument:: -read_body(bool owns_source) { - if (_state != S_read_header || _source == (IBioStream *)NULL) { - return NULL; - } - - string transfer_coding = downcase(get_header_value("Transfer-Encoding")); - string content_length = get_header_value("Content-Length"); - - istream *result; - if (transfer_coding == "chunked") { - // Chunked can be used directly. - _file_size = 0; - _state = S_started_body; - _read_index++; - result = new IChunkedStream(_source, owns_source, (HTTPDocument *)this); - if (owns_source) { - _source = (IBioStream *)NULL; - } - - } else if (!content_length.empty()) { - // If we have a content length, we can use an IdentityStream. - _state = S_started_body; - _read_index++; - result = new IIdentityStream(_source, owns_source, (HTTPDocument *)this, _file_size); - if (owns_source) { - _source = (IBioStream *)NULL; - } - - } else if (owns_source) { - // If we own the source, we can return it. - _state = S_started_body; - result = _source; - _source = (IBioStream *)NULL; - - } else { - // Otherwise, we don't own the source; too bad. - result = NULL; - } - - return result; -} - //////////////////////////////////////////////////////////////////// // Function: HTTPDocument::prepare_for_next // Access: Private @@ -1473,15 +1375,21 @@ read_body(bool owns_source) { // skipping past the unread body in the persistent // connection, or it might do nothing at all if the body // has already been completely read. +// +// If allow_reconnect is true, then the current +// connection may be automatically dropped and a new +// connection reestablished if necessary; otherwise, +// this function will fail (and return false) if +// multiple connections are required. //////////////////////////////////////////////////////////////////// bool HTTPDocument:: -prepare_for_next() { +prepare_for_next(bool allow_reconnect) { if (get_persistent_connection() && !will_close_connection() && _proxy == _client->get_proxy()) { // See if we can reuse the current connection. if (_state == S_read_header) { // We have read the header; now skip past the body. - istream *body = read_body(false); + istream *body = read_body(); if (body != (istream *)NULL) { string line; getline(*body, line); @@ -1491,27 +1399,22 @@ prepare_for_next() { } getline(*body, line); } - nassertr(body != _source, false); delete body; } } - if (_source == (IBioStream *)NULL) { - _source = new IBioStream(_bio, false); - } - if (_state == S_read_body) { // We have read the body, but there's a trailer to read. string line; - getline(*_source, line); + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } if (downloader_cat.is_spam()) { downloader_cat.spam() << "skip: " << line << "\n"; } - while (!_source->eof() && !_source->fail() && !line.empty()) { - getline(*_source, line); + while (!(*_source)->eof() && !(*_source)->fail() && !line.empty()) { + getline(**_source, line); if (!line.empty() && line[line.length() - 1] == '\r') { line = line.substr(0, line.length() - 1); } @@ -1528,12 +1431,9 @@ prepare_for_next() { } } - if (_bio != (BIO *)NULL && _state == S_new) { + if (!_bio.is_null() && _state == S_new) { // If we have a BIO and the _state is S_new, then we haven't done // anything with the BIO yet, so we can still use it. - if (_source == (IBioStream *)NULL) { - _source = new IBioStream(_bio, false); - } return true; } @@ -1541,9 +1441,9 @@ prepare_for_next() { // body, or we were only partly through reading the body elsewhere; // or possibly we don't have a connection yet at all. In any case, // we must now get a new connection. - if (_bio != (BIO *)NULL && !_owns_bio) { - // We have a connection, but we don't own it, so we can't close - // it. Too bad. + if (!_bio.is_null() && !allow_reconnect) { + // We have a connection, and we're not allowed to throw it away. + // Too bad. return false; } @@ -1553,9 +1453,7 @@ prepare_for_next() { _proxy = _client->get_proxy(); _http_version = _client->get_http_version(); _http_version_string = _client->get_http_version_string(); - _owns_bio = true; if (establish_connection()) { - _source = new IBioStream(_bio, false); return true; } @@ -1572,24 +1470,8 @@ prepare_for_next() { //////////////////////////////////////////////////////////////////// void HTTPDocument:: free_bio() { - if (_source != (IBioStream *)NULL) { - delete _source; - _source = (IBioStream *)NULL; - } - if (_bio != (BIO *)NULL) { - if (_owns_bio) { - // TODO: We should be more careful here to manage reference - // counts so we don't free the bio out from under a BIOStreamBuf - // that's trying to read from it. - if (downloader_cat.is_debug()) { - const URLSpec &url = _proxy.empty() ? _url : _proxy; - downloader_cat.debug() - << "Dropping connection to " << url.get_server() << "\n"; - } - BIO_free_all(_bio); - } - _bio = (BIO *)NULL; - } + _source.clear(); + _bio.clear(); _read_index++; _state = S_new; } diff --git a/panda/src/downloader/httpDocument.h b/panda/src/downloader/httpDocument.h index 1de4ea6370..f18e2a8ceb 100644 --- a/panda/src/downloader/httpDocument.h +++ b/panda/src/downloader/httpDocument.h @@ -31,10 +31,12 @@ #include "httpClient.h" #include "urlSpec.h" #include "virtualFile.h" +#include "bioPtr.h" +#include "bioStreamPtr.h" #include "pmap.h" +#include "pointerTo.h" #include -class IBioStream; class HTTPClient; //////////////////////////////////////////////////////////////////// @@ -43,11 +45,12 @@ class HTTPClient; //////////////////////////////////////////////////////////////////// class EXPCL_PANDAEXPRESS HTTPDocument : public VirtualFile { private: - HTTPDocument(HTTPClient *client, BIO *bio = NULL); + HTTPDocument(HTTPClient *client); bool send_request(const string &method, const URLSpec &url, const string &body); - bool send_request(const string &header, const string &body); + bool send_request(const string &header, const string &body, + bool allow_reconnect); public: virtual ~HTTPDocument(); @@ -78,9 +81,12 @@ PUBLISHED: void write_headers(ostream &out) const; - INLINE bool get_document(const URLSpec &url, const string &body = string()); + INLINE bool post_form(const URLSpec &url, const string &body); + INLINE bool get_document(const URLSpec &url); INLINE bool get_header(const URLSpec &url); + ISocketStream *read_body(); + private: bool establish_connection(); bool establish_http(); @@ -112,15 +118,13 @@ private: static void show_send(const string &message); #endif - istream *read_body(bool owns_source); - bool prepare_for_next(); + bool prepare_for_next(bool allow_reconnect); void free_bio(); HTTPClient *_client; URLSpec _proxy; - BIO *_bio; - bool _owns_bio; - IBioStream *_source; + PT(BioPtr) _bio; + PT(BioStreamPtr) _source; bool _persistent_connection; URLSpec _url; diff --git a/panda/src/downloader/identityStream.I b/panda/src/downloader/identityStream.I index b8ab30af1d..dc304a7f2d 100644 --- a/panda/src/downloader/identityStream.I +++ b/panda/src/downloader/identityStream.I @@ -23,7 +23,7 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE IIdentityStream:: -IIdentityStream() : istream(&_buf) { +IIdentityStream() : ISocketStream(&_buf) { } //////////////////////////////////////////////////////////////////// @@ -32,9 +32,11 @@ IIdentityStream() : istream(&_buf) { // Description: //////////////////////////////////////////////////////////////////// INLINE IIdentityStream:: -IIdentityStream(istream *source, bool owns_source, - HTTPDocument *doc, size_t content_length) : istream(&_buf) { - open(source, owns_source, doc, content_length); +IIdentityStream(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length) : + ISocketStream(&_buf) +{ + open(source, doc, has_content_length, content_length); } //////////////////////////////////////////////////////////////////// @@ -43,10 +45,10 @@ IIdentityStream(istream *source, bool owns_source, // Description: //////////////////////////////////////////////////////////////////// INLINE IIdentityStream &IIdentityStream:: -open(istream *source, bool owns_source, HTTPDocument *doc, - size_t content_length) { +open(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length) { clear(0); - _buf.open_read(source, owns_source, doc, content_length); + _buf.open_read(source, doc, has_content_length, content_length); return *this; } diff --git a/panda/src/downloader/identityStream.cxx b/panda/src/downloader/identityStream.cxx index 1598272e58..dac5db3917 100644 --- a/panda/src/downloader/identityStream.cxx +++ b/panda/src/downloader/identityStream.cxx @@ -17,3 +17,25 @@ //////////////////////////////////////////////////////////////////// #include "identityStream.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Function: IIdentityStream::is_closed +// Access: Public, Virtual +// Description: Returns true if the last eof condition was triggered +// because the socket has genuinely closed, or false if +// we can expect more data to come along shortly. +//////////////////////////////////////////////////////////////////// +INLINE bool IIdentityStream:: +is_closed() { + if ((_buf._has_content_length && _buf._bytes_remaining == 0) || + (*_buf._source)->is_closed()) { + return true; + } + clear(); + return false; +} + +#endif // HAVE_SSL diff --git a/panda/src/downloader/identityStream.h b/panda/src/downloader/identityStream.h index 86b8818a79..b0af379e65 100644 --- a/panda/src/downloader/identityStream.h +++ b/panda/src/downloader/identityStream.h @@ -21,9 +21,14 @@ #include "pandabase.h" +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + +#include "socketStream.h" #include "identityStreamBuf.h" class HTTPDocument; +class BioStreamPtr; //////////////////////////////////////////////////////////////////// // Class : IIdentityStream @@ -39,22 +44,26 @@ class HTTPDocument; // completely read. //////////////////////////////////////////////////////////////////// // No need to export from DLL. -class IIdentityStream : public istream { +class IIdentityStream : public ISocketStream { public: INLINE IIdentityStream(); - INLINE IIdentityStream(istream *source, bool owns_source, - HTTPDocument *doc, size_t content_length); + INLINE IIdentityStream(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length); - INLINE IIdentityStream &open(istream *source, bool owns_source, - HTTPDocument *doc, size_t content_length); + INLINE IIdentityStream &open(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length); INLINE IIdentityStream &close(); + virtual bool is_closed(); + private: IdentityStreamBuf _buf; }; #include "identityStream.I" +#endif // HAVE_SSL + #endif diff --git a/panda/src/downloader/identityStreamBuf.cxx b/panda/src/downloader/identityStreamBuf.cxx index d88ab720dc..47c211d5b4 100644 --- a/panda/src/downloader/identityStreamBuf.cxx +++ b/panda/src/downloader/identityStreamBuf.cxx @@ -18,6 +18,9 @@ #include "identityStreamBuf.h" +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + #ifndef HAVE_STREAMSIZE // Some compilers (notably SGI) don't define this for us typedef int streamsize; @@ -30,8 +33,7 @@ typedef int streamsize; //////////////////////////////////////////////////////////////////// IdentityStreamBuf:: IdentityStreamBuf() { - _source = (istream *)NULL; - _owns_source = false; + _has_content_length = true; _bytes_remaining = 0; #ifdef WIN32_VC @@ -68,11 +70,11 @@ IdentityStreamBuf:: // from the identity encoding. //////////////////////////////////////////////////////////////////// void IdentityStreamBuf:: -open_read(istream *source, bool owns_source, HTTPDocument *doc, - size_t content_length) { +open_read(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length) { _source = source; - _owns_source = owns_source; _doc = doc; + _has_content_length = has_content_length; _bytes_remaining = content_length; if (_doc != (HTTPDocument *)NULL) { @@ -87,13 +89,7 @@ open_read(istream *source, bool owns_source, HTTPDocument *doc, //////////////////////////////////////////////////////////////////// void IdentityStreamBuf:: close_read() { - if (_source != (istream *)NULL) { - if (_owns_source) { - delete _source; - _owns_source = false; - } - _source = (istream *)NULL; - } + _source.clear(); } //////////////////////////////////////////////////////////////////// @@ -137,24 +133,44 @@ underflow() { //////////////////////////////////////////////////////////////////// size_t IdentityStreamBuf:: read_chars(char *start, size_t length) { - if (_bytes_remaining == 0) { - return 0; - } + if (!_has_content_length) { + // If we have no restrictions on content length, read till end of + // file. + (*_source)->read(start, length); + length = (*_source)->gcount(); - // Extract some of the bytes remaining in the chunk. - length = min(length, _bytes_remaining); - _source->read(start, length); - length = _source->gcount(); - _bytes_remaining -= length; + if (length == 0) { + // End of file; we're done. + if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) { + // An IdentityStreamBuf doesn't have a trailer, so we've already + // "read" it. + _doc->_state = HTTPDocument::S_read_trailer; + } + } - if (_bytes_remaining == 0) { - // We're done. - if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) { - // An IdentityStreamBuf doesn't have a trailer, so we've already - // "read" it. - _doc->_state = HTTPDocument::S_read_trailer; + } else { + if (_bytes_remaining == 0) { + return 0; + } + + // Extract some of the bytes remaining in the chunk. + + length = min(length, _bytes_remaining); + (*_source)->read(start, length); + length = (*_source)->gcount(); + _bytes_remaining -= length; + + if (_bytes_remaining == 0) { + // We're done. + if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) { + // An IdentityStreamBuf doesn't have a trailer, so we've already + // "read" it. + _doc->_state = HTTPDocument::S_read_trailer; + } } } return length; } + +#endif // HAVE_SSL diff --git a/panda/src/downloader/identityStreamBuf.h b/panda/src/downloader/identityStreamBuf.h index 5ff3c35e9c..cae835e73e 100644 --- a/panda/src/downloader/identityStreamBuf.h +++ b/panda/src/downloader/identityStreamBuf.h @@ -20,7 +20,12 @@ #define IDENTITYSTREAMBUF_H #include "pandabase.h" + +// This module is not compiled if OpenSSL is not available. +#ifdef HAVE_SSL + #include "httpDocument.h" +#include "bioStreamPtr.h" #include "pointerTo.h" //////////////////////////////////////////////////////////////////// @@ -33,8 +38,8 @@ public: IdentityStreamBuf(); virtual ~IdentityStreamBuf(); - void open_read(istream *source, bool owns_source, HTTPDocument *doc, - size_t content_length); + void open_read(BioStreamPtr *source, HTTPDocument *doc, + bool has_content_length, size_t content_length); void close_read(); protected: @@ -43,12 +48,16 @@ protected: private: size_t read_chars(char *start, size_t length); - istream *_source; - bool _owns_source; + PT(BioStreamPtr) _source; + bool _has_content_length; size_t _bytes_remaining; PT(HTTPDocument) _doc; int _read_index; + + friend class IIdentityStream; }; +#endif // HAVE_SSL + #endif diff --git a/panda/src/downloader/socketStream.I b/panda/src/downloader/socketStream.I new file mode 100644 index 0000000000..7be19e2e35 --- /dev/null +++ b/panda/src/downloader/socketStream.I @@ -0,0 +1,27 @@ +// Filename: socketStream.I +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ISocketStream::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ISocketStream:: +ISocketStream(streambuf *buf) : istream(buf) { +} diff --git a/panda/src/downloader/socketStream.h b/panda/src/downloader/socketStream.h new file mode 100644 index 0000000000..c396982aa2 --- /dev/null +++ b/panda/src/downloader/socketStream.h @@ -0,0 +1,54 @@ +// Filename: socketStream.h +// Created by: drose (15Oct02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + +#ifndef SOCKETSTREAM_H +#define SOCKETSTREAM_H + +#include "pandabase.h" + +// At the present, this module is not compiled if OpenSSL is not +// available, since the only current use for it is to implement +// OpenSSL-defined constructs (like ISocketStream). + +#ifdef HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Class : ISocketStream +// Description : This is a base class for istreams implemented in +// Panda that read from a (possibly non-blocking) +// socket. It adds is_closed(), which can be called +// after an eof condition to check whether the socket +// has been closed, or whether more data may be +// available later. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ISocketStream : public istream { +public: + INLINE ISocketStream(streambuf *buf); + +PUBLISHED: + virtual bool is_closed() = 0; +}; + +#include "socketStream.I" + +#endif // HAVE_SSL + + +#endif + +