فهرست منبع

add do_connect()

David Rose 23 سال پیش
والد
کامیت
3ad212f066

+ 1 - 0
panda/src/downloader/Sources.pp

@@ -41,6 +41,7 @@
     httpChannel.cxx \
     identityStream.cxx identityStreamBuf.cxx \
     multiplexStream.cxx multiplexStreamBuf.cxx \
+    socketStream.cxx \
     urlSpec.cxx \
     $[if $[HAVE_NET], downloadDb.cxx downloader.cxx] \
     $[if $[HAVE_ZLIB], decompressor.cxx download_utils.cxx]

+ 0 - 27
panda/src/downloader/bioPtr.cxx

@@ -54,31 +54,4 @@ BioPtr::
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     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

+ 0 - 1
panda/src/downloader/bioPtr.h

@@ -51,7 +51,6 @@ public:
   INLINE void set_bio(BIO *bio);
   INLINE BIO *get_bio() const;
 
-  bool connect() const;
   INLINE const string &get_server_name() const;
   INLINE int get_port() const;
   

+ 88 - 2
panda/src/downloader/bioStream.I

@@ -44,7 +44,7 @@ IBioStream(BioPtr *source) : ISocketStream(&_buf) {
 INLINE IBioStream &IBioStream::
 open(BioPtr *source) {
   clear(0);
-  _buf.open_read(source);
+  _buf.open(source);
   return *this;
 }
 
@@ -56,6 +56,92 @@ open(BioPtr *source) {
 ////////////////////////////////////////////////////////////////////
 INLINE IBioStream &IBioStream::
 close() {
-  _buf.close_read();
+  _buf.close();
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OBioStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE OBioStream::
+OBioStream() : OSocketStream(&_buf) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OBioStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE OBioStream::
+OBioStream(BioPtr *source) : OSocketStream(&_buf) {
+  open(source);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OBioStream::open
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE OBioStream &OBioStream::
+open(BioPtr *source) {
+  clear(0);
+  _buf.open(source);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OBioStream::close
+//       Access: Public
+//  Description: Resets the BioStream to empty, but does not actually
+//               close the source BIO unless owns_source was true.
+////////////////////////////////////////////////////////////////////
+INLINE OBioStream &OBioStream::
+close() {
+  _buf.close();
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE BioStream::
+BioStream() : SocketStream(&_buf) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE BioStream::
+BioStream(BioPtr *source) : SocketStream(&_buf) {
+  open(source);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStream::open
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE BioStream &BioStream::
+open(BioPtr *source) {
+  clear(0);
+  _buf.open(source);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStream::close
+//       Access: Public
+//  Description: Resets the BioStream to empty, but does not actually
+//               close the source BIO unless owns_source was true.
+////////////////////////////////////////////////////////////////////
+INLINE BioStream &BioStream::
+close() {
+  _buf.close();
   return *this;
 }

+ 34 - 0
panda/src/downloader/bioStream.cxx

@@ -33,3 +33,37 @@ is_closed() {
   clear();
   return false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: OBioStream::is_closed
+//       Access: Public, Virtual
+//  Description: Returns true if the last write fail condition was
+//               triggered because the socket has genuinely closed, or
+//               false if we can expect to send more data along
+//               shortly.
+////////////////////////////////////////////////////////////////////
+INLINE bool OBioStream::
+is_closed() {
+  if (_buf._is_closed) {
+    return true;
+  }
+  clear();
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStream::is_closed
+//       Access: Public, Virtual
+//  Description: Returns true if the last eof or failure condition was
+//               triggered because the socket has genuinely closed, or
+//               false if we can expect to read or send more data
+//               shortly.
+////////////////////////////////////////////////////////////////////
+INLINE bool BioStream::
+is_closed() {
+  if (_buf._is_closed) {
+    return true;
+  }
+  clear();
+  return false;
+}

+ 42 - 0
panda/src/downloader/bioStream.h

@@ -50,6 +50,48 @@ private:
   BioStreamBuf _buf;
 };
 
+////////////////////////////////////////////////////////////////////
+//       Class : OBioStream
+// Description : An output stream object that writes data to an
+//               OpenSSL BIO object.  This is used by the HTTPClient
+//               and HTTPChannel classes to provide a C++ interface
+//               to OpenSSL.
+//
+//               Seeking is not supported.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS OBioStream : public OSocketStream {
+public:
+  INLINE OBioStream();
+  INLINE OBioStream(BioPtr *source);
+
+  INLINE OBioStream &open(BioPtr *source);
+  INLINE OBioStream &close();
+
+  virtual bool is_closed();
+
+private:
+  BioStreamBuf _buf;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : BioStream
+// Description : A bi-directional stream object that reads and writes
+//               data to an OpenSSL BIO object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS BioStream : public SocketStream {
+public:
+  INLINE BioStream();
+  INLINE BioStream(BioPtr *source);
+
+  INLINE BioStream &open(BioPtr *source);
+  INLINE BioStream &close();
+
+  virtual bool is_closed();
+
+private:
+  BioStreamBuf _buf;
+};
+
 #include "bioStream.I"
 
 #endif  // HAVE_SSL

+ 113 - 11
panda/src/downloader/bioStreamBuf.cxx

@@ -38,15 +38,21 @@ BioStreamBuf() {
   // In spite of the claims of the MSDN Library to the contrary,
   // Windows doesn't seem to provide an allocate() function, so we'll
   // do it by hand.
-  char *buf = new char[4096];
-  char *ebuf = buf + 4096;
-  setg(buf, ebuf, ebuf);
-  setp(buf, ebuf);
+  char *buf = new char[8192];
+  char *ebuf = buf + 8192;
+  char *mbuf = buf + 4096;
+  setg(buf, mbuf, mbuf);
+  setp(mbuf, ebuf);
 
 #else
   allocate();
-  setg(base(), ebuf(), ebuf());
-  setp(base(), ebuf());
+  // Chop the buffer in half.  The bottom half goes to the get buffer;
+  // the top half goes to the put buffer.
+  char *b = base();
+  char *t = ebuf();
+  char *m = b + (t - b) / 2;
+  setg(b, m, m);
+  setp(b, m);
 #endif
 }
 
@@ -57,29 +63,79 @@ BioStreamBuf() {
 ////////////////////////////////////////////////////////////////////
 BioStreamBuf::
 ~BioStreamBuf() {
-  close_read();
+  close();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BioStreamBuf::open_read
+//     Function: BioStreamBuf::open
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BioStreamBuf::
-open_read(BioPtr *source) {
+open(BioPtr *source) {
   _source = source;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BioStreamBuf::close_read
+//     Function: BioStreamBuf::close
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BioStreamBuf::
-close_read() {
+close() {
+  sync();
   _source.clear();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BioStreamBuf::overflow
+//       Access: Protected, Virtual
+//  Description: Called by the system ostream implementation when its
+//               internal buffer is filled, plus one character.
+////////////////////////////////////////////////////////////////////
+int BioStreamBuf::
+overflow(int ch) {
+  size_t n = pptr() - pbase();
+  if (n != 0) {
+    size_t num_wrote = write_chars(pbase(), n);
+    pbump(-(int)n);
+    if (num_wrote != n) {
+      return EOF;
+    }
+  }
+
+  if (ch != EOF) {
+    // Store the next character back in the buffer.
+    *pptr() = ch;
+    pbump(1);
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BioStreamBuf::sync
+//       Access: Protected, Virtual
+//  Description: Called by the system iostream implementation to
+//               implement a flush operation.
+////////////////////////////////////////////////////////////////////
+int BioStreamBuf::
+sync() {
+  /*
+  size_t n = egptr() - gptr();
+  gbump(n);
+  */
+
+  size_t n = pptr() - pbase();
+  size_t num_wrote = write_chars(pbase(), n);
+  pbump(-(int)n);
+  if (num_wrote != n) {
+    return EOF;
+  }
+
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BioStreamBuf::underflow
 //       Access: Protected, Virtual
@@ -118,5 +174,51 @@ underflow() {
   return (unsigned char)*gptr();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BioStreamBuf::write_chars
+//       Access: Private
+//  Description: Sends some characters to the dest stream.  Does not
+//               return until all characters are sent or the socket is
+//               closed, even if the underlying BIO is non-blocking.
+////////////////////////////////////////////////////////////////////
+size_t BioStreamBuf::
+write_chars(const char *start, size_t length) {
+  size_t wrote_so_far = 0;
+
+  int write_count = BIO_write(*_source, start, length);
+  while (write_count != (int)(length - wrote_so_far)) {
+    if (write_count <= 0) {
+      _is_closed = !BIO_should_retry(*_source);
+      if (_is_closed) {
+        return wrote_so_far;
+      }
+
+      // Block on the underlying socket before we try to write some
+      // more.
+      int fd = -1;
+      BIO_get_fd(*_source, &fd);
+      if (fd < 0) {
+        downloader_cat.warning()
+          << "socket BIO has no file descriptor.\n";
+      } else {
+        downloader_cat.spam()
+          << "waiting to write to BIO.\n";
+        fd_set wset;
+        FD_ZERO(&wset);
+        FD_SET(fd, &wset);
+        select(fd + 1, NULL, &wset, NULL, NULL);
+      }        
+
+    } else {
+      // wrote some characters.
+      wrote_so_far += write_count;
+    }
+
+    // Try to write some more.
+    write_count = BIO_write(*_source, start + wrote_so_far, length - wrote_so_far);
+  }
+
+  return length;
+}
 
 #endif  // HAVE_SSL

+ 8 - 2
panda/src/downloader/bioStreamBuf.h

@@ -38,17 +38,23 @@ public:
   BioStreamBuf();
   virtual ~BioStreamBuf();
 
-  void open_read(BioPtr *source);
-  void close_read();
+  void open(BioPtr *source);
+  void close();
 
 protected:
+  virtual int overflow(int c);
+  virtual int sync(void);
   virtual int underflow(void);
 
 private:
+  size_t write_chars(const char *start, size_t length);
+
   PT(BioPtr) _source;
   bool _is_closed;
 
   friend class IBioStream;
+  friend class OBioStream;
+  friend class BioStream;
 };
 
 #endif  // HAVE_SSL

+ 6 - 6
panda/src/downloader/bioStreamPtr.I

@@ -23,7 +23,7 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE BioStreamPtr::
-BioStreamPtr(IBioStream *stream) : _stream(stream) {
+BioStreamPtr(BioStream *stream) : _stream(stream) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -31,7 +31,7 @@ BioStreamPtr(IBioStream *stream) : _stream(stream) {
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE IBioStream &BioStreamPtr::
+INLINE BioStream &BioStreamPtr::
 operator *() const {
   return *_stream;
 }
@@ -41,7 +41,7 @@ operator *() const {
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE IBioStream *BioStreamPtr::
+INLINE BioStream *BioStreamPtr::
 operator ->() const {
   return _stream;
 }
@@ -52,7 +52,7 @@ operator ->() const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE BioStreamPtr::
-operator IBioStream * () const {
+operator BioStream * () const {
   return _stream;
 }
 
@@ -62,7 +62,7 @@ operator IBioStream * () const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void BioStreamPtr::
-set_stream(IBioStream *stream) {
+set_stream(BioStream *stream) {
   _stream = stream;
 }
 
@@ -71,7 +71,7 @@ set_stream(IBioStream *stream) {
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE IBioStream *BioStreamPtr::
+INLINE BioStream *BioStreamPtr::
 get_stream() const {
   return _stream;
 }

+ 2 - 2
panda/src/downloader/bioStreamPtr.cxx

@@ -27,9 +27,9 @@
 ////////////////////////////////////////////////////////////////////
 BioStreamPtr::
 ~BioStreamPtr() {
-  if (_stream != (IBioStream *)NULL) {
+  if (_stream != (BioStream *)NULL) {
     delete _stream;
-    _stream = (IBioStream *)NULL;
+    _stream = (BioStream *)NULL;
   }
 }
 

+ 8 - 8
panda/src/downloader/bioStreamPtr.h

@@ -30,25 +30,25 @@
 
 ////////////////////////////////////////////////////////////////////
 //       Class : BioStreamPtr
-// Description : A wrapper around an IBioStream object to make a
+// Description : A wrapper around an BioStream object to make a
 //               reference-counting pointer to it.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS BioStreamPtr : public ReferenceCount {
 public:
-  INLINE BioStreamPtr(IBioStream *stream);
+  INLINE BioStreamPtr(BioStream *stream);
   virtual ~BioStreamPtr();
 
-  INLINE IBioStream &operator *() const;
-  INLINE IBioStream *operator -> () const;
-  INLINE operator IBioStream * () const;
+  INLINE BioStream &operator *() const;
+  INLINE BioStream *operator -> () const;
+  INLINE operator BioStream * () const;
 
-  INLINE void set_stream(IBioStream *stream);
-  INLINE IBioStream *get_stream() const;
+  INLINE void set_stream(BioStream *stream);
+  INLINE BioStream *get_stream() const;
 
   bool connect() const;
   
 private:
-  IBioStream *_stream;
+  BioStream *_stream;
 };
 
 #include "bioStreamPtr.I"

+ 1 - 0
panda/src/downloader/downloader_composite1.cxx

@@ -13,5 +13,6 @@
 #include "identityStreamBuf.cxx"
 #include "multiplexStream.cxx"
 #include "multiplexStreamBuf.cxx"
+#include "socketStream.cxx"
 #include "urlSpec.cxx"
 

+ 62 - 8
panda/src/downloader/httpChannel.I

@@ -30,6 +30,18 @@ is_valid() const {
           (_status_code / 100) == 2);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPChannel::is_connection_ready
+//       Access: Published
+//  Description: Returns true if a connection has been established to
+//               the named server in a previous call to connect_to()
+//               or begin_connect_to(), false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool HTTPChannel::
+is_connection_ready() const {
+  return (!_source.is_null() && _state == S_ready);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HTTPChannel::get_url
 //       Access: Published
@@ -312,7 +324,7 @@ reset() {
 ////////////////////////////////////////////////////////////////////
 INLINE bool HTTPChannel::
 post_form(const URLSpec &url, const string &body) {
-  begin_request("POST", url, body, false, 0, 0);
+  begin_request(M_post, url, body, false, 0, 0);
   run();
   return is_valid();
 }
@@ -325,7 +337,7 @@ post_form(const URLSpec &url, const string &body) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool HTTPChannel::
 get_document(const URLSpec &url) {
-  begin_request("GET", url, string(), false, 0, 0);
+  begin_request(M_get, url, string(), false, 0, 0);
   run();
   return is_valid();
 }
@@ -342,7 +354,7 @@ get_document(const URLSpec &url) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool HTTPChannel::
 get_subdocument(const URLSpec &url, size_t first_byte, size_t last_byte) {
-  begin_request("GET", url, string(), false, first_byte, last_byte);
+  begin_request(M_get, url, string(), false, first_byte, last_byte);
   run();
   return is_valid();
 }
@@ -358,11 +370,30 @@ get_subdocument(const URLSpec &url, size_t first_byte, size_t last_byte) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool HTTPChannel::
 get_header(const URLSpec &url) {
-  begin_request("HEAD", url, string(), false, 0, 0);
+  begin_request(M_head, url, string(), false, 0, 0);
   run();
   return is_valid();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPChannel::connect_to
+//       Access: Published
+//  Description: Establish a direct connection to the server and port
+//               indicated by the URL, but do not issue any HTTP
+//               requests.  If successful, the connection may then be
+//               taken to use for whatever purposes you like by
+//               calling get_connection().
+//
+//               This establishes a blocking I/O socket.  Also see
+//               begin_connect_to().
+////////////////////////////////////////////////////////////////////
+INLINE bool HTTPChannel::
+connect_to(const URLSpec &url) {
+  begin_request(M_connect, url, string(), false, 0, 0);
+  run();
+  return is_connection_ready();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HTTPChannel::begin_post_form
 //       Access: Published
@@ -379,7 +410,7 @@ get_header(const URLSpec &url) {
 ////////////////////////////////////////////////////////////////////
 INLINE void HTTPChannel::
 begin_post_form(const URLSpec &url, const string &body) {
-  begin_request("POST", url, body, true, 0, 0);
+  begin_request(M_post, url, body, true, 0, 0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -398,7 +429,7 @@ begin_post_form(const URLSpec &url, const string &body) {
 ////////////////////////////////////////////////////////////////////
 INLINE void HTTPChannel::
 begin_get_document(const URLSpec &url) {
-  begin_request("GET", url, string(), true, 0, 0);
+  begin_request(M_get, url, string(), true, 0, 0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -415,7 +446,7 @@ begin_get_document(const URLSpec &url) {
 INLINE void HTTPChannel::
 begin_get_subdocument(const URLSpec &url, size_t first_byte, 
                       size_t last_byte) {
-  begin_request("GET", url, string(), true, first_byte, last_byte);
+  begin_request(M_get, url, string(), true, first_byte, last_byte);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -426,7 +457,30 @@ begin_get_subdocument(const URLSpec &url, size_t first_byte,
 ////////////////////////////////////////////////////////////////////
 INLINE void HTTPChannel::
 begin_get_header(const URLSpec &url) {
-  begin_request("HEAD", url, string(), true, 0, 0);
+  begin_request(M_head, url, string(), true, 0, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPChannel::begin_connect_to
+//       Access: Published
+//  Description: Begins a non-blocking request to establish a direct
+//               connection to the server and port indicated by the
+//               URL.  No HTTP requests will be issued beyond what is
+//               necessary to establish the connection.  When run()
+//               has finished, you may call is_connection_ready() to
+//               determine if the connection was successfully
+//               established.
+//
+//               If successful, the connection may then be taken to
+//               use for whatever purposes you like by calling
+//               get_connection().
+//
+//               This establishes a nonblocking I/O socket.  Also see
+//               connect_to().
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPChannel::
+begin_connect_to(const URLSpec &url) {
+  begin_request(M_connect, url, string(), true, 0, 0);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 94 - 31
panda/src/downloader/httpChannel.cxx

@@ -56,6 +56,8 @@ HTTPChannel(HTTPClient *client) :
   _max_updates_per_second = 1.0f / _seconds_per_update;
   _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update);
   _nonblocking = false;
+  _want_ssl = false;
+  _proxy_serves_document = false;
   _first_byte = 0;
   _last_byte = 0;
   _read_index = 0;
@@ -258,7 +260,7 @@ run() {
       } else {
         _bio = new BioPtr(_proxy);
       }
-      _source = new BioStreamPtr(new IBioStream(_bio));
+      _source = new BioStreamPtr(new BioStream(_bio));
       if (_nonblocking) {
         BIO_set_nbio(*_bio, 1);
       }
@@ -509,6 +511,33 @@ download_to_ram(Ramfile *ramfile) {
   return is_download_complete();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPChannel::get_connection
+//       Access: Published
+//  Description: Returns the connection that was established via a
+//               previous call to connect_to() or begin_connect_to(),
+//               or NULL if the connection attempt failed or if those
+//               methods have not recently been called.
+//
+//               This stream has been allocated from the free store.
+//               It is the user's responsibility to delete this
+//               pointer when finished with it.
+////////////////////////////////////////////////////////////////////
+SocketStream *HTTPChannel::
+get_connection() {
+  if (!is_connection_ready()) {
+    return NULL;
+  }
+
+  BioStream *stream = _source->get_stream();
+  _source->set_stream(NULL);
+
+  // We're now passing ownership of the connection to the user.
+  free_bio();
+
+  return stream;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HTTPChannel::reached_done_state
 //       Access: Private
@@ -552,6 +581,8 @@ reached_done_state() {
 ////////////////////////////////////////////////////////////////////
 bool HTTPChannel::
 run_connecting() {
+  _status_code = 0;
+  _status_string = string();
   if (BIO_do_connect(*_bio) <= 0) {
     if (BIO_should_retry(*_bio)) {
       return true;
@@ -577,11 +608,7 @@ run_connecting() {
     _state = S_proxy_ready;
 
   } else {
-    if (_url.get_scheme() == "https") {
-      _state = S_setup_ssl;
-    } else {
-      _state = S_ready;
-    }
+    _state = _want_ssl ? S_setup_ssl : S_ready;
   }
   return false;
 }
@@ -702,11 +729,7 @@ run_proxy_reading_header() {
   _proxy_tunnel = true;
   make_request_text(string());
 
-  if (_url.get_scheme() == "https") {
-    _state = S_setup_ssl;
-  } else {
-    _state = S_ready;
-  }
+  _state = _want_ssl ? S_setup_ssl : S_ready;
   return false;
 }
 
@@ -978,7 +1001,7 @@ run_reading_header() {
 
   if ((get_status_code() / 100) == 3 && get_status_code() != 305) {
     // Redirect.  Should we handle it automatically?
-    if (!get_redirect().empty() && (_method == "GET" || _method == "HEAD")) {
+    if (!get_redirect().empty() && (_method == M_get || _method == M_head)) {
       // Sure!
       URLSpec new_url = get_redirect();
       if (!_redirect_trail.insert(new_url).second) {
@@ -1049,7 +1072,7 @@ run_begin_body() {
   if (get_status_code() / 100 == 1 ||
       get_status_code() == 204 ||
       get_status_code() == 304 || 
-      _method == "HEAD") {
+      _method == M_head) {
     // These status codes, or method HEAD, indicate we have no body.
     // Therefore, we have already read the (nonexistent) body.
     _state = S_ready;
@@ -1283,8 +1306,9 @@ run_download_to_ram() {
 //               necessary.
 ////////////////////////////////////////////////////////////////////
 void HTTPChannel::
-begin_request(const string &method, const URLSpec &url, const string &body,
-              bool nonblocking, size_t first_byte, size_t last_byte) {
+begin_request(HTTPChannel::Method method, const URLSpec &url,
+              const string &body, bool nonblocking, 
+              size_t first_byte, size_t last_byte) {
   reset_for_new_request();
 
   // Changing the proxy, or the nonblocking state, is grounds for
@@ -1299,19 +1323,29 @@ begin_request(const string &method, const URLSpec &url, const string &body,
     free_bio();
   }
 
-  _method = method;
   set_url(url);
+  _method = method;
   _body = body;
+
+  // An https-style request means we'll need to establish an SSL
+  // connection.
+  _want_ssl = (_url.get_scheme() == "https");
+
+  // If we have a proxy, we'll be asking the proxy to hand us the
+  // document--except when we also have https, in which case we'll be
+  // tunnelling through the proxy to talk to the server directly.
+  _proxy_serves_document = (!_proxy.empty() && !_want_ssl);
+
   _first_byte = first_byte;
   _last_byte = last_byte;
+
   make_header();
   make_request_text(string());
 
-  if (!_proxy.empty() && _url.get_scheme() == "https") {
-    // HTTPS over proxy requires tunnelling through the proxy to the
-    // server so we can handle the SSL connection directly, rather
-    // than asking the proxy to hand us the particular document(s) in
-    // question.
+  if (!_proxy.empty() && (_want_ssl || _method == M_connect)) {
+    // Maybe we need to tunnel through the proxy to connect to the
+    // server directly.  We need this for HTTPS, or if the user
+    // requested a direct connection somewhere.
     ostringstream request;
     request 
       << "CONNECT " << _url.get_server() << ":" << _url.get_port()
@@ -1338,7 +1372,7 @@ begin_request(const string &method, const URLSpec &url, const string &body,
     _state = S_begin_body;
   }
 
-  _done_state = S_read_header;
+  _done_state = (_method == M_connect) ? S_ready : S_read_header;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1414,6 +1448,8 @@ bool HTTPChannel::
 http_send(const string &str) {
   nassertr(str.length() > _sent_so_far, true);
 
+  // Use the underlying BIO to write to the server, instead of the
+  // BIOStream, which would insist on blocking.
   size_t bytes_to_send = str.length() - _sent_so_far;
   int write_count =
     BIO_write(*_bio, str.data() + _sent_so_far, bytes_to_send);
@@ -1923,24 +1959,51 @@ x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
 ////////////////////////////////////////////////////////////////////
 void HTTPChannel::
 make_header() {
-  string path;
-  if (_proxy.empty() || _url.get_scheme() == "https") {
-    // In either of these cases, we contact the server directly for
-    // the document, so we just need the server-relative path.
-    path = _url.get_path();
+  if (_method == M_connect) {
+    // This method doesn't require an HTTP header at all; we'll just
+    // open a plain connection.  (Except when we're using a proxy; but
+    // in that case, it's the proxy_header we'll need, not the regular
+    // HTTP header.)
+    _header = string();
+    return;
+  }
 
-  } else {
-    // In this case (http-over-proxy), we ask the proxy for the
-    // document, so we need its full URL.
+  string path;
+  if (_proxy_serves_document) {
+    // If we'll be asking the proxy for the document, we need its full
+    // URL--but we omit the username, which is information just for us.
     URLSpec url_no_username = _url;
     url_no_username.set_username(string());
     path = url_no_username.get_url();
+
+  } else {
+    // If we'll be asking the server directly for the document, we
+    // just want its path relative to the server.
+    path = _url.get_path();
   }
 
   ostringstream stream;
 
+  switch (_method) {
+  case M_get:
+    stream << "GET";
+    break;
+
+  case M_head:
+    stream << "HEAD";
+    break;
+
+  case M_post:
+    stream << "POST";
+    break;
+
+  case M_connect:
+    stream << "CONNECT";
+    break;
+  }
+
   stream 
-    << _method << " " << path << " " 
+    << " " << path << " " 
     << _client->get_http_version_string() << "\r\n";
 
   if (_client->get_http_version() >= HTTPClient::HV_11) {

+ 15 - 2
panda/src/downloader/httpChannel.h

@@ -74,6 +74,7 @@ public:
 
 PUBLISHED:
   INLINE bool is_valid() const;
+  INLINE bool is_connection_ready() const;
   INLINE const URLSpec &get_url() const;
   INLINE HTTPClient::HTTPVersion get_http_version() const;
   INLINE const string &get_http_version_string() const;
@@ -106,6 +107,7 @@ PUBLISHED:
   INLINE bool get_subdocument(const URLSpec &url, 
                               size_t first_byte, size_t last_byte);
   INLINE bool get_header(const URLSpec &url);
+  INLINE bool connect_to(const URLSpec &url);
 
   INLINE void begin_post_form(const URLSpec &url, const string &body);
   INLINE void begin_get_document(const URLSpec &url);
@@ -113,16 +115,25 @@ PUBLISHED:
                                     size_t first_byte, size_t last_byte);
   INLINE void begin_get_header(const URLSpec &url);
   bool run();
+  INLINE void begin_connect_to(const URLSpec &url);
 
   ISocketStream *read_body();
   bool download_to_file(const Filename &filename, size_t first_byte = 0);
   bool download_to_ram(Ramfile *ramfile);
+  SocketStream *get_connection();
 
   INLINE size_t get_bytes_downloaded() const;
   INLINE size_t get_bytes_requested() const;
   INLINE bool is_download_complete() const;
 
 private:
+  enum Method {
+    M_get,
+    M_head,
+    M_post,
+    M_connect
+  };
+
   bool reached_done_state();
   bool run_connecting();
   bool run_proxy_ready();
@@ -142,7 +153,7 @@ private:
   bool run_download_to_file();
   bool run_download_to_ram();
 
-  void begin_request(const string &method, const URLSpec &url, 
+  void begin_request(Method method, const URLSpec &url, 
                      const string &body, bool nonblocking,
                      size_t first_byte, size_t last_byte);
   void reset_for_new_request();
@@ -193,9 +204,11 @@ private:
   bool _nonblocking;
 
   URLSpec _url;
-  string _method;
+  Method _method;
   string _header;
   string _body;
+  bool _want_ssl;
+  bool _proxy_serves_document;
   size_t _first_byte;
   size_t _last_byte;
 

+ 20 - 0
panda/src/downloader/socketStream.I

@@ -24,4 +24,24 @@
 ////////////////////////////////////////////////////////////////////
 INLINE ISocketStream::
 ISocketStream(streambuf *buf) : istream(buf) {
+  _data_expected = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OSocketStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE OSocketStream::
+OSocketStream(streambuf *buf) : ostream(buf) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SocketStream::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SocketStream::
+SocketStream(streambuf *buf) : iostream(buf) {
+  _data_expected = 0;
 }

+ 165 - 0
panda/src/downloader/socketStream.cxx

@@ -0,0 +1,165 @@
+// Filename: socketStream.cxx
+// Created by:  drose (19Oct02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "socketStream.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ISocketStream::receive_datagram
+//       Access: Public
+//  Description: Receives a datagram over the socket by expecting a
+//               little-endian 16-bit byte count as a prefix.  If the
+//               socket stream is non-blocking, may return false if
+//               the data is not available; otherwise, returns false
+//               only if the socket closes.
+////////////////////////////////////////////////////////////////////
+bool ISocketStream::
+receive_datagram(Datagram &dg) {
+  if (_data_expected == 0) {
+    // Read the first two bytes: the datagram length.
+    while (_data_so_far.length() < 2) {
+      int ch = get();
+      if (eof()) {
+        clear();
+        return false;
+      }
+      _data_so_far += (char)ch;
+    }
+
+    Datagram header(_data_so_far);
+    DatagramIterator di(header);
+    _data_expected = di.get_uint16();
+    _data_so_far = string();
+
+    if (_data_expected == 0) {
+      // Empty datagram.
+      dg.clear();
+      return true;
+    }
+  }
+
+  // Read the next n bytes until the datagram is filled.
+  while (_data_so_far.length() < _data_expected) {
+    int ch = get();
+    if (eof()) {
+      clear();
+      return false;
+    }
+    _data_so_far += (char)ch;
+  }
+
+  dg.clear();
+  dg.append_data(_data_so_far);
+
+  _data_expected = 0;
+  _data_so_far = string();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OSocketStream::send_datagram
+//       Access: Public
+//  Description: Transmits the indicated datagram over the socket by
+//               prepending it with a little-endian 16-bit byte count.
+//               Does not return until the data is sent or the
+//               connection is closed, even if the socket stream is
+//               non-blocking.
+////////////////////////////////////////////////////////////////////
+bool OSocketStream::
+send_datagram(const Datagram &dg) {
+  Datagram header;
+  header.add_uint16(dg.get_length());
+  write(header.get_data(), header.get_length());
+  write(dg.get_data(), dg.get_length());
+  flush();
+
+  return !is_closed();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SocketStream::receive_datagram
+//       Access: Public
+//  Description: Receives a datagram over the socket by expecting a
+//               little-endian 16-bit byte count as a prefix.  If the
+//               socket stream is non-blocking, may return false if
+//               the data is not available; otherwise, returns false
+//               only if the socket closes.
+////////////////////////////////////////////////////////////////////
+bool SocketStream::
+receive_datagram(Datagram &dg) {
+  if (_data_expected == 0) {
+    // Read the first two bytes: the datagram length.
+    while (_data_so_far.length() < 2) {
+      int ch = get();
+      if (eof()) {
+        clear();
+        return false;
+      }
+      _data_so_far += (char)ch;
+    }
+
+    Datagram header(_data_so_far);
+    DatagramIterator di(header);
+    _data_expected = di.get_uint16();
+    _data_so_far = string();
+
+    if (_data_expected == 0) {
+      // Empty datagram.
+      dg.clear();
+      return true;
+    }
+  }
+
+  // Read the next n bytes until the datagram is filled.
+  while (_data_so_far.length() < _data_expected) {
+    int ch = get();
+    if (eof()) {
+      clear();
+      return false;
+    }
+    _data_so_far += (char)ch;
+  }
+
+  dg.clear();
+  dg.append_data(_data_so_far);
+
+  _data_expected = 0;
+  _data_so_far = string();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SocketStream::send_datagram
+//       Access: Public
+//  Description: Transmits the indicated datagram over the socket by
+//               prepending it with a little-endian 16-bit byte count.
+//               Does not return until the data is sent or the
+//               connection is closed, even if the socket stream is
+//               non-blocking.
+////////////////////////////////////////////////////////////////////
+bool SocketStream::
+send_datagram(const Datagram &dg) {
+  Datagram header;
+  header.add_uint16(dg.get_length());
+  write(header.get_data(), header.get_length());
+  write(dg.get_data(), dg.get_length());
+  flush();
+
+  return !is_closed();
+}

+ 47 - 0
panda/src/downloader/socketStream.h

@@ -27,6 +27,8 @@
 
 #ifdef HAVE_SSL
 
+class Datagram;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : ISocketStream
 // Description : This is a base class for istreams implemented in
@@ -41,9 +43,54 @@ public:
   INLINE ISocketStream(streambuf *buf);
 
 PUBLISHED:
+  bool receive_datagram(Datagram &dg);
+
+  virtual bool is_closed() = 0;
+
+private:
+  size_t _data_expected;
+  string _data_so_far;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : OSocketStream
+// Description : A base class for ostreams that write to a (possibly
+//               non-blocking) socket.  It adds is_closed(), which can
+//               be called after any write operation fails to check
+//               whether the socket has been closed, or whether more
+//               data may be sent later.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS OSocketStream : public ostream {
+public:
+  INLINE OSocketStream(streambuf *buf);
+
+PUBLISHED:
+  bool send_datagram(const Datagram &dg);
+
   virtual bool is_closed() = 0;
 };
 
+////////////////////////////////////////////////////////////////////
+//       Class : SocketStream
+// Description : A base class for iostreams that read and write to a
+//               (possibly non-blocking) socket.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS SocketStream : public iostream {
+public:
+  INLINE SocketStream(streambuf *buf);
+
+PUBLISHED:
+  bool receive_datagram(Datagram &dg);
+  bool send_datagram(const Datagram &dg);
+
+  virtual bool is_closed() = 0;
+
+private:
+  size_t _data_expected;
+  string _data_so_far;
+};
+
+
 #include "socketStream.I"
 
 #endif  // HAVE_SSL