// Filename: httpChannel.I // Created by: drose (24Sep02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_client // Access: Published // Description: Returns the HTTPClient object that owns this channel. //////////////////////////////////////////////////////////////////// INLINE HTTPClient *HTTPChannel:: get_client() const { return _client; } //////////////////////////////////////////////////////////////////// // 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 && (get_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_entry._status_code; } //////////////////////////////////////////////////////////////////// // 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::get_num_redirect_steps // Access: Published // Description: If the document automatically followed one or more // redirects, this will return the number of redirects // that were automatically followed. Use // get_redirect_step() to retrieve each URL in // sequence. //////////////////////////////////////////////////////////////////// INLINE int HTTPChannel:: get_num_redirect_steps() const { return _redirect_trail.size(); } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_redirect_step // Access: Published // Description: Use in conjunction with get_num_redirect_steps() to // extract the chain of URL's that the channel was // automatically redirected through to arrive at the // final document. //////////////////////////////////////////////////////////////////// INLINE const URLSpec &HTTPChannel:: get_redirect_step(int n) const { nassertr(n >= 0 && n < (int)_redirect_trail.size(), _redirect_trail[0]); return _redirect_trail[n]; } //////////////////////////////////////////////////////////////////// // 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_allow_proxy // Access: Published // Description: If this is true (the normal case), the HTTPClient // will be consulted for information about the proxy to // be used for each connection via this HTTPChannel. If // this has been set to false by the user, then all // connections will be made directly, regardless of the // proxy settings indicated on the HTTPClient. //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: set_allow_proxy(bool allow_proxy) { _allow_proxy = allow_proxy; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_allow_proxy // Access: Published // Description: If this is true (the normal case), the HTTPClient // will be consulted for information about the proxy to // be used for each connection via this HTTPChannel. If // this has been set to false by the user, then all // connections will be made directly, regardless of the // proxy settings indicated on the HTTPClient. //////////////////////////////////////////////////////////////////// INLINE bool HTTPChannel:: get_allow_proxy() const { return _allow_proxy; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::set_proxy_tunnel // Access: Published // Description: Normally, a proxy is itself asked for ordinary URL's, // and the proxy decides whether to hand the client a // cached version of the document or to contact the // server for a fresh version. The proxy may also // modify the headers and transfer encoding on the way. // // If this is set to true, then instead of asking for // URL's from the proxy, we will ask the proxy to open a // connection to the server (for instance, on port 80); // if the proxy honors this request, then we contact the // server directly through this connection to retrieve // the document. If the proxy does not honor the // connect request, then the retrieve operation fails. // // SSL connections (e.g. https), and connections through // a Socks proxy, are always tunneled, regardless of the // setting of this flag. //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: set_proxy_tunnel(bool proxy_tunnel) { _proxy_tunnel = proxy_tunnel; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_proxy_tunnel // Access: Published // Description: Returns true if connections always tunnel through a // proxy, or false (the normal case) if we allow the // proxy to serve up documents. See set_proxy_tunnel(). //////////////////////////////////////////////////////////////////// INLINE bool HTTPChannel:: get_proxy_tunnel() const { return _proxy_tunnel; } //////////////////////////////////////////////////////////////////// // 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_skip_body_size // Access: Published // Description: Specifies the maximum number of bytes in a received // (but unwanted) body that will be skipped past, in // order to reset to a new request. // // That is, if this HTTPChannel requests a file via // get_document(), but does not call download_to_ram(), // download_to_file(), or open_read_body(), and instead // immediately requests a new file, then the HTTPChannel // has a choice whether to skip past the unwanted // document, or to close the connection and open a new // one. If the number of bytes to skip is more than // this threshold, the connection will be closed; // otherwise, the data will simply be read and // discarded. //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: set_skip_body_size(size_t skip_body_size) { _skip_body_size = skip_body_size; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_skip_body_size // Access: Published // Description: Returns the maximum number of bytes in a received // (but unwanted) body that will be skipped past, in // order to reset to a new request. See // set_skip_body_size(). //////////////////////////////////////////////////////////////////// INLINE size_t HTTPChannel:: get_skip_body_size() const { return _skip_body_size; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::set_idle_timeout // Access: Published // Description: Specifies the amount of time, in seconds, in which a // previously-established connection is allowed to // remain open and unused. If a previous connection has // remained unused for at least this number of seconds, // it will be closed and a new connection will be // opened; otherwise, the same connection will be reused // for the next request (for this particular // HTTPChannel). //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: set_idle_timeout(double idle_timeout) { _idle_timeout = idle_timeout; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_idle_timeout // Access: Published // Description: Returns the amount of time, in seconds, in which an // previously-established connection is allowed to // remain open and unused. See set_idle_timeout(). //////////////////////////////////////////////////////////////////// INLINE double HTTPChannel:: get_idle_timeout() const { return _idle_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::set_expected_file_size // Access: Published // Description: This may be called immediately after a call to // get_document() or some related function to specify // the expected size of the document we are retrieving, // if we happen to know. This is used as the return // value to get_file_size() only in the case that the // server does not tell us the actual file size. //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: set_expected_file_size(size_t file_size) { _expected_file_size = file_size; _got_expected_file_size = true; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::is_file_size_known // Access: Published // Description: Returns true if the size of the file we are currently // retrieving was told us by the server and thus is // reliably known, or false if the size reported by // get_file_size() represents an educated guess // (possibly as set by set_expected_file_size(), or as // inferred from a chunked transfer encoding in // progress). //////////////////////////////////////////////////////////////////// INLINE bool HTTPChannel:: is_file_size_known() const { return _got_file_size; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_first_byte_requested // Access: Published // Description: Returns the first byte of the file requested by the // request. This will normally be 0 to indicate that // the file is being requested from the beginning, but // if the file was requested via a get_subdocument() // call, this will contain the first_byte parameter from // that call. //////////////////////////////////////////////////////////////////// INLINE size_t HTTPChannel:: get_first_byte_requested() const { return _first_byte_requested; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_last_byte_requested // Access: Published // Description: Returns the last byte of the file requested by the // request. This will normally be 0 to indicate that // the file is being requested to its last byte, but if // the file was requested via a get_subdocument() call, // this will contain the last_byte parameter from that // call. //////////////////////////////////////////////////////////////////// INLINE size_t HTTPChannel:: get_last_byte_requested() const { return _last_byte_requested; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_first_byte_delivered // Access: Published // Description: Returns the first byte of the file (that will be) // delivered by the server in response to the current // request. Normally, this is the same as // get_first_byte_requested(), but some servers will // ignore a subdocument request and always return the // whole file, in which case this value will be 0, // regardless of what was requested to // get_subdocument(). //////////////////////////////////////////////////////////////////// INLINE size_t HTTPChannel:: get_first_byte_delivered() const { return _first_byte_delivered; } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_last_byte_delivered // Access: Published // Description: Returns the last byte of the file (that will be) // delivered by the server in response to the current // request. Normally, this is the same as // get_last_byte_requested(), but some servers will // ignore a subdocument request and always return the // whole file, in which case this value will be 0, // regardless of what was requested to // get_subdocument(). //////////////////////////////////////////////////////////////////// INLINE size_t HTTPChannel:: get_last_byte_delivered() const { return _last_byte_delivered; } //////////////////////////////////////////////////////////////////// // 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(); _status_list.clear(); } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::preserve_status // Access: Published // Description: Preserves the previous status code (presumably a // failure) from the previous connection attempt. If // the subsequent connection attempt also fails, the // returned status code will be the better of the // previous code and the current code. // // This can be called to daisy-chain subsequent attempts // to download the same document from different servers. // After all servers have been attempted, the final // status code will reflect the attempt that most nearly // succeeded. //////////////////////////////////////////////////////////////////// INLINE void HTTPChannel:: preserve_status() { _status_list.push_back(_status_entry); } //////////////////////////////////////////////////////////////////// // 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); while (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); while (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); while (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); while (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); while (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); while (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); while (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); while (run()) { } return is_connection_ready(); } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_options // Access: Published // Description: Sends an OPTIONS message to the server, which should // query the available options, possibly in relation to // a specified URL. //////////////////////////////////////////////////////////////////// INLINE bool HTTPChannel:: get_options(const DocumentSpec &url) { begin_request(HTTPEnum::M_options, url, string(), false, 0, 0); while (run()) { } return is_valid(); } //////////////////////////////////////////////////////////////////// // 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)); } //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::StatusEntry::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// INLINE HTTPChannel::StatusEntry:: StatusEntry() { _status_code = SC_incomplete; }