open_toontown_panda3d/panda/src/downloader/httpChannel.I

744 lines
32 KiB
Plaintext

// Filename: httpChannel.I
// Created by: drose (24Sep02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::is_valid
// Access: Published
// Description: Returns true if the last-requested document was
// successfully retrieved and is ready to be read, false
// otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
is_valid() const {
return (_state != S_failure && (_status_code / 100) == 2 &&
(_server_response_has_no_body || !_source.is_null()));
}
////////////////////////////////////////////////////////////////////
// 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
// Description: Returns the URL that was used to retrieve the
// most recent document: whatever URL was last passed to
// get_document() or get_header(). If a redirect has
// transparently occurred, this will return the new,
// redirected URL (the actual URL at which the document
// was located).
////////////////////////////////////////////////////////////////////
INLINE const URLSpec &HTTPChannel::
get_url() const {
return _document_spec.get_url();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_document_spec
// Access: Published
// Description: Returns the DocumentSpec associated with the most
// recent document. This includes its actual URL
// (following redirects) along with the identity tag and
// last-modified date, if supplied by the server.
//
// This structure may be saved and used to retrieve the
// same version of the document later, or to
// conditionally retrieve a newer version if it is
// available.
////////////////////////////////////////////////////////////////////
INLINE const DocumentSpec &HTTPChannel::
get_document_spec() const {
return _document_spec;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_http_version
// Access: Published
// Description: Returns the HTTP version number returned by the
// server, as one of the HTTPClient enumerated types,
// e.g. HTTPClient::HV_11.
////////////////////////////////////////////////////////////////////
INLINE HTTPEnum::HTTPVersion HTTPChannel::
get_http_version() const {
return _http_version;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_http_version_string
// Access: Published
// Description: Returns the HTTP version number returned by the
// server, formatted as a string, e.g. "HTTP/1.1".
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPChannel::
get_http_version_string() const {
return _http_version_string;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_status_code
// Access: Published
// Description: Returns the HTML return code from the document
// retrieval request. This will be in the 200 range if
// the document is successfully retrieved, or some other
// value in the case of an error.
//
// Some proxy errors during an https-over-proxy request
// would return the same status code as a different
// error that occurred on the host server. To
// differentiate these cases, status codes that are
// returned by the proxy during the CONNECT phase
// (except code 407) are incremented by 1000.
////////////////////////////////////////////////////////////////////
INLINE int HTTPChannel::
get_status_code() const {
return _status_code;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_status_string
// Access: Published
// Description: Returns the string as returned by the server
// describing the status code for humans. This may or
// may not be meaningful.
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPChannel::
get_status_string() const {
return _status_string;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_www_realm
// Access: Published
// Description: If the document failed to connect because of a 401
// (Authorization required), this method will return the
// "realm" returned by the server in which the requested
// document must be authenticated. This string may be
// presented to the user to request an associated
// username and password (which then should be stored in
// HTTPClient::set_username()).
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPChannel::
get_www_realm() const {
return _www_realm;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_proxy_realm
// Access: Published
// Description: If the document failed to connect because of a 407
// (Proxy authorization required), this method will
// return the "realm" returned by the proxy. This
// string may be presented to the user to request an
// associated username and password (which then should
// be stored in HTTPClient::set_username()).
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPChannel::
get_proxy_realm() const {
return _proxy_realm;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_redirect
// Access: Published
// Description: If the document failed with a redirect code (300
// series), this will generally contain the new URL the
// server wants us to try. In many cases, the client
// will automatically follow redirects; if these are
// succesful the client will return a successful code
// and get_redirect() will return empty, but get_url()
// will return the new, redirected URL.
////////////////////////////////////////////////////////////////////
INLINE const URLSpec &HTTPChannel::
get_redirect() const {
return _redirect;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_persistent_connection
// Access: Published
// Description: Indicates whether the HTTPChannel should try to keep
// the connection to the server open and reuse that
// connection for multiple documents, or whether it
// should close the connection and open a new one for
// each request. Set this true to keep the connections
// around when possible, false to recycle them.
//
// It makes most sense to set this false when the
// HTTPChannel will be used only once to retrieve a
// single document, true when you will be using the same
// HTTPChannel object to retrieve multiple documents.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_persistent_connection(bool persistent_connection) {
_persistent_connection = persistent_connection;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_persistent_connection
// Access: Published
// Description: Returns whether the HTTPChannel should try to keep
// the connection to the server open and reuse that
// connection for multiple documents, or whether it
// should close the connection and open a new one for
// each request. See set_persistent_connection().
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_persistent_connection() const {
return _persistent_connection;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_connect_timeout
// Access: Published
// Description: Sets the maximum length of time, in seconds, that the
// channel will wait before giving up on establishing a
// TCP connection.
//
// At present, this is used only for the nonblocking
// interfaces (e.g. begin_get_document(),
// begin_connect_to()), but it is used whether
// set_blocking_connect() is true or false.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_connect_timeout(double connect_timeout) {
_connect_timeout = connect_timeout;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_connect_timeout
// Access: Published
// Description: Returns the length of time, in seconds, to wait for a
// new nonblocking socket to connect. See
// set_connect_timeout().
////////////////////////////////////////////////////////////////////
INLINE double HTTPChannel::
get_connect_timeout() const {
return _connect_timeout;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_blocking_connect
// Access: Published
// Description: If this flag is true, a socket connect will block
// even for nonblocking I/O calls like
// begin_get_document(), begin_connect_to(), etc. If
// false, a socket connect will not block for
// nonblocking I/O calls, but will block for blocking
// I/O calls (get_document(), connect_to(), etc.).
// Setting this true is useful when you want to use
// non-blocking I/O once you have established the
// connection, but you don't want to bother with polling
// for the initial connection. It's also useful when
// you don't particularly care about non-blocking I/O,
// but you need to respect timeouts like connect_timeout
// and http_timeout.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_blocking_connect(bool blocking_connect) {
_blocking_connect = blocking_connect;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_blocking_connect
// Access: Published
// Description: If this flag is true, a socket connect will block
// even for nonblocking I/O calls like
// begin_get_document(), begin_connect_to(), etc. If
// false, a socket connect will not block for
// nonblocking I/O calls, but will block for blocking
// I/O calls (get_document(), connect_to(), etc.).
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_blocking_connect() const {
return _blocking_connect;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_http_timeout
// Access: Published
// Description: Sets the maximum length of time, in seconds, that the
// channel will wait for the HTTP server to finish
// sending its response to our request.
//
// The timer starts counting after the TCP connection
// has been established (see set_connect_timeout(),
// above) and the request has been sent.
//
// At present, this is used only for the nonblocking
// interfaces (e.g. begin_get_document(),
// begin_connect_to()), but it is used whether
// set_blocking_connect() is true or false.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_http_timeout(double http_timeout) {
_http_timeout = http_timeout;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_http_timeout
// Access: Published
// Description: Returns the length of time, in seconds, to wait for
// the HTTP server to respond to our request. See
// set_http_timeout().
////////////////////////////////////////////////////////////////////
INLINE double HTTPChannel::
get_http_timeout() const {
return _http_timeout;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_download_throttle
// Access: Published
// Description: Specifies whether nonblocking downloads (via
// download_to_file() or download_to_ram()) will be
// limited so as not to use all available bandwidth.
//
// If this is true, when a download has been started on
// this channel it will be invoked no more frequently
// than get_max_updates_per_second(), and the total
// bandwidth used by the download will be no more than
// get_max_bytes_per_second(). If this is false,
// downloads will proceed as fast as the server can send
// the data.
//
// This only has effect on the nonblocking I/O methods
// like begin_get_document(), etc. The blocking methods
// like get_document() always use as much CPU and
// bandwidth as they can get.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_download_throttle(bool download_throttle) {
_download_throttle = download_throttle;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_download_throttle
// Access: Published
// Description: Returns whether the nonblocking downloads will be
// bandwidth-limited. See set_download_throttle().
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_download_throttle() const {
return _download_throttle;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_max_bytes_per_second
// Access: Published
// Description: When bandwidth throttling is in effect (see
// set_download_throttle()), this specifies the maximum
// number of bytes per second that may be consumed by
// this channel.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_max_bytes_per_second(double max_bytes_per_second) {
_max_bytes_per_second = max_bytes_per_second;
_bytes_per_update = int(_max_bytes_per_second * _seconds_per_update);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_max_bytes_per_second
// Access: Published
// Description: Returns the maximum number of bytes per second that
// may be consumed by this channel when
// get_download_throttle() is true.
////////////////////////////////////////////////////////////////////
INLINE double HTTPChannel::
get_max_bytes_per_second() const {
return _max_bytes_per_second;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::set_max_updates_per_second
// Access: Published
// Description: When bandwidth throttling is in effect (see
// set_download_throttle()), this specifies the maximum
// number of times per second that run() will attempt to
// do any downloading at all.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
set_max_updates_per_second(double max_updates_per_second) {
nassertv(max_updates_per_second != 0.0f);
_max_updates_per_second = max_updates_per_second;
_seconds_per_update = 1.0f / _max_updates_per_second;
_bytes_per_update = int(_max_bytes_per_second * _seconds_per_update);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_max_updates_per_second
// Access: Published
// Description: Returns the maximum number of times per second that
// run() will do anything at all, when
// get_download_throttle() is true.
////////////////////////////////////////////////////////////////////
INLINE double HTTPChannel::
get_max_updates_per_second() const {
return _max_updates_per_second;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_file_size
// Access: Published
// Description: Returns the size of the file, if it is known.
// Returns 0 if the file size is not known.
//
// If the file is dynamically generated, the size may
// not be available until a read has started
// (e.g. open_read_file() has been called); and even
// then it may increase as more of the file is read due
// to the nature of HTTP/1.1 requests which can change
// their minds midstream about how much data they're
// sending you.
////////////////////////////////////////////////////////////////////
INLINE size_t HTTPChannel::
get_file_size() const {
return _file_size;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::reset
// Access: Published
// Description: Stops whatever file transaction is currently in
// progress, closes the connection, and resets to begin
// anew. You shouldn't ever need to call this, since
// the channel should be able to reset itself cleanly
// between requests, but it is provided in case you are
// an especially nervous type.
//
// Don't call this after every request unless you set
// set_persistent_connection() to false, since calling
// reset() rudely closes the connection regardless of
// whether we have told the server we intend to keep it
// open or not.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
reset() {
reset_for_new_request();
reset_to_new();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::clear_extra_headers
// Access: Published
// Description: Resets the extra headers that were previously added
// via calls to send_extra_header().
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
clear_extra_headers() {
_send_extra_headers = string();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::send_extra_header
// Access: Published
// Description: Specifies an additional key: value pair that is added
// into the header sent to the server with the next
// request. This is passed along with no interpretation
// by the HTTPChannel code. You may call this
// repeatedly to append multiple headers.
//
// This is persistent for one request only; it must be
// set again for each new request.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
send_extra_header(const string &key, const string &value) {
_send_extra_headers += key;
_send_extra_headers += ": ";
_send_extra_headers += value;
_send_extra_headers += "\r\n";
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_document
// Access: Published
// Description: Opens the named document for reading, if available.
// Returns true if successful, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_document(const DocumentSpec &url) {
begin_request(HTTPEnum::M_get, url, string(), false, 0, 0);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_subdocument
// Access: Published
// Description: Retrieves only the specified byte range of the
// indicated document. If last_byte is 0, it stands for
// the last byte of the document. When a subdocument is
// requested, get_file_size() and get_bytes_downloaded()
// will report the number of bytes of the subdocument,
// not of the complete document.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_subdocument(const DocumentSpec &url, size_t first_byte, size_t last_byte) {
begin_request(HTTPEnum::M_get, url, string(), false, first_byte, last_byte);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_header
// Access: Published
// Description: Like get_document(), except only the header
// associated with the document is retrieved. This may
// be used to test for existence of the document; it
// might also return the size of the document (if the
// server gives us this information).
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_header(const DocumentSpec &url) {
begin_request(HTTPEnum::M_head, url, string(), false, 0, 0);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::post_form
// Access: Published
// Description: Posts form data to a particular URL and retrieves the
// response.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
post_form(const DocumentSpec &url, const string &body) {
begin_request(HTTPEnum::M_post, url, body, false, 0, 0);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::put_document
// Access: Published
// Description: Uploads the indicated body to the server to replace
// the indicated URL, if the server allows this.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
put_document(const DocumentSpec &url, const string &body) {
begin_request(HTTPEnum::M_put, url, body, false, 0, 0);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::delete_document
// Access: Published
// Description: Requests the server to remove the indicated URL.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
delete_document(const DocumentSpec &url) {
begin_request(HTTPEnum::M_delete, url, string(), false, 0, 0);
run();
return is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_trace
// Access: Published
// Description: Sends a TRACE message to the server, which should
// return back the same message as the server received
// it, allowing inspection of proxy hops, etc.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
get_trace(const DocumentSpec &url) {
begin_request(HTTPEnum::M_trace, 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 DocumentSpec &url) {
begin_request(HTTPEnum::M_connect, url, string(), false, 0, 0);
run();
return is_connection_ready();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::begin_get_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::
begin_get_document(const DocumentSpec &url) {
begin_request(HTTPEnum::M_get, url, string(), true, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::begin_get_subdocument
// Access: Published
// Description: Begins a non-blocking request to retrieve only the
// specified byte range of the indicated document. If
// last_byte is 0, it stands for the last byte of the
// document. When a subdocument is requested,
// get_file_size() and get_bytes_downloaded() will
// report the number of bytes of the subdocument, not of
// the complete document.
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
begin_get_subdocument(const DocumentSpec &url, size_t first_byte,
size_t last_byte) {
begin_request(HTTPEnum::M_get, url, string(), true, first_byte, last_byte);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::begin_get_header
// Access: Published
// Description: Begins a non-blocking request to retrieve a given
// header. See begin_get_document() and get_header().
////////////////////////////////////////////////////////////////////
INLINE void HTTPChannel::
begin_get_header(const DocumentSpec &url) {
begin_request(HTTPEnum::M_head, url, string(), true, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::begin_post_form
// Access: Published
// Description: Posts form data to a particular URL and retrieves the
// response, all using non-blocking I/O. See
// begin_get_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::
begin_post_form(const DocumentSpec &url, const string &body) {
begin_request(HTTPEnum::M_post, url, body, 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 DocumentSpec &url) {
begin_request(HTTPEnum::M_connect, url, string(), true, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_bytes_downloaded
// Access: Published
// Description: Returns the number of bytes downloaded during the
// last (or current) download_to_file() or
// download_to_ram operation(). This can be used in
// conjunction with get_file_size() to report the
// percent complete (but be careful, since
// get_file_size() may return 0 if the server has not
// told us the size of the file).
////////////////////////////////////////////////////////////////////
INLINE size_t HTTPChannel::
get_bytes_downloaded() const {
return _bytes_downloaded;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::get_bytes_requested
// Access: Published
// Description: When download throttling is in effect
// (set_download_throttle() has been set to true) and
// non-blocking I/O methods (like begin_get_document())
// are used, this returns the number of bytes
// "requested" from the server so far: that is, the
// theoretical maximum value for get_bytes_downloaded(),
// if the server has been keeping up with our demand.
//
// If this number is less than get_bytes_downloaded(),
// then the server has not been supplying bytes fast
// enough to meet our own download throttle rate.
//
// When download throttling is not in effect, or when
// the blocking I/O methods (like get_document(), etc.)
// are used, this returns 0.
////////////////////////////////////////////////////////////////////
INLINE size_t HTTPChannel::
get_bytes_requested() const {
return _bytes_requested;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::is_download_complete
// Access: Published
// Description: Returns true when a download_to() or
// download_to_ram() has executed and the file has been
// fully downloaded. If this still returns false after
// processing has completed, there was an error in
// transmission.
//
// Note that simply testing is_download_complete() does
// not prove that the requested document was succesfully
// retrieved--you might have just downloaded the "404
// not found" stub (for instance) that a server would
// provide in response to some error condition. You
// should also check is_valid() to prove that the file
// you expected has been successfully retrieved.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPChannel::
is_download_complete() const {
return (_download_dest != DD_none &&
(_state == S_read_body || _state == S_read_trailer));
}