Browse Source

reference-count sockets, prepare for non-blocking i/o

David Rose 23 years ago
parent
commit
cd7815bb88

+ 8 - 0
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
     

+ 77 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     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;
+}

+ 84 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 69 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 <openssl/ssl.h>
+
+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
+
+

+ 5 - 5
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;
 }
 

+ 16 - 0
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;
+}

+ 6 - 3
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;
 };

+ 5 - 12
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;
       }
 

+ 7 - 3
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 <openssl/ssl.h>
 
 ////////////////////////////////////////////////////////////////////
@@ -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

+ 77 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     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;
+}

+ 36 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 61 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 <openssl/ssl.h>
+
+////////////////////////////////////////////////////////////////////
+//       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
+
+

+ 5 - 6
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;
 }
 

+ 21 - 0
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

+ 12 - 5
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
 
 

+ 10 - 14
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

+ 11 - 3
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

+ 2 - 0
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"

+ 27 - 7
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) {

+ 2 - 1
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);

+ 20 - 16
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) {

+ 99 - 217
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;
 }

+ 13 - 9
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 <openssl/ssl.h>
 
-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;

+ 9 - 7
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;
 }
 

+ 22 - 0
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

+ 14 - 5
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
 
 

+ 43 - 27
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();
+
+    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;
+      }
+    }
 
-  // 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;
+  } 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

+ 13 - 4
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

+ 27 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ISocketStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ISocketStream::
+ISocketStream(streambuf *buf) : istream(buf) {
+}

+ 54 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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
+
+