mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
non-blocking I/O
This commit is contained in:
parent
a33c3414b9
commit
36d8e8feb3
@ -75,3 +75,25 @@ INLINE BIO *BioPtr::
|
|||||||
get_bio() const {
|
get_bio() const {
|
||||||
return _bio;
|
return _bio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: BioPtr::get_server_name
|
||||||
|
// Access: Public
|
||||||
|
// Description: Returns the name of the server we are (or should be)
|
||||||
|
// connected to.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE const string &BioPtr::
|
||||||
|
get_server_name() const {
|
||||||
|
return _server_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: BioPtr::get_port
|
||||||
|
// Access: Public
|
||||||
|
// Description: Returns the port on which we are (or should be)
|
||||||
|
// connected.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE int BioPtr::
|
||||||
|
get_port() const {
|
||||||
|
return _port;
|
||||||
|
}
|
||||||
|
@ -52,6 +52,8 @@ public:
|
|||||||
INLINE BIO *get_bio() const;
|
INLINE BIO *get_bio() const;
|
||||||
|
|
||||||
bool connect() const;
|
bool connect() const;
|
||||||
|
INLINE const string &get_server_name() const;
|
||||||
|
INLINE int get_port() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BIO *_bio;
|
BIO *_bio;
|
||||||
|
@ -103,6 +103,7 @@ underflow() {
|
|||||||
// Oops, we didn't read what we thought we would.
|
// Oops, we didn't read what we thought we would.
|
||||||
if (read_count <= 0) {
|
if (read_count <= 0) {
|
||||||
_is_closed = !BIO_should_retry(*_source);
|
_is_closed = !BIO_should_retry(*_source);
|
||||||
|
gbump(num_bytes);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ underflow() {
|
|||||||
if (read_count != num_bytes) {
|
if (read_count != num_bytes) {
|
||||||
// Oops, we didn't read what we thought we would.
|
// Oops, we didn't read what we thought we would.
|
||||||
if (read_count == 0) {
|
if (read_count == 0) {
|
||||||
|
gbump(num_bytes);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,16 +147,16 @@ read_chars(char *start, size_t length) {
|
|||||||
// Extract some of the bytes remaining in the chunk.
|
// Extract some of the bytes remaining in the chunk.
|
||||||
length = min(length, _chunk_remaining);
|
length = min(length, _chunk_remaining);
|
||||||
(*_source)->read(start, length);
|
(*_source)->read(start, length);
|
||||||
length = (*_source)->gcount();
|
size_t read_count = (*_source)->gcount();
|
||||||
_chunk_remaining -= length;
|
_chunk_remaining -= read_count;
|
||||||
return length;
|
return read_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the next chunk.
|
// Read the next chunk.
|
||||||
string line;
|
string line;
|
||||||
getline(**_source, line);
|
if (!http_getline(line)) {
|
||||||
if (!line.empty() && line[line.length() - 1] == '\r') {
|
// EOF (or data unavailable) while trying to read the chunk size.
|
||||||
line = line.substr(0, line.length() - 1);
|
return 0;
|
||||||
}
|
}
|
||||||
size_t chunk_size = (size_t)strtol(line.c_str(), NULL, 16);
|
size_t chunk_size = (size_t)strtol(line.c_str(), NULL, 16);
|
||||||
if (chunk_size == 0) {
|
if (chunk_size == 0) {
|
||||||
@ -175,4 +176,40 @@ read_chars(char *start, size_t length) {
|
|||||||
return read_chars(start, length);
|
return read_chars(start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: ChunkedStreamBuf::http_getline
|
||||||
|
// Access: Private
|
||||||
|
// Description: Reads a single line from the stream. Returns
|
||||||
|
// true if the line is successfully retrieved, or false
|
||||||
|
// if a complete line has not yet been received or if
|
||||||
|
// the connection has been closed.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool ChunkedStreamBuf::
|
||||||
|
http_getline(string &str) {
|
||||||
|
nassertr(!_source.is_null(), false);
|
||||||
|
int ch = (*_source)->get();
|
||||||
|
while (!(*_source)->eof() && !(*_source)->fail()) {
|
||||||
|
switch (ch) {
|
||||||
|
case '\n':
|
||||||
|
// end-of-line character, we're done.
|
||||||
|
if (downloader_cat.is_spam()) {
|
||||||
|
downloader_cat.spam() << "recv: " << _working_getline << "\n";
|
||||||
|
}
|
||||||
|
str = _working_getline;
|
||||||
|
_working_getline = string();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
// Ignore CR characters.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_working_getline += (char)ch;
|
||||||
|
}
|
||||||
|
ch = (*_source)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_SSL
|
#endif // HAVE_SSL
|
||||||
|
@ -47,10 +47,12 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
size_t read_chars(char *start, size_t length);
|
size_t read_chars(char *start, size_t length);
|
||||||
|
bool http_getline(string &str);
|
||||||
|
|
||||||
PT(BioStreamPtr) _source;
|
PT(BioStreamPtr) _source;
|
||||||
size_t _chunk_remaining;
|
size_t _chunk_remaining;
|
||||||
bool _done;
|
bool _done;
|
||||||
|
string _working_getline;
|
||||||
|
|
||||||
PT(HTTPChannel) _doc;
|
PT(HTTPChannel) _doc;
|
||||||
int _read_index;
|
int _read_index;
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE bool HTTPChannel::
|
INLINE bool HTTPChannel::
|
||||||
is_valid() const {
|
is_valid() const {
|
||||||
return (!_source.is_null() && (_status_code / 100) == 2);
|
return (!_source.is_null() && _state != S_failure &&
|
||||||
|
(_status_code / 100) == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -192,7 +193,9 @@ get_file_size() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE bool HTTPChannel::
|
INLINE bool HTTPChannel::
|
||||||
post_form(const URLSpec &url, const string &body) {
|
post_form(const URLSpec &url, const string &body) {
|
||||||
return send_request("POST", url, body);
|
begin_request("POST", url, body, false);
|
||||||
|
run();
|
||||||
|
return is_valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -203,7 +206,9 @@ post_form(const URLSpec &url, const string &body) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE bool HTTPChannel::
|
INLINE bool HTTPChannel::
|
||||||
get_document(const URLSpec &url) {
|
get_document(const URLSpec &url) {
|
||||||
return send_request("GET", url, string());
|
begin_request("GET", url, string(), false);
|
||||||
|
run();
|
||||||
|
return is_valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -217,5 +222,75 @@ get_document(const URLSpec &url) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE bool HTTPChannel::
|
INLINE bool HTTPChannel::
|
||||||
get_header(const URLSpec &url) {
|
get_header(const URLSpec &url) {
|
||||||
return send_request("HEAD", url, string());
|
begin_request("HEAD", url, string(), false);
|
||||||
|
run();
|
||||||
|
return is_valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: HTTPChannel::request_post_form
|
||||||
|
// Access: Published
|
||||||
|
// Description: Posts form data to a particular URL and retrieves the
|
||||||
|
// response, all using non-blocking I/O. See
|
||||||
|
// request_document() and post_form().
|
||||||
|
//
|
||||||
|
// It is important to note that you *must* call run()
|
||||||
|
// repeatedly after calling this method until run()
|
||||||
|
// returns false, and you may not call any other
|
||||||
|
// document posting or retrieving methods using the
|
||||||
|
// HTTPChannel object in the interim, or your form data
|
||||||
|
// may not get posted.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void HTTPChannel::
|
||||||
|
request_post_form(const URLSpec &url, const string &body) {
|
||||||
|
begin_request("POST", url, body, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: HTTPChannel::request_document
|
||||||
|
// Access: Published
|
||||||
|
// Description: Begins a non-blocking request to retrieve a given
|
||||||
|
// document. This method will return immediately, even
|
||||||
|
// before a connection to the server has necessarily
|
||||||
|
// been established; you must then call run() from time
|
||||||
|
// to time until the return value of run() is false.
|
||||||
|
// Then you may check is_valid() and get_status_code()
|
||||||
|
// to determine the status of your request.
|
||||||
|
//
|
||||||
|
// If a previous request had been pending, that request
|
||||||
|
// is discarded.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void HTTPChannel::
|
||||||
|
request_document(const URLSpec &url) {
|
||||||
|
begin_request("GET", url, string(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: HTTPChannel::request_header
|
||||||
|
// Access: Published
|
||||||
|
// Description: Begins a non-blocking request to retrieve a given
|
||||||
|
// header. See request_document() and get_header().
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void HTTPChannel::
|
||||||
|
request_header(const URLSpec &url) {
|
||||||
|
begin_request("HEAD", url, string(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: HTTPChannel::check_socket
|
||||||
|
// Access: Private
|
||||||
|
// Description: Checks whether the connection to the server has been
|
||||||
|
// closed after a failed read. If it has, issues a
|
||||||
|
// warning and calls free_bio().
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void HTTPChannel::
|
||||||
|
check_socket() {
|
||||||
|
nassertv(!_source.is_null());
|
||||||
|
if ((*_source)->is_closed()) {
|
||||||
|
if (downloader_cat.is_debug()) {
|
||||||
|
downloader_cat.debug()
|
||||||
|
<< "Lost connection to server unexpectedly during read.\n";
|
||||||
|
}
|
||||||
|
free_bio();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@
|
|||||||
#include "bioStreamPtr.h"
|
#include "bioStreamPtr.h"
|
||||||
#include "pmap.h"
|
#include "pmap.h"
|
||||||
#include "pointerTo.h"
|
#include "pointerTo.h"
|
||||||
|
#include "config_downloader.h"
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
class HTTPClient;
|
class HTTPClient;
|
||||||
@ -58,11 +59,6 @@ class EXPCL_PANDAEXPRESS HTTPChannel : public VirtualFile {
|
|||||||
private:
|
private:
|
||||||
HTTPChannel(HTTPClient *client);
|
HTTPChannel(HTTPClient *client);
|
||||||
|
|
||||||
bool send_request(const string &method, const URLSpec &url,
|
|
||||||
const string &body);
|
|
||||||
bool send_request(const string &header, const string &body,
|
|
||||||
bool allow_reconnect);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~HTTPChannel();
|
virtual ~HTTPChannel();
|
||||||
|
|
||||||
@ -96,26 +92,48 @@ PUBLISHED:
|
|||||||
INLINE bool get_document(const URLSpec &url);
|
INLINE bool get_document(const URLSpec &url);
|
||||||
INLINE bool get_header(const URLSpec &url);
|
INLINE bool get_header(const URLSpec &url);
|
||||||
|
|
||||||
|
INLINE void request_post_form(const URLSpec &url, const string &body);
|
||||||
|
INLINE void request_document(const URLSpec &url);
|
||||||
|
INLINE void request_header(const URLSpec &url);
|
||||||
|
bool run();
|
||||||
|
|
||||||
ISocketStream *read_body();
|
ISocketStream *read_body();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool establish_connection();
|
bool run_connecting();
|
||||||
bool establish_http();
|
bool run_proxy_ready();
|
||||||
bool establish_https();
|
bool run_proxy_request_sent();
|
||||||
bool establish_http_proxy();
|
bool run_proxy_reading_header();
|
||||||
bool establish_https_proxy();
|
bool run_setup_ssl();
|
||||||
|
bool run_ssl_handshake();
|
||||||
|
bool run_ready();
|
||||||
|
bool run_request_sent();
|
||||||
|
bool run_reading_header();
|
||||||
|
bool run_read_header();
|
||||||
|
bool run_begin_body();
|
||||||
|
bool run_reading_body();
|
||||||
|
bool run_read_body();
|
||||||
|
bool run_read_trailer();
|
||||||
|
|
||||||
bool make_https_connection();
|
void begin_request(const string &method, const URLSpec &url,
|
||||||
|
const string &body, bool nonblocking);
|
||||||
|
|
||||||
|
bool http_getline(string &str);
|
||||||
|
bool http_send(const string &str);
|
||||||
|
bool parse_http_response(const string &line);
|
||||||
|
bool parse_http_header();
|
||||||
|
|
||||||
|
INLINE void check_socket();
|
||||||
bool verify_server(X509_NAME *subject) const;
|
bool verify_server(X509_NAME *subject) const;
|
||||||
|
|
||||||
static string get_x509_name_component(X509_NAME *name, int nid);
|
static string get_x509_name_component(X509_NAME *name, int nid);
|
||||||
static bool x509_name_subset(X509_NAME *name_a, X509_NAME *name_b);
|
static bool x509_name_subset(X509_NAME *name_a, X509_NAME *name_b);
|
||||||
|
|
||||||
void make_header(string &header, const string &method,
|
void make_header();
|
||||||
const URLSpec &url, const string &body);
|
void make_proxy_request_text();
|
||||||
|
void make_request_text(const string &authorization);
|
||||||
|
|
||||||
void set_url(const URLSpec &url);
|
void set_url(const URLSpec &url);
|
||||||
void issue_request(const string &header, const string &body);
|
|
||||||
void read_http_response();
|
|
||||||
void store_header_field(const string &field_name, const string &field_value);
|
void store_header_field(const string &field_name, const string &field_value);
|
||||||
bool get_authorization(string &authorization,
|
bool get_authorization(string &authorization,
|
||||||
const string &authenticate_request,
|
const string &authenticate_request,
|
||||||
@ -129,7 +147,6 @@ private:
|
|||||||
static void show_send(const string &message);
|
static void show_send(const string &message);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool prepare_for_next(bool allow_reconnect);
|
|
||||||
void free_bio();
|
void free_bio();
|
||||||
|
|
||||||
HTTPClient *_client;
|
HTTPClient *_client;
|
||||||
@ -137,18 +154,13 @@ private:
|
|||||||
PT(BioPtr) _bio;
|
PT(BioPtr) _bio;
|
||||||
PT(BioStreamPtr) _source;
|
PT(BioStreamPtr) _source;
|
||||||
bool _persistent_connection;
|
bool _persistent_connection;
|
||||||
|
bool _nonblocking;
|
||||||
|
|
||||||
URLSpec _url;
|
URLSpec _url;
|
||||||
string _method;
|
string _method;
|
||||||
|
string _header;
|
||||||
|
string _body;
|
||||||
|
|
||||||
enum State {
|
|
||||||
S_new,
|
|
||||||
S_read_header,
|
|
||||||
S_started_body,
|
|
||||||
S_read_body,
|
|
||||||
S_read_trailer
|
|
||||||
};
|
|
||||||
State _state;
|
|
||||||
int _read_index;
|
int _read_index;
|
||||||
|
|
||||||
HTTPClient::HTTPVersion _http_version;
|
HTTPClient::HTTPVersion _http_version;
|
||||||
@ -163,6 +175,45 @@ private:
|
|||||||
|
|
||||||
size_t _file_size;
|
size_t _file_size;
|
||||||
|
|
||||||
|
// These members are used to maintain the current state while
|
||||||
|
// communicating with the server. We need to store everything in
|
||||||
|
// the class object instead of using local variables because in the
|
||||||
|
// case of nonblocking I/O we have to be able to return to the
|
||||||
|
// caller after any I/O operation and resume later where we left
|
||||||
|
// off.
|
||||||
|
enum State {
|
||||||
|
S_new,
|
||||||
|
S_connecting,
|
||||||
|
S_proxy_ready,
|
||||||
|
S_proxy_request_sent,
|
||||||
|
S_proxy_reading_header,
|
||||||
|
S_setup_ssl,
|
||||||
|
S_ssl_handshake,
|
||||||
|
S_ready,
|
||||||
|
S_request_sent,
|
||||||
|
S_reading_header,
|
||||||
|
S_read_header,
|
||||||
|
S_begin_body,
|
||||||
|
S_reading_body,
|
||||||
|
S_read_body,
|
||||||
|
S_read_trailer,
|
||||||
|
S_failure
|
||||||
|
};
|
||||||
|
State _state;
|
||||||
|
State _done_state;
|
||||||
|
string _proxy_header;
|
||||||
|
string _proxy_request_text;
|
||||||
|
bool _proxy_tunnel;
|
||||||
|
string _request_text;
|
||||||
|
string _working_getline;
|
||||||
|
size_t _sent_so_far;
|
||||||
|
string _current_field_name;
|
||||||
|
string _current_field_value;
|
||||||
|
ISocketStream *_body_stream;
|
||||||
|
BIO *_sbio;
|
||||||
|
pset<URLSpec> _redirect_trail;
|
||||||
|
int _last_status_code;
|
||||||
|
|
||||||
typedef pmap<string, string> Tokens;
|
typedef pmap<string, string> Tokens;
|
||||||
typedef pmap<string, Tokens> AuthenticationSchemes;
|
typedef pmap<string, Tokens> AuthenticationSchemes;
|
||||||
static void parse_authentication_schemes(AuthenticationSchemes &schemes,
|
static void parse_authentication_schemes(AuthenticationSchemes &schemes,
|
||||||
|
@ -111,6 +111,7 @@ underflow() {
|
|||||||
if (read_count != num_bytes) {
|
if (read_count != num_bytes) {
|
||||||
// Oops, we didn't read what we thought we would.
|
// Oops, we didn't read what we thought we would.
|
||||||
if (read_count == 0) {
|
if (read_count == 0) {
|
||||||
|
gbump(num_bytes);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,32 +134,23 @@ underflow() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
size_t IdentityStreamBuf::
|
size_t IdentityStreamBuf::
|
||||||
read_chars(char *start, size_t length) {
|
read_chars(char *start, size_t length) {
|
||||||
|
size_t read_count = 0;
|
||||||
|
|
||||||
if (!_has_content_length) {
|
if (!_has_content_length) {
|
||||||
// If we have no restrictions on content length, read till end of
|
// If we have no restrictions on content length, read till end of
|
||||||
// file.
|
// file.
|
||||||
(*_source)->read(start, length);
|
(*_source)->read(start, length);
|
||||||
length = (*_source)->gcount();
|
read_count = (*_source)->gcount();
|
||||||
|
|
||||||
if (length == 0) {
|
|
||||||
// End of file; we're done.
|
|
||||||
if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
|
|
||||||
// An IdentityStreamBuf doesn't have a trailer, so we've already
|
|
||||||
// "read" it.
|
|
||||||
_doc->_state = HTTPChannel::S_read_trailer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (_bytes_remaining == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract some of the bytes remaining in the chunk.
|
// Extract some of the bytes remaining in the chunk.
|
||||||
|
|
||||||
|
if (_bytes_remaining != 0) {
|
||||||
length = min(length, _bytes_remaining);
|
length = min(length, _bytes_remaining);
|
||||||
(*_source)->read(start, length);
|
(*_source)->read(start, length);
|
||||||
length = (*_source)->gcount();
|
read_count = (*_source)->gcount();
|
||||||
_bytes_remaining -= length;
|
_bytes_remaining -= read_count;
|
||||||
|
}
|
||||||
|
|
||||||
if (_bytes_remaining == 0) {
|
if (_bytes_remaining == 0) {
|
||||||
// We're done.
|
// We're done.
|
||||||
@ -170,7 +162,19 @@ read_chars(char *start, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
if (read_count == 0) {
|
||||||
|
if ((*_source)->is_closed()) {
|
||||||
|
// socket closed; we're done.
|
||||||
|
if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
|
||||||
|
// An IdentityStreamBuf doesn't have a trailer, so we've already
|
||||||
|
// "read" it.
|
||||||
|
_doc->_state = HTTPChannel::S_read_trailer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_SSL
|
#endif // HAVE_SSL
|
||||||
|
Loading…
x
Reference in New Issue
Block a user