add do_connect()

This commit is contained in:
David Rose 2002-10-20 00:17:23 +00:00
parent 191cd9ff38
commit 3ad212f066
18 changed files with 706 additions and 100 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#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();
}

View File

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