diff --git a/panda/src/downloader/httpChannel.I b/panda/src/downloader/httpChannel.I index 0d147be377..32a5beab02 100644 --- a/panda/src/downloader/httpChannel.I +++ b/panda/src/downloader/httpChannel.I @@ -484,22 +484,3 @@ is_download_complete() const { return (_download_dest != DD_none && (_state == S_read_body || _state == S_read_trailer)); } - -//////////////////////////////////////////////////////////////////// -// 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(); - } -} diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index a9ebf7fe49..e1f3afe862 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -64,6 +64,7 @@ HTTPChannel(HTTPClient *client) : _bytes_requested = 0; _status_code = 0; _status_string = string(); + _response_type = RT_none; _proxy = _client->get_proxy(); _http_version = _client->get_http_version(); _http_version_string = _client->get_http_version_string(); @@ -602,11 +603,21 @@ run_proxy_request_sent() { // Wait for the first line to come back from the server. string line; if (!http_getline(line)) { + if (_bio.is_null()) { + // Huh, the proxy hung up on us as soon as we tried to connect. + if (_response_type == RT_hangup) { + // This was our second immediate hangup in a row. Give up. + _state = S_failure; + + } else { + // Try again, once. + _response_type = RT_hangup; + } + } return true; } if (!parse_http_response(line)) { - _state = S_failure; return false; } @@ -614,6 +625,7 @@ run_proxy_request_sent() { _current_field_name = string(); _current_field_value = string(); _headers.clear(); + _file_size = 0; return false; } @@ -832,11 +844,21 @@ run_request_sent() { // Wait for the first line to come back from the server. string line; if (!http_getline(line)) { + if (_bio.is_null()) { + // Huh, the server hung up on us as soon as we tried to connect. + if (_response_type == RT_hangup) { + // This was our second immediate hangup in a row. Give up. + _state = S_failure; + + } else { + // Try again, once. + _response_type = RT_hangup; + } + } return true; } if (!parse_http_response(line)) { - _state = S_failure; return false; } @@ -844,6 +866,7 @@ run_request_sent() { _current_field_name = string(); _current_field_value = string(); _headers.clear(); + _file_size = 0; return false; } @@ -997,6 +1020,18 @@ run_begin_body() { // Therefore, we have already read the (nonexistent) body. _state = S_ready; + } else if (_file_size > 8192) { + // If we know the size of the body we are about to skip and it's + // too large (and here we arbitrarily say 8KB is too large), then + // don't bother skipping it--just drop the connection and get a + // new one. + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Dropping connection rather than skipping past " << _file_size + << " bytes.\n"; + } + free_bio(); + } else { nassertr(_body_stream == NULL, false); _body_stream = read_body(); @@ -1281,11 +1316,9 @@ begin_request(const string &method, const URLSpec &url, const string &body, void HTTPChannel:: reset_for_new_request() { reset_download_to(); - _status_code = 0; - _status_string = string(); - _redirect_trail.clear(); _last_status_code = 0; - _file_size = 0; + _response_type = RT_none; + _redirect_trail.clear(); _bytes_downloaded = 0; _bytes_requested = 0; } @@ -1397,9 +1430,22 @@ parse_http_response(const string &line) { // Not an HTTP response. _status_code = 0; _status_string = "Not an HTTP response"; + if (_response_type == RT_non_http) { + // This was our second non-HTTP response in a row. Give up. + _state = S_failure; + + } else { + // Maybe we were just in some bad state. Drop the connection + // and try again, once. + free_bio(); + _response_type = RT_non_http; + } return false; } + // Okay, we're sane. + _response_type = RT_http; + // Split out the first line into its three components. size_t p = 5; while (p < line.length() && !isspace(line[p])) { @@ -1530,6 +1576,25 @@ parse_content_range(const string &content_range) { } +//////////////////////////////////////////////////////////////////// +// 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(). +//////////////////////////////////////////////////////////////////// +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(); + } +} + //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::verify_server // Access: Private diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index 50833354fe..058edbb5e1 100644 --- a/panda/src/downloader/httpChannel.h +++ b/panda/src/downloader/httpChannel.h @@ -153,7 +153,7 @@ private: bool parse_http_header(); bool parse_content_range(const string &content_range); - INLINE void check_socket(); + void check_socket(); bool verify_server(X509_NAME *subject) const; static string get_x509_name_component(X509_NAME *name, int nid); @@ -218,6 +218,15 @@ private: string _realm; URLSpec _redirect; + enum ResponseType { + RT_none, + RT_hangup, + RT_non_http, + RT_http + }; + ResponseType _response_type; + + typedef pmap Headers; Headers _headers; diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index dd05946f8d..ae2e80a235 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -301,11 +301,18 @@ clear_expected_servers() { // connection, for greater network efficiency than // calling HTTPClient::get_document() repeatedly (and // thus forcing a new connection for each document). +// +// Pass true for persistent_connection to gain this +// network efficiency. If, on the other hand, your +// intention is to use the channel to retrieve only one +// document, then pass false to inform the server that +// we will be dropping the connection after the first +// document. //////////////////////////////////////////////////////////////////// PT(HTTPChannel) HTTPClient:: -make_channel() { +make_channel(bool persistent_connection) { PT(HTTPChannel) doc = new HTTPChannel(this); - doc->set_persistent_connection(true); + doc->set_persistent_connection(persistent_connection); return doc; } diff --git a/panda/src/downloader/httpClient.h b/panda/src/downloader/httpClient.h index d819b4d012..e2cee21b5d 100644 --- a/panda/src/downloader/httpClient.h +++ b/panda/src/downloader/httpClient.h @@ -89,7 +89,7 @@ PUBLISHED: bool add_expected_server(const string &server_attributes); void clear_expected_servers(); - PT(HTTPChannel) make_channel(); + PT(HTTPChannel) make_channel(bool persistent_connection); PT(HTTPChannel) post_form(const URLSpec &url, const string &body); PT(HTTPChannel) get_document(const URLSpec &url, const string &body = string());