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

This commit is contained in:
David Rose 2002-10-16 01:05:04 +00:00
parent 203a2dbda7
commit cd7815bb88
30 changed files with 854 additions and 343 deletions

View File

@ -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

View File

@ -0,0 +1,77 @@
// Filename: bioPtr.I
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: BioPtr::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BioPtr::
BioPtr(BIO *bio) : _bio(bio) {
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::operator *
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BIO &BioPtr::
operator *() const {
return *_bio;
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::operator ->
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BIO *BioPtr::
operator ->() const {
return _bio;
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::operator typecast
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BioPtr::
operator BIO * () const {
return _bio;
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::get_bio
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void BioPtr::
set_bio(BIO *bio) {
_bio = bio;
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::get_bio
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BIO *BioPtr::
get_bio() const {
return _bio;
}

View File

@ -0,0 +1,84 @@
// Filename: bioPtr.cxx
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "bioPtr.h"
#ifdef HAVE_SSL
////////////////////////////////////////////////////////////////////
// Function: BioPtr::Constructor
// Access: Public
// Description: This flavor of the constructor automatically creates
// a socket BIO and feeds it the server and port name
// from the indicated URL. It doesn't call
// BIO_do_connect(), though.
////////////////////////////////////////////////////////////////////
BioPtr::
BioPtr(const URLSpec &url) {
_server_name = url.get_server();
_port = url.get_port();
_bio = BIO_new_connect((char *)_server_name.c_str());
BIO_set_conn_int_port(_bio, &_port);
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
BioPtr::
~BioPtr() {
if (_bio != (BIO *)NULL) {
if (downloader_cat.is_debug() && !_server_name.empty()) {
downloader_cat.debug()
<< "Dropping connection to " << _server_name << ":" << _port << "\n";
}
BIO_free_all(_bio);
_bio = (BIO *)NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: BioPtr::connect
// Access: Public
// Description: Calls BIO_do_connect() to establish a connection on
// the previously-named server and port. Returns true
// if successful, false otherwise.
////////////////////////////////////////////////////////////////////
bool BioPtr::
connect() const {
nassertr(_bio != (BIO *)NULL && !_server_name.empty(), false);
if (downloader_cat.is_debug()) {
downloader_cat.debug()
<< "Connecting to " << _server_name << ":" << _port << "\n";
}
if (BIO_do_connect(_bio) <= 0) {
downloader_cat.info()
<< "Could not connect to " << _server_name << ":" << _port << "\n";
#ifdef REPORT_SSL_ERRORS
ERR_print_errors_fp(stderr);
#endif
return false;
}
return true;
}
#endif // HAVE_SSL

View File

@ -0,0 +1,69 @@
// Filename: bioPtr.h
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef BIOPTR_H
#define BIOPTR_H
#include "pandabase.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_SSL
#include "referenceCount.h"
#include <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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,77 @@
// Filename: bioStreamPtr.I
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BioStreamPtr::
BioStreamPtr(IBioStream *stream) : _stream(stream) {
}
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::operator *
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IBioStream &BioStreamPtr::
operator *() const {
return *_stream;
}
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::operator ->
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IBioStream *BioStreamPtr::
operator ->() const {
return _stream;
}
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::operator typecast
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE BioStreamPtr::
operator IBioStream * () const {
return _stream;
}
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::get_stream
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void BioStreamPtr::
set_stream(IBioStream *stream) {
_stream = stream;
}
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::get_stream
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IBioStream *BioStreamPtr::
get_stream() const {
return _stream;
}

View File

@ -0,0 +1,36 @@
// Filename: bioStreamPtr.cxx
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "bioPtr.h"
#ifdef HAVE_SSL
////////////////////////////////////////////////////////////////////
// Function: BioStreamPtr::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
BioStreamPtr::
~BioStreamPtr() {
if (_stream != (IBioStream *)NULL) {
delete _stream;
_stream = (IBioStream *)NULL;
}
}
#endif // HAVE_SSL

View File

@ -0,0 +1,61 @@
// Filename: bioStreamPtr.h
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef BIOSTREAMPTR_H
#define BIOSTREAMPTR_H
#include "pandabase.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_SSL
#include "bioStream.h"
#include "referenceCount.h"
#include <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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -18,6 +18,9 @@
#include "identityStreamBuf.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_SSL
#ifndef HAVE_STREAMSIZE
// Some compilers (notably SGI) don't define this for us
typedef int streamsize;
@ -30,8 +33,7 @@ typedef int streamsize;
////////////////////////////////////////////////////////////////////
IdentityStreamBuf::
IdentityStreamBuf() {
_source = (istream *)NULL;
_owns_source = false;
_has_content_length = true;
_bytes_remaining = 0;
#ifdef WIN32_VC
@ -68,11 +70,11 @@ IdentityStreamBuf::
// from the identity encoding.
////////////////////////////////////////////////////////////////////
void IdentityStreamBuf::
open_read(istream *source, bool owns_source, HTTPDocument *doc,
size_t content_length) {
open_read(BioStreamPtr *source, HTTPDocument *doc,
bool has_content_length, size_t content_length) {
_source = source;
_owns_source = owns_source;
_doc = doc;
_has_content_length = has_content_length;
_bytes_remaining = content_length;
if (_doc != (HTTPDocument *)NULL) {
@ -87,13 +89,7 @@ open_read(istream *source, bool owns_source, HTTPDocument *doc,
////////////////////////////////////////////////////////////////////
void IdentityStreamBuf::
close_read() {
if (_source != (istream *)NULL) {
if (_owns_source) {
delete _source;
_owns_source = false;
}
_source = (istream *)NULL;
}
_source.clear();
}
////////////////////////////////////////////////////////////////////
@ -137,24 +133,44 @@ underflow() {
////////////////////////////////////////////////////////////////////
size_t IdentityStreamBuf::
read_chars(char *start, size_t length) {
if (_bytes_remaining == 0) {
return 0;
}
if (!_has_content_length) {
// If we have no restrictions on content length, read till end of
// file.
(*_source)->read(start, length);
length = (*_source)->gcount();
// Extract some of the bytes remaining in the chunk.
length = min(length, _bytes_remaining);
_source->read(start, length);
length = _source->gcount();
_bytes_remaining -= length;
if (length == 0) {
// End of file; we're done.
if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) {
// An IdentityStreamBuf doesn't have a trailer, so we've already
// "read" it.
_doc->_state = HTTPDocument::S_read_trailer;
}
}
if (_bytes_remaining == 0) {
// We're done.
if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) {
// An IdentityStreamBuf doesn't have a trailer, so we've already
// "read" it.
_doc->_state = HTTPDocument::S_read_trailer;
} else {
if (_bytes_remaining == 0) {
return 0;
}
// Extract some of the bytes remaining in the chunk.
length = min(length, _bytes_remaining);
(*_source)->read(start, length);
length = (*_source)->gcount();
_bytes_remaining -= length;
if (_bytes_remaining == 0) {
// We're done.
if (_doc != (HTTPDocument *)NULL && _read_index == _doc->_read_index) {
// An IdentityStreamBuf doesn't have a trailer, so we've already
// "read" it.
_doc->_state = HTTPDocument::S_read_trailer;
}
}
}
return length;
}
#endif // HAVE_SSL

View File

@ -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

View File

@ -0,0 +1,27 @@
// Filename: socketStream.I
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: ISocketStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ISocketStream::
ISocketStream(streambuf *buf) : istream(buf) {
}

View File

@ -0,0 +1,54 @@
// Filename: socketStream.h
// Created by: drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef SOCKETSTREAM_H
#define SOCKETSTREAM_H
#include "pandabase.h"
// At the present, this module is not compiled if OpenSSL is not
// available, since the only current use for it is to implement
// OpenSSL-defined constructs (like ISocketStream).
#ifdef HAVE_SSL
////////////////////////////////////////////////////////////////////
// Class : ISocketStream
// Description : This is a base class for istreams implemented in
// Panda that read from a (possibly non-blocking)
// socket. It adds is_closed(), which can be called
// after an eof condition to check whether the socket
// has been closed, or whether more data may be
// available later.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ISocketStream : public istream {
public:
INLINE ISocketStream(streambuf *buf);
PUBLISHED:
virtual bool is_closed() = 0;
};
#include "socketStream.I"
#endif // HAVE_SSL
#endif