mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
add do_connect()
This commit is contained in:
parent
191cd9ff38
commit
3ad212f066
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -27,9 +27,9 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
BioStreamPtr::
|
||||
~BioStreamPtr() {
|
||||
if (_stream != (IBioStream *)NULL) {
|
||||
if (_stream != (BioStream *)NULL) {
|
||||
delete _stream;
|
||||
_stream = (IBioStream *)NULL;
|
||||
_stream = (BioStream *)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -13,5 +13,6 @@
|
||||
#include "identityStreamBuf.cxx"
|
||||
#include "multiplexStream.cxx"
|
||||
#include "multiplexStreamBuf.cxx"
|
||||
#include "socketStream.cxx"
|
||||
#include "urlSpec.cxx"
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -24,4 +24,24 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ISocketStream::
|
||||
ISocketStream(streambuf *buf) : istream(buf) {
|
||||
_data_expected = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OSocketStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE OSocketStream::
|
||||
OSocketStream(streambuf *buf) : ostream(buf) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SocketStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE SocketStream::
|
||||
SocketStream(streambuf *buf) : iostream(buf) {
|
||||
_data_expected = 0;
|
||||
}
|
||||
|
165
panda/src/downloader/socketStream.cxx
Normal file
165
panda/src/downloader/socketStream.cxx
Normal 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();
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user