historical/toontown-classic.git/panda/include/httpChannel.h
2024-01-16 11:20:27 -06:00

459 lines
14 KiB
C++

/**
* 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."
*
* @file httpChannel.h
* @author drose
* @date 2002-09-24
*/
#ifndef HTTPCHANNEL_H
#define HTTPCHANNEL_H
#include "pandabase.h"
// This module requires OpenSSL to compile, even if you do not intend to use
// this to establish https connections; this is because it uses the OpenSSL
// library to portably handle all of the socket communications.
#ifdef HAVE_OPENSSL
#include "httpClient.h"
#include "httpEnum.h"
#include "urlSpec.h"
#include "documentSpec.h"
#include "bioPtr.h"
#include "bioStreamPtr.h"
#include "pmap.h"
#include "pvector.h"
#include "pointerTo.h"
#include "config_downloader.h"
#include "filename.h"
#include "typedReferenceCount.h"
typedef struct bio_st BIO;
class Ramfile;
class HTTPClient;
/**
* A single channel of communication from an HTTPClient. This is similar to
* the concept of a 'connection', except that HTTP is technically
* connectionless; in fact, a channel may represent one unbroken connection or
* it may transparently close and reopen a new connection with each request.
*
* A channel is conceptually a single thread of I/O. One document at a time
* may be requested using a channel; a new document may (in general) not be
* requested from the same HTTPChannel until the first document has been fully
* retrieved.
*/
class EXPCL_PANDA_DOWNLOADER HTTPChannel : public TypedReferenceCount {
private:
HTTPChannel(HTTPClient *client);
public:
virtual ~HTTPChannel();
PUBLISHED:
// get_status_code() will either return an HTTP-style status code >= 100
// (e.g. 404), or one of the following values. In general, these are
// ordered from less-successful to more-successful.
enum StatusCode {
SC_incomplete = 0,
SC_internal_error,
SC_no_connection,
SC_timeout,
SC_lost_connection,
SC_non_http_response,
SC_invalid_http,
SC_socks_invalid_version,
SC_socks_no_acceptable_login_method,
SC_socks_refused,
SC_socks_no_connection,
SC_ssl_internal_failure,
SC_ssl_no_handshake,
// No one returns this code, but StatusCode values higher than this are
// deemed more successful than any generic HTTP response.
SC_http_error_watermark,
SC_ssl_invalid_server_certificate,
SC_ssl_self_signed_server_certificate,
SC_ssl_unexpected_server,
// These errors are only generated after a download_to_*() call been
// issued.
SC_download_open_error,
SC_download_write_error,
SC_download_invalid_range,
};
INLINE HTTPClient *get_client() const;
INLINE bool is_valid() const;
INLINE bool is_connection_ready() const;
INLINE const URLSpec &get_url() const;
INLINE const DocumentSpec &get_document_spec() const;
INLINE HTTPEnum::HTTPVersion get_http_version() const;
INLINE const std::string &get_http_version_string() const;
INLINE int get_status_code() const;
std::string get_status_string() const;
INLINE const std::string &get_www_realm() const;
INLINE const std::string &get_proxy_realm() const;
INLINE const URLSpec &get_redirect() const;
std::string get_header_value(const std::string &key) const;
INLINE int get_num_redirect_steps() const;
INLINE const URLSpec &get_redirect_step(int n) const;
MAKE_SEQ(get_redirect_steps, get_num_redirect_steps, get_redirect_step);
INLINE void set_persistent_connection(bool persistent_connection);
INLINE bool get_persistent_connection() const;
bool will_close_connection() const;
INLINE void set_allow_proxy(bool allow_proxy);
INLINE bool get_allow_proxy() const;
INLINE void set_proxy_tunnel(bool proxy_tunnel);
INLINE bool get_proxy_tunnel() const;
INLINE void set_connect_timeout(double timeout_seconds);
INLINE double get_connect_timeout() const;
INLINE void set_blocking_connect(bool blocking_connect);
INLINE bool get_blocking_connect() const;
INLINE void set_http_timeout(double timeout_seconds);
INLINE double get_http_timeout() const;
INLINE void set_skip_body_size(size_t skip_body_size);
INLINE size_t get_skip_body_size() const;
INLINE void set_idle_timeout(double idle_timeout);
INLINE double get_idle_timeout() const;
INLINE void set_download_throttle(bool download_throttle);
INLINE bool get_download_throttle() const;
INLINE void set_max_bytes_per_second(double max_bytes_per_second);
INLINE double get_max_bytes_per_second() const;
INLINE void set_max_updates_per_second(double max_updates_per_second);
INLINE double get_max_updates_per_second() const;
INLINE void set_content_type(std::string content_type);
INLINE std::string get_content_type() const;
INLINE void set_expected_file_size(size_t file_size);
std::streamsize get_file_size() const;
INLINE bool is_file_size_known() const;
INLINE size_t get_first_byte_requested() const;
INLINE size_t get_last_byte_requested() const;
INLINE size_t get_first_byte_delivered() const;
INLINE size_t get_last_byte_delivered() const;
void write_headers(std::ostream &out) const;
INLINE void reset();
INLINE void preserve_status();
INLINE void clear_extra_headers();
INLINE void send_extra_header(const std::string &key, const std::string &value);
BLOCKING INLINE bool get_document(const DocumentSpec &url);
BLOCKING INLINE bool get_subdocument(const DocumentSpec &url,
size_t first_byte, size_t last_byte);
BLOCKING INLINE bool get_header(const DocumentSpec &url);
BLOCKING INLINE bool post_form(const DocumentSpec &url, const std::string &body);
BLOCKING INLINE bool put_document(const DocumentSpec &url, const std::string &body);
BLOCKING INLINE bool delete_document(const DocumentSpec &url);
BLOCKING INLINE bool get_trace(const DocumentSpec &url);
BLOCKING INLINE bool connect_to(const DocumentSpec &url);
BLOCKING INLINE bool get_options(const DocumentSpec &url);
INLINE void begin_get_document(const DocumentSpec &url);
INLINE void begin_get_subdocument(const DocumentSpec &url,
size_t first_byte, size_t last_byte);
INLINE void begin_get_header(const DocumentSpec &url);
INLINE void begin_post_form(const DocumentSpec &url, const std::string &body);
bool run();
INLINE void begin_connect_to(const DocumentSpec &url);
ISocketStream *open_read_body();
void close_read_body(std::istream *stream) const;
BLOCKING bool download_to_file(const Filename &filename, bool subdocument_resumes = true);
BLOCKING bool download_to_ram(Ramfile *ramfile, bool subdocument_resumes = true);
BLOCKING bool download_to_stream(std::ostream *strm, bool subdocument_resumes = true);
SocketStream *get_connection();
INLINE size_t get_bytes_downloaded() const;
INLINE size_t get_bytes_requested() const;
INLINE bool is_download_complete() const;
public:
static std::string downcase(const std::string &s);
void body_stream_destructs(ISocketStream *stream);
private:
bool reached_done_state();
bool run_try_next_proxy();
bool run_connecting();
bool run_connecting_wait();
bool run_http_proxy_ready();
bool run_http_proxy_request_sent();
bool run_http_proxy_reading_header();
bool run_socks_proxy_greet();
bool run_socks_proxy_greet_reply();
bool run_socks_proxy_connect();
bool run_socks_proxy_connect_reply();
bool run_setup_ssl();
bool run_ssl_handshake();
bool run_ready();
bool run_request_sent();
bool run_reading_header();
bool run_start_direct_file_read();
bool run_read_header();
bool run_begin_body();
bool run_reading_body();
bool run_read_body();
bool run_read_trailer();
bool run_download_to_file();
bool run_download_to_ram();
bool run_download_to_stream();
void begin_request(HTTPEnum::Method method, const DocumentSpec &url,
const std::string &body, bool nonblocking,
size_t first_byte, size_t last_byte);
void reconsider_proxy();
void reset_for_new_request();
void finished_body(bool has_trailer);
bool open_download_file();
bool server_getline(std::string &str);
bool server_getline_failsafe(std::string &str);
bool server_get(std::string &str, size_t num_bytes);
bool server_get_failsafe(std::string &str, size_t num_bytes);
bool server_send(const std::string &str, bool secret);
bool parse_http_response(const std::string &line);
bool parse_http_header();
bool parse_content_range(const std::string &content_range);
void check_socket();
void check_preapproved_server_certificate(X509 *cert, bool &cert_preapproved,
bool &cert_name_preapproved) const;
bool validate_server_name(X509 *cert);
static bool match_cert_name(const std::string &cert_name, const std::string &hostname);
static std::string get_x509_name_component(X509_NAME *name, int nid);
void make_header();
void make_proxy_request_text();
void make_request_text();
void reset_url(const URLSpec &old_url, const URLSpec &new_url);
void store_header_field(const std::string &field_name, const std::string &field_value);
#ifndef NDEBUG
static void show_send(const std::string &message);
#endif
void reset_download_to();
void close_download_stream();
void reset_to_new();
void reset_body_stream();
void close_connection();
static bool more_useful_status_code(int a, int b);
public:
// This is declared public solely so we can make an ostream operator for it.
enum State {
S_new,
S_try_next_proxy,
S_connecting,
S_connecting_wait,
S_http_proxy_ready,
S_http_proxy_request_sent,
S_http_proxy_reading_header,
S_socks_proxy_greet,
S_socks_proxy_greet_reply,
S_socks_proxy_connect,
S_socks_proxy_connect_reply,
S_setup_ssl,
S_ssl_handshake,
S_ready,
S_request_sent,
S_reading_header,
S_start_direct_file_read,
S_read_header,
S_begin_body,
S_reading_body,
S_read_body,
S_read_trailer,
S_failure
};
private:
class StatusEntry {
public:
INLINE StatusEntry();
int _status_code;
std::string _status_string;
};
typedef pvector<URLSpec> Proxies;
typedef pvector<StatusEntry> StatusList;
HTTPClient *_client;
Proxies _proxies;
size_t _proxy_next_index;
StatusList _status_list;
URLSpec _proxy;
PT(BioPtr) _bio;
PT(BioStreamPtr) _source;
bool _persistent_connection;
bool _allow_proxy;
bool _proxy_tunnel;
double _connect_timeout;
double _http_timeout;
size_t _skip_body_size;
double _idle_timeout;
bool _blocking_connect;
bool _download_throttle;
double _max_bytes_per_second;
double _max_updates_per_second;
double _seconds_per_update;
int _bytes_per_update;
bool _nonblocking;
bool _wanted_nonblocking;
std::string _send_extra_headers;
DocumentSpec _document_spec;
DocumentSpec _request;
HTTPEnum::Method _method;
std::string request_path;
std::string _header;
std::string _body;
std::string _content_type;
bool _want_ssl;
bool _proxy_serves_document;
bool _proxy_tunnel_now;
bool _server_response_has_no_body;
size_t _first_byte_requested;
size_t _last_byte_requested;
size_t _first_byte_delivered;
size_t _last_byte_delivered;
int _connect_count;
enum DownloadDest {
DD_none,
DD_file,
DD_ram,
DD_stream,
};
DownloadDest _download_dest;
bool _subdocument_resumes;
Filename _download_to_filename;
Ramfile *_download_to_ramfile;
std::ostream *_download_to_stream;
int _read_index;
HTTPEnum::HTTPVersion _http_version;
std::string _http_version_string;
StatusEntry _status_entry;
URLSpec _redirect;
std::string _proxy_realm;
std::string _proxy_username;
PT(HTTPAuthorization) _proxy_auth;
std::string _www_realm;
std::string _www_username;
PT(HTTPAuthorization) _www_auth;
// What type of response do we get to our HTTP request?
enum ResponseType {
RT_none,
RT_hangup, // immediately lost connection
RT_non_http, // something that wasn't an expected HTTP response
RT_http_hangup, // the start of an HTTP response, then a lost connection
RT_http_complete // a valid HTTP response completed
};
ResponseType _response_type;
// Not a phash_map, to maintain sorted order.
typedef pmap<std::string, std::string> Headers;
Headers _headers;
size_t _expected_file_size;
size_t _file_size;
size_t _transfer_file_size;
size_t _bytes_downloaded;
size_t _bytes_requested;
bool _got_expected_file_size;
bool _got_file_size;
bool _got_transfer_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 IO we have to
// be able to return to the caller after any IO operation and resume later
// where we left off.
State _state;
State _done_state;
double _started_connecting_time;
double _sent_request_time;
bool _started_download;
std::string _proxy_header;
std::string _proxy_request_text;
std::string _request_text;
std::string _working_get;
size_t _sent_so_far;
std::string _current_field_name;
std::string _current_field_value;
ISocketStream *_body_stream;
bool _owns_body_stream;
BIO *_sbio;
std::string _cipher_list;
pvector<URLSpec> _redirect_trail;
int _last_status_code;
double _last_run_time;
// RAU we find that we may need a little more time for the ssl handshake
// when the phase files are downloading
double _extra_ssl_handshake_time;
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedReferenceCount::init_type();
register_type(_type_handle, "HTTPChannel",
TypedReferenceCount::get_class_type());
}
private:
static TypeHandle _type_handle;
friend class ChunkedStreamBuf;
friend class IdentityStreamBuf;
friend class HTTPClient;
};
std::ostream &operator << (std::ostream &out, HTTPChannel::State state);
#include "httpChannel.I"
#endif // HAVE_OPENSSL
#endif