32 #if defined(WIN32_VC) || defined(WIN64_VC) 41 using std::ostringstream;
46 #define _NOTIFY_HTTP_CHANNEL_ID "[" << this << "] " 52 HTTPChannel(HTTPClient *client) :
55 if (downloader_cat.is_debug()) {
56 downloader_cat.debug()
57 << _NOTIFY_HTTP_CHANNEL_ID
62 (
"extra-ssl-handshake-time", 0.0,
63 PRC_DESC(
"This specifies how much extra time to try to establish" 64 "the ssl handshake before we bail."));
65 _extra_ssl_handshake_time = extra_ssl_handshake_time;
66 _proxy_next_index = 0;
67 _persistent_connection =
false;
69 _proxy_tunnel = http_proxy_tunnel;
70 _connect_timeout = http_connect_timeout;
71 _http_timeout = http_timeout;
72 _skip_body_size = http_skip_body_size;
73 _idle_timeout = http_idle_timeout;
74 _blocking_connect =
false;
75 _download_throttle = download_throttle;
76 _max_bytes_per_second = downloader_byte_rate;
77 _seconds_per_update = downloader_frequency;
78 _max_updates_per_second = 1.0f / _seconds_per_update;
79 _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update);
87 _wanted_nonblocking =
false;
90 _proxy_serves_document =
false;
91 _proxy_tunnel_now =
false;
92 _first_byte_requested = 0;
93 _last_byte_requested = 0;
94 _first_byte_delivered = 0;
95 _last_byte_delivered = 0;
97 _expected_file_size = 0;
99 _transfer_file_size = 0;
100 _got_expected_file_size =
false;
101 _got_file_size =
false;
102 _got_transfer_file_size =
false;
103 _bytes_downloaded = 0;
104 _bytes_requested = 0;
105 _status_entry = StatusEntry();
106 _response_type = RT_none;
107 _http_version = _client->get_http_version();
108 _http_version_string = _client->get_http_version_string();
109 _content_type =
"application/x-www-form-urlencoded";
112 _started_download =
false;
114 _body_stream =
nullptr;
115 _owns_body_stream =
false;
117 _cipher_list = _client->get_cipher_list();
118 _last_status_code = 0;
119 _last_run_time = 0.0f;
120 _download_to_ramfile =
nullptr;
121 _download_to_stream =
nullptr;
129 if (downloader_cat.is_debug()) {
130 downloader_cat.debug()
131 << _NOTIFY_HTTP_CHANNEL_ID
144 get_status_string()
const {
145 switch (_status_entry._status_code) {
147 return "Connection in progress";
149 case SC_internal_error:
150 return "Internal error";
152 case SC_no_connection:
153 return "No connection";
156 return "Timeout on connection";
158 case SC_lost_connection:
159 return "Lost connection";
161 case SC_non_http_response:
162 return "Non-HTTP response";
164 case SC_invalid_http:
165 return "Could not understand HTTP response";
167 case SC_socks_invalid_version:
168 return "Unsupported SOCKS version";
170 case SC_socks_no_acceptable_login_method:
171 return "No acceptable SOCKS login method";
173 case SC_socks_refused:
174 return "SOCKS proxy refused connection";
176 case SC_socks_no_connection:
177 return "SOCKS proxy unable to connect";
179 case SC_ssl_internal_failure:
180 return "SSL internal failure";
182 case SC_ssl_no_handshake:
183 return "No SSL handshake";
185 case SC_http_error_watermark:
187 return "Internal error";
189 case SC_ssl_invalid_server_certificate:
190 return "SSL invalid server certificate";
192 case SC_ssl_unexpected_server:
193 return "Unexpected SSL server";
195 case SC_download_open_error:
196 return "Error opening file";
198 case SC_download_write_error:
199 return "Error writing to disk";
201 case SC_download_invalid_range:
202 return "Invalid subrange requested";
205 return _status_entry._status_string;
213 get_header_value(
const string &key)
const {
214 Headers::const_iterator hi = _headers.find(
downcase(key));
215 if (hi != _headers.end()) {
227 will_close_connection()
const {
228 if (get_http_version() < HTTPEnum::HV_11) {
233 string connection = get_header_value(
"Connection");
234 if (
downcase(connection) ==
"close") {
239 if (connection.empty() && !get_persistent_connection()) {
259 std::streamsize HTTPChannel::
260 get_file_size()
const {
261 if (_got_file_size) {
263 }
else if (_got_transfer_file_size) {
264 return _transfer_file_size;
265 }
else if (_got_expected_file_size) {
266 return _expected_file_size;
277 write_headers(ostream &out)
const {
278 Headers::const_iterator hi;
279 for (hi = _headers.begin(); hi != _headers.end(); ++hi) {
280 out << (*hi).first <<
": " << (*hi).second <<
"\n";
296 if (downloader_cat.is_spam()) {
297 downloader_cat.spam()
298 << _NOTIFY_HTTP_CHANNEL_ID
302 if (_state == _done_state || _state == S_failure) {
303 clear_extra_headers();
304 if (!reached_done_state()) {
309 if (_started_download) {
310 if (_wanted_nonblocking && _download_throttle) {
312 double elapsed = now - _last_run_time;
313 if (elapsed < _seconds_per_update) {
318 int num_potential_updates = (int)(elapsed / _seconds_per_update);
319 _last_run_time = now;
320 _bytes_requested += _bytes_per_update * num_potential_updates;
321 if (downloader_cat.is_spam()) {
322 downloader_cat.spam()
323 << _NOTIFY_HTTP_CHANNEL_ID
324 <<
"elapsed = " << elapsed <<
" num_potential_updates = " 325 << num_potential_updates <<
" bytes_requested = " 326 << _bytes_requested <<
"\n";
330 bool repeat_later =
false;
331 switch (_download_dest) {
337 repeat_later = run_download_to_file();
341 repeat_later = run_download_to_ram();
345 repeat_later = run_download_to_stream();
363 if (_state == _done_state) {
364 return reached_done_state();
372 if (_bio.is_null() && _state != S_try_next_proxy) {
373 if (_connect_count > http_max_connect_count) {
378 downloader_cat.warning()
379 << _NOTIFY_HTTP_CHANNEL_ID
380 <<
"Too many lost connections, giving up.\n";
381 _status_entry._status_code = SC_lost_connection;
388 if (_proxy.empty()) {
393 _bio =
new BioPtr(url);
394 _source =
new BioStreamPtr(
new BioStream(_bio));
396 _bio->set_nbio(
true);
399 if (downloader_cat.is_debug()) {
400 if (_connect_count > 0) {
401 downloader_cat.debug()
402 << _NOTIFY_HTTP_CHANNEL_ID
403 <<
"Reconnecting to " << _bio->get_server_name() <<
" port " 404 << _bio->get_port() <<
"\n";
406 downloader_cat.debug()
407 << _NOTIFY_HTTP_CHANNEL_ID
408 <<
"Connecting to " << _bio->get_server_name() <<
" port " 409 << _bio->get_port() <<
"\n";
413 _state = S_connecting;
414 _started_connecting_time =
428 case S_try_next_proxy:
429 repeat_later = run_try_next_proxy();
433 repeat_later = run_connecting();
436 case S_connecting_wait:
437 repeat_later = run_connecting_wait();
440 case S_http_proxy_ready:
441 repeat_later = run_http_proxy_ready();
444 case S_http_proxy_request_sent:
445 repeat_later = run_http_proxy_request_sent();
448 case S_http_proxy_reading_header:
449 repeat_later = run_http_proxy_reading_header();
452 case S_socks_proxy_greet:
453 repeat_later = run_socks_proxy_greet();
456 case S_socks_proxy_greet_reply:
457 repeat_later = run_socks_proxy_greet_reply();
460 case S_socks_proxy_connect:
461 repeat_later = run_socks_proxy_connect();
464 case S_socks_proxy_connect_reply:
465 repeat_later = run_socks_proxy_connect_reply();
469 repeat_later = run_setup_ssl();
472 case S_ssl_handshake:
473 repeat_later = run_ssl_handshake();
477 repeat_later = run_ready();
481 repeat_later = run_request_sent();
484 case S_reading_header:
485 repeat_later = run_reading_header();
488 case S_start_direct_file_read:
489 repeat_later = run_start_direct_file_read();
493 repeat_later = run_read_header();
497 repeat_later = run_begin_body();
501 repeat_later = run_reading_body();
505 repeat_later = run_read_body();
509 repeat_later = run_read_trailer();
513 downloader_cat.warning()
514 << _NOTIFY_HTTP_CHANNEL_ID
515 <<
"Unhandled state " << _state <<
"\n";
519 if (_state == _done_state || _state == S_failure) {
520 clear_extra_headers();
522 return reached_done_state();
524 thread_consider_yield();
525 }
while (!repeat_later || _bio.is_null());
553 ISocketStream *HTTPChannel::
557 if ((_state != S_read_header && _state != S_begin_body) || _source.is_null()) {
561 string transfer_coding =
downcase(get_header_value(
"Transfer-Encoding"));
563 ISocketStream *result;
564 if (transfer_coding ==
"chunked") {
568 _state = S_reading_body;
570 result =
new IChunkedStream(_source,
this);
577 _state = S_reading_body;
579 result =
new IIdentityStream(_source,
this, _got_file_size, _file_size);
582 result->_channel =
this;
583 _body_stream = result;
584 _owns_body_stream =
false;
596 close_read_body(istream *stream)
const {
597 if (stream !=
nullptr) {
602 #if !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 604 (*global_operator_delete)(stream);
636 download_to_file(
const Filename &filename,
bool subdocument_resumes) {
638 _download_to_filename = filename;
640 _subdocument_resumes = subdocument_resumes;
642 _download_dest = DD_file;
644 if (_wanted_nonblocking && _state != S_read_header) {
651 if (!open_download_file()) {
658 return is_download_complete() && is_valid();
685 download_to_ram(
Ramfile *ramfile,
bool subdocument_resumes) {
686 nassertr(ramfile !=
nullptr,
false);
689 _download_to_ramfile = ramfile;
690 _download_dest = DD_ram;
691 _subdocument_resumes = (subdocument_resumes && _first_byte_delivered != 0);
693 if (_wanted_nonblocking && _state != S_read_header) {
700 if (!open_download_file()) {
707 return is_download_complete() && is_valid();
735 download_to_stream(ostream *strm,
bool subdocument_resumes) {
737 _download_to_stream = strm;
738 _download_to_stream->clear();
739 _subdocument_resumes = subdocument_resumes;
741 _download_dest = DD_stream;
743 if (_wanted_nonblocking && _state != S_read_header) {
750 if (!open_download_file()) {
757 return is_download_complete() && is_valid();
768 SocketStream *HTTPChannel::
770 if (!is_connection_ready()) {
774 BioStream *stream = _source->get_stream();
775 _source->set_stream(
nullptr);
778 if (downloader_cat.is_debug()) {
779 downloader_cat.debug()
780 << _NOTIFY_HTTP_CHANNEL_ID
781 <<
"passing ownership of connection to caller.\n";
794 result.reserve(s.size());
795 string::const_iterator p;
796 for (p = s.begin(); p != s.end(); ++p) {
797 result += tolower(*p);
806 body_stream_destructs(ISocketStream *stream) {
807 if (stream == _body_stream) {
808 if (_state == S_reading_body) {
809 switch (_body_stream->get_read_state()) {
810 case ISocketStream::RS_complete:
811 finished_body(
false);
814 case ISocketStream::RS_error:
815 _state = HTTPChannel::S_failure;
816 _status_entry._status_code = HTTPChannel::SC_lost_connection;
823 _body_stream =
nullptr;
824 _owns_body_stream =
false;
834 reached_done_state() {
844 if (_state == S_failure) {
848 if (!_status_list.empty()) {
849 _status_list.push_back(_status_entry);
850 if (downloader_cat.is_debug()) {
851 downloader_cat.debug()
852 << _NOTIFY_HTTP_CHANNEL_ID
853 <<
"Reexamining failure responses.\n";
856 if (downloader_cat.is_debug()) {
857 downloader_cat.debug()
858 << _NOTIFY_HTTP_CHANNEL_ID
859 <<
" " << 0 <<
". " << _status_list[0]._status_code <<
" " 860 << _status_list[0]._status_string <<
"\n";
862 for (
size_t i = 1; i < _status_list.size(); i++) {
863 if (downloader_cat.is_debug()) {
864 downloader_cat.debug()
865 << _NOTIFY_HTTP_CHANNEL_ID
866 <<
" " << i <<
". " << _status_list[i]._status_code <<
" " 867 << _status_list[i]._status_string <<
"\n";
869 if (more_useful_status_code(_status_list[i]._status_code,
870 _status_list[best_i]._status_code)) {
874 if (downloader_cat.is_debug()) {
875 downloader_cat.debug()
876 << _NOTIFY_HTTP_CHANNEL_ID
877 <<
"chose index " << best_i <<
", above.\n";
879 _status_entry = _status_list[best_i];
880 _status_list.clear();
887 _status_list.clear();
889 if (_download_dest == DD_none) {
896 if (_body_stream ==
nullptr) {
897 if (downloader_cat.is_debug()) {
898 downloader_cat.debug()
899 << _NOTIFY_HTTP_CHANNEL_ID
900 <<
"Unable to download body: " << _request.get_url() <<
"\n";
905 _owns_body_stream =
true;
906 if (_state != S_reading_body) {
909 _started_download =
true;
911 _done_state = S_read_trailer;
924 run_try_next_proxy() {
925 if (_proxy_next_index < _proxies.size()) {
928 _status_list.push_back(_status_entry);
929 _status_entry = StatusEntry();
932 _proxy = _proxies[_proxy_next_index];
933 _proxy_auth =
nullptr;
937 _state = S_connecting;
953 _status_entry = StatusEntry();
955 if (!_bio->connect()) {
956 if (_bio->should_retry()) {
957 _state = S_connecting_wait;
960 downloader_cat.info()
961 << _NOTIFY_HTTP_CHANNEL_ID
962 <<
"Could not connect to " << _bio->get_server_name() <<
" port " 963 << _bio->get_port() <<
"\n";
964 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
965 _status_entry._status_code = SC_no_connection;
966 _state = S_try_next_proxy;
970 if (downloader_cat.is_debug()) {
971 downloader_cat.debug()
972 << _NOTIFY_HTTP_CHANNEL_ID
973 <<
"Connected to " << _bio->get_server_name() <<
" port " 974 << _bio->get_port() <<
"\n";
977 if (_proxy_tunnel_now) {
978 if (_proxy.get_scheme() ==
"socks") {
979 _state = S_socks_proxy_greet;
981 _state = S_http_proxy_ready;
986 _state = S_setup_ssl;
1000 run_connecting_wait() {
1002 BIO_get_fd(*_bio, &fd);
1004 downloader_cat.warning()
1005 << _NOTIFY_HTTP_CHANNEL_ID
1006 <<
"nonblocking socket BIO has no file descriptor.\n";
1008 _status_entry._status_code = SC_internal_error;
1009 _state = S_try_next_proxy;
1013 if (downloader_cat.is_spam()) {
1014 downloader_cat.spam()
1015 << _NOTIFY_HTTP_CHANNEL_ID
1016 <<
"waiting to connect to " << _request.get_url().get_server_and_port() <<
".\n";
1022 if (get_blocking_connect()) {
1025 tv.tv_sec = (int)_connect_timeout;
1026 tv.tv_usec = (int)((_connect_timeout - tv.tv_sec) * 1000000.0);
1032 int errcode = select(fd + 1,
nullptr, &wset,
nullptr, &tv);
1034 downloader_cat.warning()
1035 << _NOTIFY_HTTP_CHANNEL_ID
1036 <<
"Error in select.\n";
1038 _status_entry._status_code = SC_internal_error;
1039 _state = S_try_next_proxy;
1045 if (get_blocking_connect() ||
1047 _started_connecting_time > get_connect_timeout())) {
1049 downloader_cat.info()
1050 << _NOTIFY_HTTP_CHANNEL_ID
1051 <<
"Timeout connecting to " 1052 << _request.get_url().get_server_and_port()
1053 <<
" for " << _request.get_url()
1055 _status_entry._status_code = SC_timeout;
1056 _state = S_try_next_proxy;
1063 _state = S_connecting;
1074 run_http_proxy_ready() {
1076 nassertr(!_proxy_request_text.empty(),
false);
1077 if (!server_send(_proxy_request_text,
false)) {
1082 _state = S_http_proxy_request_sent;
1094 run_http_proxy_request_sent() {
1097 if (!server_getline_failsafe(line)) {
1102 while (line.empty()) {
1103 if (!server_getline_failsafe(line)) {
1108 if (!parse_http_response(line)) {
1112 _state = S_http_proxy_reading_header;
1113 _current_field_name = string();
1114 _current_field_value = string();
1116 _got_file_size =
false;
1117 _got_transfer_file_size =
false;
1126 run_http_proxy_reading_header() {
1127 if (parse_http_header()) {
1131 _redirect = get_header_value(
"Location");
1135 _server_response_has_no_body =
1136 (get_status_code() / 100 == 1 ||
1137 get_status_code() == 204 ||
1138 get_status_code() == 304);
1140 int last_status = _last_status_code;
1141 _last_status_code = get_status_code();
1143 if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
1145 string authenticate_request = get_header_value(
"Proxy-Authenticate");
1146 _proxy_auth = _client->generate_auth(_proxy,
true, authenticate_request);
1147 if (_proxy_auth !=
nullptr) {
1148 _proxy_realm = _proxy_auth->get_realm();
1149 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
1150 if (!_proxy_username.empty()) {
1151 make_proxy_request_text();
1154 _state = S_begin_body;
1166 if (get_status_code() != 407) {
1167 _status_entry._status_code += 1000;
1170 _state = S_try_next_proxy;
1175 make_request_text();
1178 _state = S_setup_ssl;
1191 run_socks_proxy_greet() {
1192 static const char socks_greeting[] = {
1201 static const int socks_greeting_len =
sizeof(socks_greeting);
1202 if (!server_send(
string(socks_greeting, socks_greeting_len),
true)) {
1208 _state = S_socks_proxy_greet_reply;
1216 run_socks_proxy_greet_reply() {
1220 if (!server_get_failsafe(reply, 2)) {
1224 if (reply[0] != 0x05) {
1226 downloader_cat.info()
1227 << _NOTIFY_HTTP_CHANNEL_ID
1228 <<
"Rejecting Socks version " << (int)reply[0] <<
"\n";
1229 _status_entry._status_code = SC_socks_invalid_version;
1230 _state = S_try_next_proxy;
1234 if (reply[1] == (
char)0xff) {
1235 downloader_cat.info()
1236 << _NOTIFY_HTTP_CHANNEL_ID
1237 <<
"Socks server does not accept our available login methods.\n";
1238 _status_entry._status_code = SC_socks_no_acceptable_login_method;
1239 _state = S_try_next_proxy;
1243 if (reply[1] == 0x00) {
1245 _state = S_socks_proxy_connect;
1250 downloader_cat.info()
1251 << _NOTIFY_HTTP_CHANNEL_ID
1252 <<
"Socks server accepted unrequested login method " 1253 << (int)reply[1] <<
"\n";
1254 _status_entry._status_code = SC_socks_no_acceptable_login_method;
1255 _state = S_try_next_proxy;
1263 run_socks_proxy_connect() {
1264 static const char socks_connect[] = {
1270 static const int socks_connect_len =
sizeof(socks_connect);
1272 string hostname = _request.get_url().get_server();
1273 int port = _request.get_url().get_port();
1275 if (downloader_cat.is_debug()) {
1276 downloader_cat.debug()
1277 << _NOTIFY_HTTP_CHANNEL_ID
1278 <<
"Requesting SOCKS5 connection to " 1279 << _request.get_url().get_server_and_port() <<
"\n";
1283 string(socks_connect, socks_connect_len) +
1284 string(1, (
char)hostname.length()) +
1286 string(1, (
char)((port >> 8) & 0xff)) +
1287 string(1, (
char)(port & 0xff));
1289 if (!server_send(connect,
true)) {
1294 _state = S_socks_proxy_connect_reply;
1302 run_socks_proxy_connect_reply() {
1306 if (!server_get_failsafe(reply, 2)) {
1310 if (reply[0] != 0x05) {
1312 downloader_cat.info()
1313 << _NOTIFY_HTTP_CHANNEL_ID
1314 <<
"Rejecting Socks version " << (int)reply[0] <<
"\n";
1316 _status_entry._status_code = SC_socks_invalid_version;
1317 _state = S_try_next_proxy;
1321 if (reply[1] != 0x00) {
1322 downloader_cat.info()
1323 << _NOTIFY_HTTP_CHANNEL_ID
1324 <<
"Connection refused, SOCKS code " << (int)reply[1] <<
"\n";
1346 _status_entry._status_code = SC_socks_no_connection;
1350 _status_entry._status_code = SC_socks_refused;
1354 _state = S_try_next_proxy;
1359 _working_get = reply;
1360 if (!server_get_failsafe(reply, 5)) {
1365 int total_bytes = 6;
1373 total_bytes += (
unsigned int)reply[4];
1381 downloader_cat.info()
1382 << _NOTIFY_HTTP_CHANNEL_ID
1383 <<
"Unsupported SOCKS address type: " << (int)reply[3] <<
"\n";
1384 _status_entry._status_code = SC_socks_invalid_version;
1385 _state = S_try_next_proxy;
1390 _working_get = reply;
1391 if (!server_get_failsafe(reply, total_bytes)) {
1395 if (downloader_cat.is_debug()) {
1397 string connect_host;
1403 strm << (
unsigned int)(
unsigned char)reply[4] <<
"." 1404 << (
unsigned int)(
unsigned char)reply[5] <<
"." 1405 << (
unsigned int)(
unsigned char)reply[6] <<
"." 1406 << (
unsigned int)(
unsigned char)reply[7];
1407 connect_host = strm.str();
1412 connect_host = string(&reply[5], (
unsigned int)reply[4]);
1418 sprintf(buf,
"[%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx" 1419 ":%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx]",
1420 reply[4], reply[5], reply[6], reply[7], reply[8], reply[9],
1421 reply[10], reply[11], reply[12], reply[13], reply[14],
1422 reply[15], reply[16], reply[17], reply[18], reply[19]);
1429 (((
unsigned int)(
unsigned char)reply[total_bytes - 2]) << 8) |
1430 ((
unsigned int)(
unsigned char)reply[total_bytes - 1]);
1432 downloader_cat.debug()
1433 << _NOTIFY_HTTP_CHANNEL_ID
1434 << _proxy <<
" directed us to " << connect_host <<
":" 1435 << connect_port <<
"\n";
1439 _state = S_setup_ssl;
1453 _sbio = BIO_new_ssl(_client->get_ssl_ctx(),
true);
1454 BIO_push(_sbio, *_bio);
1457 BIO_get_ssl(_sbio, &ssl);
1458 nassertr(ssl !=
nullptr,
false);
1462 string cipher_list = _cipher_list;
1463 if (!cipher_list.empty()) {
1464 size_t space = cipher_list.find(
" ");
1465 if (space != string::npos) {
1466 cipher_list = cipher_list.substr(0, space);
1470 if (downloader_cat.is_debug()) {
1471 downloader_cat.debug()
1472 << _NOTIFY_HTTP_CHANNEL_ID
1473 <<
"Setting ssl-cipher-list '" << cipher_list <<
"'\n";
1475 int result = SSL_set_cipher_list(ssl, cipher_list.c_str());
1477 downloader_cat.error()
1478 << _NOTIFY_HTTP_CHANNEL_ID
1479 <<
"Invalid cipher list: '" << cipher_list <<
"'\n";
1480 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
1481 _status_entry._status_code = SC_ssl_internal_failure;
1486 string hostname = _request.get_url().get_server();
1487 result = SSL_set_tlsext_host_name(ssl, hostname.c_str());
1489 downloader_cat.error()
1490 << _NOTIFY_HTTP_CHANNEL_ID
1491 <<
"Could not set TLS SNI hostname to '" << hostname <<
"'\n";
1503 if (_client->load_client_certificate()) {
1504 SSL_use_certificate(ssl, _client->_client_certificate_pub);
1505 SSL_use_PrivateKey(ssl, _client->_client_certificate_priv);
1506 if (!SSL_check_private_key(ssl)) {
1507 downloader_cat.warning()
1508 << _NOTIFY_HTTP_CHANNEL_ID
1509 <<
"Client private key does not match public key!\n";
1513 if (downloader_cat.is_spam()) {
1514 downloader_cat.spam()
1515 << _NOTIFY_HTTP_CHANNEL_ID
1516 <<
"SSL Ciphers available:\n";
1519 name = SSL_get_cipher_list(ssl, pri);
1520 while (name !=
nullptr) {
1521 downloader_cat.spam()
1522 << _NOTIFY_HTTP_CHANNEL_ID
1523 <<
" " << pri + 1 <<
". " << name <<
"\n";
1525 name = SSL_get_cipher_list(ssl, pri);
1529 if (downloader_cat.is_debug()) {
1530 downloader_cat.debug()
1531 << _NOTIFY_HTTP_CHANNEL_ID
1532 <<
"performing SSL handshake\n";
1534 _state = S_ssl_handshake;
1537 _started_connecting_time =
1548 run_ssl_handshake() {
1549 if (BIO_do_handshake(_sbio) <= 0) {
1550 if (BIO_should_retry(_sbio)) {
1553 _started_connecting_time;
1554 if (elapsed <= get_connect_timeout() + _extra_ssl_handshake_time) {
1561 downloader_cat.info()
1562 << _NOTIFY_HTTP_CHANNEL_ID
1563 <<
"Could not establish SSL handshake with " 1564 << _request.get_url().get_server_and_port() <<
"\n";
1565 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
1570 if (!_cipher_list.empty()) {
1572 size_t space = _cipher_list.find(
" ");
1573 if (space != string::npos) {
1574 while (space < _cipher_list.length() && _cipher_list[space] ==
' ') {
1577 _cipher_list = _cipher_list.substr(space);
1578 if (!_cipher_list.empty()) {
1581 _state = S_connecting;
1588 _cipher_list = _client->get_cipher_list();
1589 _status_entry._status_code = SC_ssl_no_handshake;
1595 BIO_get_ssl(_sbio, &ssl);
1596 nassertr(ssl !=
nullptr,
false);
1598 if (!_nonblocking) {
1599 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
1602 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1603 if (cipher ==
nullptr) {
1604 downloader_cat.warning()
1605 << _NOTIFY_HTTP_CHANNEL_ID
1606 <<
"No current cipher on SSL connection.\n";
1608 if (downloader_cat.is_debug()) {
1609 downloader_cat.debug()
1610 << _NOTIFY_HTTP_CHANNEL_ID
1611 <<
"Using cipher " << SSL_CIPHER_get_name((SSL_CIPHER *) cipher) <<
"\n";
1617 _bio->set_bio(_sbio);
1620 X509 *cert = SSL_get_peer_certificate(ssl);
1621 if (cert ==
nullptr) {
1622 downloader_cat.info()
1623 << _NOTIFY_HTTP_CHANNEL_ID
1624 <<
"No certificate was presented by server.\n";
1627 _status_entry._status_code = SC_ssl_invalid_server_certificate;
1632 X509_NAME *subject = X509_get_subject_name(cert);
1633 if (downloader_cat.is_debug()) {
1634 string org_name = get_x509_name_component(subject, NID_organizationName);
1635 string org_unit_name = get_x509_name_component(subject, NID_organizationalUnitName);
1636 string common_name = get_x509_name_component(subject, NID_commonName);
1638 downloader_cat.debug()
1639 << _NOTIFY_HTTP_CHANNEL_ID
1640 <<
"Server is " << common_name <<
" from " << org_unit_name
1641 <<
" / " << org_name <<
"\n";
1643 if (downloader_cat.is_spam()) {
1644 downloader_cat.spam()
1645 << _NOTIFY_HTTP_CHANNEL_ID
1646 <<
"Received certificate from server:\n" << std::flush;
1647 X509_print_fp(stderr, cert);
1652 bool cert_preapproved =
false;
1653 bool cert_name_preapproved =
false;
1654 check_preapproved_server_certificate(cert, cert_preapproved, cert_name_preapproved);
1657 long verify_result = SSL_get_verify_result(ssl);
1658 bool cert_valid =
true;
1660 if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) {
1661 downloader_cat.info()
1662 << _NOTIFY_HTTP_CHANNEL_ID
1663 <<
"Expired certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1664 if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
1668 }
else if (verify_result == X509_V_ERR_CERT_NOT_YET_VALID) {
1669 downloader_cat.info()
1670 << _NOTIFY_HTTP_CHANNEL_ID
1671 <<
"Premature certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1672 if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
1676 }
else if (verify_result == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
1677 verify_result == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
1678 downloader_cat.info()
1679 << _NOTIFY_HTTP_CHANNEL_ID
1680 <<
"Self-signed certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1681 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
1685 }
else if (verify_result != X509_V_OK) {
1686 downloader_cat.info()
1687 << _NOTIFY_HTTP_CHANNEL_ID
1688 <<
"Unable to verify identity of " << _request.get_url().get_server_and_port()
1689 <<
", verify error code " << verify_result <<
"\n";
1690 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
1696 _status_entry._status_code = SC_ssl_invalid_server_certificate;
1701 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_name_preapproved) {
1703 if (!validate_server_name(cert)) {
1704 _status_entry._status_code = SC_ssl_unexpected_server;
1727 if (!_request_text.empty()) {
1728 if (!server_send(_request_text,
false)) {
1734 _state = S_request_sent;
1744 run_request_sent() {
1747 if (!server_getline_failsafe(line)) {
1752 while (line.empty()) {
1753 if (!server_getline_failsafe(line)) {
1758 if (!parse_http_response(line)) {
1763 _state = S_reading_header;
1764 _current_field_name = string();
1765 _current_field_value = string();
1767 _got_file_size =
false;
1768 _got_transfer_file_size =
false;
1778 run_reading_header() {
1779 if (parse_http_header()) {
1780 if (_bio.is_null()) {
1781 downloader_cat.info()
1782 << _NOTIFY_HTTP_CHANNEL_ID
1783 <<
"Connection lost while reading HTTP response.\n";
1784 if (_response_type == RT_http_hangup) {
1786 _status_entry._status_code = SC_lost_connection;
1787 _state = S_try_next_proxy;
1791 _response_type = RT_http_hangup;
1798 if (elapsed > get_http_timeout()) {
1800 downloader_cat.info()
1801 << _NOTIFY_HTTP_CHANNEL_ID
1802 <<
"Timeout waiting for " 1803 << _request.get_url().get_server_and_port()
1804 <<
" in run_reading_header (" << elapsed
1805 <<
" seconds elapsed).\n";
1806 _status_entry._status_code = SC_timeout;
1807 _state = S_try_next_proxy;
1812 _response_type = RT_http_complete;
1816 clear_extra_headers();
1818 _server_response_has_no_body =
1819 (get_status_code() / 100 == 1 ||
1820 get_status_code() == 204 ||
1821 get_status_code() == 304 ||
1822 _method == HTTPEnum::M_head);
1825 if (get_status_code() == 206) {
1826 string content_range = get_header_value(
"Content-Range");
1827 if (content_range.empty()) {
1828 downloader_cat.warning()
1829 << _NOTIFY_HTTP_CHANNEL_ID
1830 <<
"Got 206 response without Content-Range header!\n";
1831 _status_entry._status_code = SC_invalid_http;
1836 if (!parse_content_range(content_range)) {
1837 downloader_cat.warning()
1838 << _NOTIFY_HTTP_CHANNEL_ID
1839 <<
"Couldn't parse Content-Range: " << content_range <<
"\n";
1840 _status_entry._status_code = SC_invalid_http;
1847 _first_byte_delivered = 0;
1848 _last_byte_delivered = 0;
1850 if (downloader_cat.is_debug()) {
1851 if (_first_byte_requested != 0 || _last_byte_requested != 0 ||
1852 _first_byte_delivered != 0 || _last_byte_delivered != 0) {
1853 downloader_cat.debug()
1854 << _NOTIFY_HTTP_CHANNEL_ID
1855 <<
"Requested byte range " << _first_byte_requested
1856 <<
" to " << _last_byte_delivered
1857 <<
"; server delivers range " << _first_byte_delivered
1858 <<
" to " << _last_byte_delivered
1865 string tag = get_header_value(
"ETag");
1869 string date = get_header_value(
"Last-Modified");
1870 if (!date.empty()) {
1871 _document_spec.set_date(
HTTPDate(date));
1877 if (_server_response_has_no_body) {
1879 reset_download_to();
1882 if (!open_download_file()) {
1886 _got_expected_file_size =
false;
1887 _got_file_size =
false;
1888 _got_transfer_file_size =
false;
1890 string content_length = get_header_value(
"Content-Length");
1891 if (!content_length.empty()) {
1892 _file_size = atoi(content_length.c_str());
1893 _got_file_size =
true;
1895 }
else if (get_status_code() == 206) {
1898 _file_size = _last_byte_delivered - _first_byte_delivered + 1;
1899 _got_file_size =
true;
1901 _redirect = get_header_value(
"Location");
1906 if (_redirect.has_path() && !_redirect.has_authority()) {
1908 Filename path = _redirect.get_path();
1911 _redirect.set_path(
Filename(rel_to, path));
1917 _state = S_read_header;
1919 if (_server_response_has_no_body && will_close_connection()) {
1926 int last_status = _last_status_code;
1927 _last_status_code = get_status_code();
1929 if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
1931 string authenticate_request = get_header_value(
"Proxy-Authenticate");
1933 _client->generate_auth(_proxy,
true, authenticate_request);
1934 if (_proxy_auth !=
nullptr) {
1935 _proxy_realm = _proxy_auth->get_realm();
1936 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
1937 if (!_proxy_username.empty()) {
1938 make_request_text();
1941 _state = S_begin_body;
1947 if (get_status_code() == 401 && last_status != 401) {
1949 string authenticate_request = get_header_value(
"WWW-Authenticate");
1950 _www_auth = _client->generate_auth(_request.get_url(),
false, authenticate_request);
1951 if (_www_auth !=
nullptr) {
1952 _www_realm = _www_auth->get_realm();
1953 _www_username = _client->select_username(_request.get_url(),
false, _www_realm);
1954 if (!_www_username.empty()) {
1955 make_request_text();
1958 _state = S_begin_body;
1964 if ((get_status_code() == 300 ||
1965 get_status_code() == 301 ||
1966 get_status_code() == 302 ||
1967 get_status_code() == 303 ||
1968 get_status_code() == 307) && !get_redirect().empty()) {
1976 if (_method == HTTPEnum::M_post) {
1977 _method = HTTPEnum::M_get;
1981 if (_method == HTTPEnum::M_get || _method == HTTPEnum::M_head) {
1983 URLSpec new_url = get_redirect();
1984 if (find(_redirect_trail.begin(), _redirect_trail.end(),
1985 new_url) != _redirect_trail.end()) {
1986 downloader_cat.warning()
1987 << _NOTIFY_HTTP_CHANNEL_ID
1988 <<
"cycle detected in redirect to " << new_url <<
"\n";
1991 _redirect_trail.push_back(new_url);
1993 if (downloader_cat.is_debug()) {
1994 downloader_cat.debug()
1995 << _NOTIFY_HTTP_CHANNEL_ID
1996 <<
"following redirect to " << new_url <<
"\n";
1998 if (_request.get_url().has_username()) {
1999 new_url.
set_username(_request.get_url().get_username());
2001 reset_url(_request.get_url(), new_url);
2002 _request.set_url(new_url);
2003 _want_ssl = _request.get_url().is_ssl();
2006 make_request_text();
2009 _state = S_begin_body;
2015 if (_state == S_read_header &&
2016 ((get_status_code() / 100) == 4 || (get_status_code() / 100) == 5) &&
2017 _proxy_serves_document && _proxy_next_index < _proxies.size()) {
2024 _state = S_try_next_proxy;
2037 run_start_direct_file_read() {
2038 _state = S_read_header;
2039 if (!open_download_file()) {
2059 _state = S_begin_body;
2069 if (will_close_connection()) {
2072 if (downloader_cat.is_debug()) {
2073 downloader_cat.debug()
2074 << _NOTIFY_HTTP_CHANNEL_ID
2075 <<
"resetting to begin body; server would close anyway.\n";
2081 if (_server_response_has_no_body) {
2083 _state = S_read_trailer;
2085 }
else if (get_file_size() > (
int)_skip_body_size) {
2089 if (downloader_cat.is_debug()) {
2090 downloader_cat.debug()
2091 << _NOTIFY_HTTP_CHANNEL_ID
2092 <<
"Dropping connection rather than skipping past " 2093 << get_file_size() <<
" bytes.\n";
2099 if (_body_stream ==
nullptr) {
2100 if (downloader_cat.is_debug()) {
2101 downloader_cat.debug()
2102 << _NOTIFY_HTTP_CHANNEL_ID
2103 <<
"Unable to skip body.\n";
2108 _owns_body_stream =
true;
2109 if (_state != S_reading_body) {
2110 reset_body_stream();
2127 run_reading_body() {
2128 if (will_close_connection()) {
2131 if (downloader_cat.is_debug()) {
2132 downloader_cat.debug()
2133 << _NOTIFY_HTTP_CHANNEL_ID
2134 <<
"resetting to read body; server would close anyway.\n";
2141 if (_body_stream ==
nullptr || !_owns_body_stream) {
2143 if (downloader_cat.is_debug()) {
2144 downloader_cat.debug()
2145 << _NOTIFY_HTTP_CHANNEL_ID
2146 <<
"resetting, not in skip-body mode.\n";
2153 std::getline(*_body_stream, line);
2154 while (!_body_stream->fail() && !_body_stream->eof()) {
2155 if (downloader_cat.is_spam()) {
2156 downloader_cat.spam()
2157 << _NOTIFY_HTTP_CHANNEL_ID
2158 <<
"skip: " << line <<
"\n";
2160 std::getline(*_body_stream, line);
2163 if (!_body_stream->is_closed()) {
2168 reset_body_stream();
2171 nassertr(_state != S_reading_body,
false);
2186 if (will_close_connection()) {
2189 if (downloader_cat.is_debug()) {
2190 downloader_cat.debug()
2191 << _NOTIFY_HTTP_CHANNEL_ID
2192 <<
"resetting to read body; server would close anyway.\n";
2200 if (!server_getline(line)) {
2203 while (!line.empty()) {
2204 if (!server_getline(line)) {
2209 _state = S_read_trailer;
2218 run_read_trailer() {
2219 if (will_close_connection()) {
2222 if (downloader_cat.is_debug()) {
2223 downloader_cat.debug()
2224 << _NOTIFY_HTTP_CHANNEL_ID
2225 <<
"resetting to read trailer; server would close anyway.\n";
2240 run_download_to_file() {
2241 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2243 bool do_throttle = _wanted_nonblocking && _download_throttle;
2245 static const size_t buffer_size = 4096;
2246 char buffer[buffer_size];
2248 size_t remaining_this_pass = buffer_size;
2250 remaining_this_pass = _bytes_per_update;
2253 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2254 size_t count = _body_stream->gcount();
2255 while (count != 0) {
2256 _download_to_stream->write(buffer, count);
2257 _bytes_downloaded += count;
2259 nassertr(count <= remaining_this_pass,
false);
2260 remaining_this_pass -= count;
2261 if (remaining_this_pass == 0) {
2267 thread_consider_yield();
2268 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2269 count = _body_stream->gcount();
2272 if (_download_to_stream->fail()) {
2273 downloader_cat.warning()
2274 << _NOTIFY_HTTP_CHANNEL_ID
2275 <<
"Error writing to " << _download_to_filename <<
"\n";
2276 _status_entry._status_code = SC_download_write_error;
2278 reset_download_to();
2282 _download_to_stream->flush();
2284 if (_body_stream->is_closed()) {
2286 reset_body_stream();
2287 close_download_stream();
2288 _started_download =
false;
2301 run_download_to_ram() {
2302 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2303 nassertr(_download_to_ramfile !=
nullptr,
false);
2305 bool do_throttle = _wanted_nonblocking && _download_throttle;
2307 static const size_t buffer_size = 4096;
2308 char buffer[buffer_size];
2310 size_t remaining_this_pass = buffer_size;
2312 remaining_this_pass = _bytes_per_update;
2315 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2316 size_t count = _body_stream->gcount();
2317 while (count != 0) {
2318 _download_to_ramfile->_data += string(buffer, count);
2319 _bytes_downloaded += count;
2321 nassertr(count <= remaining_this_pass,
false);
2322 remaining_this_pass -= count;
2323 if (remaining_this_pass == 0) {
2329 thread_consider_yield();
2330 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2331 count = _body_stream->gcount();
2334 if (_body_stream->is_closed()) {
2336 reset_body_stream();
2337 close_download_stream();
2338 _started_download =
false;
2351 run_download_to_stream() {
2352 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2354 bool do_throttle = _wanted_nonblocking && _download_throttle;
2356 static const size_t buffer_size = 4096;
2357 char buffer[buffer_size];
2359 size_t remaining_this_pass = buffer_size;
2361 remaining_this_pass = _bytes_per_update;
2364 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2365 size_t count = _body_stream->gcount();
2366 while (count != 0) {
2367 _download_to_stream->write(buffer, count);
2368 _bytes_downloaded += count;
2370 nassertr(count <= remaining_this_pass,
false);
2371 remaining_this_pass -= count;
2372 if (remaining_this_pass == 0) {
2378 thread_consider_yield();
2379 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2380 count = _body_stream->gcount();
2383 if (_download_to_stream->fail()) {
2384 downloader_cat.warning()
2385 << _NOTIFY_HTTP_CHANNEL_ID
2386 <<
"Error writing to stream\n";
2387 _status_entry._status_code = SC_download_write_error;
2389 reset_download_to();
2393 _download_to_stream->flush();
2395 if (_body_stream->is_closed()) {
2397 reset_body_stream();
2398 close_download_stream();
2399 _started_download =
false;
2413 begin_request(HTTPEnum::Method method,
const DocumentSpec &url,
2414 const string &body,
bool nonblocking,
2415 size_t first_byte,
size_t last_byte) {
2417 downloader_cat.info()
2418 << _NOTIFY_HTTP_CHANNEL_ID
2419 <<
"begin " << method <<
" " << url <<
"\n";
2421 reset_for_new_request();
2423 _wanted_nonblocking = nonblocking;
2424 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS) 2432 _proxy_next_index = 0;
2433 if (get_allow_proxy()) {
2434 _client->get_proxies_for_url(url.get_url(), _proxies);
2440 if (!_bio.is_null() && !_proxies.empty() && !_proxy.empty()) {
2441 Proxies::iterator pi = find(_proxies.begin(), _proxies.end(), _proxy);
2442 if (pi != _proxies.end()) {
2444 _proxies.insert(_proxies.begin(), _proxy);
2449 if (_proxy_next_index < _proxies.size()) {
2450 new_proxy = _proxies[_proxy_next_index];
2451 _proxy_next_index++;
2455 if (_proxy != new_proxy) {
2457 _proxy_auth =
nullptr;
2458 if (downloader_cat.is_debug()) {
2459 downloader_cat.debug()
2460 << _NOTIFY_HTTP_CHANNEL_ID
2461 <<
"resetting to change proxy to " << _proxy <<
"\n";
2467 if (_nonblocking != nonblocking) {
2468 _nonblocking = nonblocking;
2469 if (downloader_cat.is_debug()) {
2470 downloader_cat.debug()
2471 << _NOTIFY_HTTP_CHANNEL_ID
2472 <<
"resetting to change nonblocking state to " << _nonblocking <<
".\n";
2477 reset_url(_request.get_url(), url.get_url());
2484 _want_ssl = _request.get_url().is_ssl();
2486 _first_byte_requested = first_byte;
2487 _last_byte_requested = last_byte;
2493 if (_request.get_url().get_scheme() ==
"file") {
2498 _bio =
new BioPtr(_request.get_url());
2499 if (_bio->get_bio() !=
nullptr) {
2501 _source =
new BioStreamPtr(
new BioStream(_bio));
2502 _status_entry._status_code = 200;
2503 _state = S_start_direct_file_read;
2507 BIO_get_fp(_bio->get_bio(), &fp);
2508 if (fp !=
nullptr) {
2509 if (fseek(fp, 0, SEEK_END) == 0) {
2510 _file_size = ftell(fp);
2511 _got_file_size =
true;
2512 fseek(fp, 0, SEEK_SET);
2518 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
2519 _status_entry._status_code = SC_no_connection;
2525 if (_state == S_failure || (_state < S_read_header && _state != S_ready)) {
2526 if (downloader_cat.is_debug()) {
2527 downloader_cat.debug()
2528 << _NOTIFY_HTTP_CHANNEL_ID
2529 <<
"resetting to clear previous request.\n";
2534 if (downloader_cat.is_debug()) {
2535 downloader_cat.debug()
2536 << _NOTIFY_HTTP_CHANNEL_ID
2537 <<
"resetting old connection: " 2543 }
else if (_state == S_read_header) {
2545 _state = S_begin_body;
2549 if (_method == HTTPEnum::M_connect) {
2550 _done_state = S_ready;
2552 _done_state = S_read_header;
2563 reconsider_proxy() {
2564 _proxy_tunnel_now =
false;
2565 _proxy_serves_document =
false;
2567 if (!_proxy.empty()) {
2574 (get_proxy_tunnel() || _want_ssl ||
2575 _method == HTTPEnum::M_connect || _proxy.get_scheme() ==
"socks");
2579 _proxy_serves_document = !_proxy_tunnel_now;
2583 make_request_text();
2585 if (_proxy_tunnel_now) {
2588 ostringstream request;
2590 <<
"CONNECT " << _request.get_url().get_server_and_port()
2591 <<
" " << _client->get_http_version_string() <<
"\r\n";
2592 if (_client->get_http_version() >= HTTPEnum::HV_11) {
2594 <<
"Host: " << _request.get_url().get_server_and_port() <<
"\r\n";
2596 _proxy_header = request.str();
2597 make_proxy_request_text();
2600 _proxy_header = string();
2601 _proxy_request_text = string();
2611 reset_for_new_request() {
2612 if (downloader_cat.is_spam()) {
2613 downloader_cat.spam()
2614 << _NOTIFY_HTTP_CHANNEL_ID
2615 <<
"reset_for_new_request.\n";
2618 reset_download_to();
2619 reset_body_stream();
2621 _last_status_code = 0;
2622 _status_entry = StatusEntry();
2624 _response_type = RT_none;
2625 _redirect_trail.clear();
2626 _bytes_downloaded = 0;
2627 _bytes_requested = 0;
2639 finished_body(
bool has_trailer) {
2640 if (will_close_connection() && _download_dest == DD_none) {
2641 if (downloader_cat.is_debug()) {
2642 downloader_cat.debug()
2643 << _NOTIFY_HTTP_CHANNEL_ID
2644 <<
"resetting to finish body; server would close anyway.\n";
2650 _state = HTTPChannel::S_read_body;
2652 _state = HTTPChannel::S_read_trailer;
2666 open_download_file() {
2667 _subdocument_resumes = (_subdocument_resumes && _first_byte_delivered != 0);
2669 if (_download_dest == DD_file) {
2671 _download_to_stream = vfs->
open_write_file(_download_to_filename,
false, !_subdocument_resumes);
2672 if (_download_to_stream ==
nullptr) {
2673 downloader_cat.info()
2674 << _NOTIFY_HTTP_CHANNEL_ID
2675 <<
"Could not open " << _download_to_filename <<
" for writing.\n";
2676 _status_entry._status_code = SC_download_open_error;
2682 if (_subdocument_resumes) {
2683 if (_download_dest == DD_file) {
2687 _download_to_stream->seekp(0, std::ios::end);
2688 if (_first_byte_delivered > (
size_t)_download_to_stream->tellp()) {
2689 downloader_cat.info()
2690 << _NOTIFY_HTTP_CHANNEL_ID
2691 <<
"Invalid starting position of byte " << _first_byte_delivered
2692 <<
" within " << _download_to_filename <<
" (which has " 2693 << _download_to_stream->tellp() <<
" bytes)\n";
2694 close_download_stream();
2695 _status_entry._status_code = SC_download_invalid_range;
2700 _download_to_stream->seekp(_first_byte_delivered);
2702 }
else if (_download_dest == DD_ram) {
2703 if (_first_byte_delivered > _download_to_ramfile->_data.length()) {
2704 downloader_cat.info()
2705 << _NOTIFY_HTTP_CHANNEL_ID
2706 <<
"Invalid starting position of byte " << _first_byte_delivered
2707 <<
" within Ramfile (which has " 2708 << _download_to_ramfile->_data.length() <<
" bytes)\n";
2709 close_download_stream();
2710 _status_entry._status_code = SC_download_invalid_range;
2715 if (_first_byte_delivered == 0) {
2716 _download_to_ramfile->_data = string();
2718 _download_to_ramfile->_data =
2719 _download_to_ramfile->_data.substr(0, _first_byte_delivered);
2721 }
else if (_download_dest == DD_stream) {
2725 _download_to_stream->seekp(0, std::ios::end);
2726 if (_first_byte_delivered > (
size_t)_download_to_stream->tellp()) {
2727 downloader_cat.info()
2728 << _NOTIFY_HTTP_CHANNEL_ID
2729 <<
"Invalid starting position of byte " << _first_byte_delivered
2730 <<
" within stream (which has " 2731 << _download_to_stream->tellp() <<
" bytes)\n";
2732 close_download_stream();
2733 _status_entry._status_code = SC_download_invalid_range;
2738 _download_to_stream->seekp(_first_byte_delivered);
2745 if (_download_dest == DD_file || _download_dest == DD_stream) {
2746 _download_to_stream->seekp(0);
2747 }
else if (_download_dest == DD_ram) {
2748 _download_to_ramfile->_data = string();
2762 server_getline(
string &str) {
2763 nassertr(!_source.is_null(),
false);
2764 int ch = (*_source)->get();
2765 while (ch != EOF && !(*_source)->fail()) {
2770 _working_get = string();
2774 size_t p = str.length();
2775 while (p > 0 && isspace(str[p - 1])) {
2778 str = str.substr(0, p);
2780 if (downloader_cat.is_spam()) {
2781 downloader_cat.spam()
2782 << _NOTIFY_HTTP_CHANNEL_ID
2783 <<
"recv: " << str <<
"\n";
2792 _working_get += (char)ch;
2794 ch = (*_source)->get();
2807 server_getline_failsafe(
string &str) {
2808 if (!server_getline(str)) {
2809 if (_bio.is_null()) {
2811 if (_response_type == RT_hangup) {
2813 _status_entry._status_code = SC_lost_connection;
2814 _state = S_try_next_proxy;
2818 _response_type = RT_hangup;
2825 if (elapsed > get_http_timeout()) {
2827 downloader_cat.info()
2828 << _NOTIFY_HTTP_CHANNEL_ID
2829 <<
"Timeout waiting for " 2830 << _request.get_url().get_server_and_port()
2831 <<
" in server_getline_failsafe (" << elapsed
2832 <<
" seconds elapsed).\n";
2833 _status_entry._status_code = SC_timeout;
2834 _state = S_try_next_proxy;
2850 server_get(
string &str,
size_t num_bytes) {
2851 nassertr(!_source.is_null(),
false);
2852 int ch = (*_source)->get();
2853 while (ch != EOF && !(*_source)->fail()) {
2854 _working_get += (char)ch;
2855 if (_working_get.length() >= num_bytes) {
2857 _working_get = string();
2861 ch = (*_source)->get();
2874 server_get_failsafe(
string &str,
size_t num_bytes) {
2875 if (!server_get(str, num_bytes)) {
2876 if (_bio.is_null()) {
2878 if (_response_type == RT_hangup) {
2880 _status_entry._status_code = SC_lost_connection;
2881 _state = S_try_next_proxy;
2885 _response_type = RT_hangup;
2892 if (elapsed > get_http_timeout()) {
2894 downloader_cat.info()
2895 << _NOTIFY_HTTP_CHANNEL_ID
2896 <<
"Timeout waiting for " 2897 << _request.get_url().get_server_and_port()
2898 <<
" in server_get_failsafe (" << elapsed
2899 <<
" seconds elapsed).\n";
2900 _status_entry._status_code = SC_timeout;
2901 _state = S_try_next_proxy;
2921 server_send(
const string &str,
bool secret) {
2922 nassertr(str.length() > _sent_so_far,
true);
2927 size_t bytes_to_send = str.length() - _sent_so_far;
2929 BIO_write(*_bio, str.data() + _sent_so_far, bytes_to_send);
2931 if (write_count <= 0) {
2932 if (BIO_should_retry(*_bio)) {
2937 if (downloader_cat.is_debug()) {
2938 downloader_cat.debug()
2939 << _NOTIFY_HTTP_CHANNEL_ID
2940 <<
"Lost connection to server unexpectedly during write.\n";
2946 if (downloader_cat.is_spam()) {
2947 downloader_cat.spam()
2948 << _NOTIFY_HTTP_CHANNEL_ID
2949 <<
"wrote " << write_count <<
" bytes to " << _bio <<
"\n";
2953 if (!secret && downloader_cat.is_spam()) {
2954 show_send(str.substr(0, write_count));
2958 if (write_count < (
int)bytes_to_send) {
2959 _sent_so_far += write_count;
2974 parse_http_response(
const string &line) {
2976 if (line.length() < 5 || line.substr(0, 5) != string(
"HTTP/")) {
2978 _status_entry._status_code = SC_non_http_response;
2979 if (_response_type == RT_non_http) {
2981 _state = S_try_next_proxy;
2986 if (downloader_cat.is_debug()) {
2987 downloader_cat.debug()
2988 << _NOTIFY_HTTP_CHANNEL_ID
2989 <<
"got non-HTTP response, resetting.\n";
2992 _response_type = RT_non_http;
2999 while (p < line.length() && !isspace(line[p])) {
3002 _http_version_string = line.substr(0, p);
3003 _http_version = HTTPClient::parse_http_version_string(_http_version_string);
3005 while (p < line.length() && isspace(line[p])) {
3009 while (q < line.length() && !isspace(line[q])) {
3012 string status_code = line.substr(p, q - p);
3013 _status_entry._status_code = atoi(status_code.c_str());
3015 while (q < line.length() && isspace(line[q])) {
3018 _status_entry._status_string = line.substr(q, line.length() - q);
3028 parse_http_header() {
3030 if (!server_getline(line)) {
3034 while (!line.empty()) {
3035 if (isspace(line[0])) {
3038 while (p < line.length() && isspace(line[p])) {
3041 _current_field_value += line.substr(p - 1);
3045 if (!_current_field_name.empty()) {
3046 store_header_field(_current_field_name, _current_field_value);
3047 _current_field_value = string();
3050 size_t colon = line.find(
':');
3051 if (colon != string::npos) {
3052 _current_field_name =
downcase(line.substr(0, colon));
3053 size_t p = colon + 1;
3054 while (p < line.length() && isspace(line[p])) {
3057 _current_field_value = line.substr(p);
3061 if (!server_getline(line)) {
3067 if (!_current_field_name.empty()) {
3068 store_header_field(_current_field_name, _current_field_value);
3069 _current_field_value = string();
3081 parse_content_range(
const string &content_range) {
3084 while (p < content_range.length() && !isspace(content_range[p])) {
3088 string units = content_range.substr(0, p);
3089 while (p < content_range.length() && isspace(content_range[p])) {
3093 if (units ==
"bytes") {
3094 const char *c_str = content_range.c_str();
3096 if (p < content_range.length() && isdigit(content_range[p])) {
3097 long first_byte = strtol(c_str + p, &endptr, 10);
3099 if (p < content_range.length() && content_range[p] ==
'-') {
3101 if (p < content_range.length() && isdigit(content_range[p])) {
3102 long last_byte = strtol(c_str + p, &endptr, 10);
3105 if (last_byte >= first_byte) {
3106 _first_byte_delivered = first_byte;
3107 _last_byte_delivered = last_byte;
3126 nassertv(!_source.is_null());
3127 if ((*_source)->is_closed()) {
3128 if (downloader_cat.is_debug()) {
3129 downloader_cat.debug()
3130 << _NOTIFY_HTTP_CHANNEL_ID
3131 <<
"Lost connection to server unexpectedly during read.\n";
3328 check_preapproved_server_certificate(X509 *cert,
bool &cert_preapproved,
3329 bool &cert_name_preapproved)
const {
3330 return _client->check_preapproved_server_certificate(_request.get_url(),
3331 cert, cert_preapproved,
3332 cert_name_preapproved);
3340 validate_server_name(X509 *cert) {
3341 string hostname = _request.get_url().get_server();
3343 vector_string cert_names;
3347 STACK_OF(GENERAL_NAME) *subject_alt_names =
3348 (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(cert, NID_subject_alt_name,
nullptr,
nullptr);
3349 if (subject_alt_names !=
nullptr) {
3350 int num_alts = sk_GENERAL_NAME_num(subject_alt_names);
3351 for (
int i = 0; i < num_alts; ++i) {
3353 const GENERAL_NAME *alt_name =
3354 sk_GENERAL_NAME_value(subject_alt_names, i);
3356 if (alt_name->type == GEN_DNS) {
3357 char *buffer =
nullptr;
3358 int len = ASN1_STRING_to_UTF8((
unsigned char**)&buffer,
3361 cert_names.push_back(
string(buffer, len));
3363 if (buffer !=
nullptr) {
3364 OPENSSL_free(buffer);
3370 if (cert_names.empty()) {
3373 X509_NAME *xname = X509_get_subject_name(cert);
3374 if (xname !=
nullptr) {
3375 string common_name = get_x509_name_component(xname, NID_commonName);
3376 cert_names.push_back(common_name);
3380 if (cert_names.empty()) {
3381 downloader_cat.info()
3382 << _NOTIFY_HTTP_CHANNEL_ID
3383 <<
"Server certificate from " << hostname
3384 <<
" provides no name.\n";
3388 if (downloader_cat.is_debug()) {
3389 downloader_cat.debug()
3390 << _NOTIFY_HTTP_CHANNEL_ID
3391 <<
"Server certificate from " << hostname
3392 <<
" provides name(s):";
3393 vector_string::const_iterator si;
3394 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3395 const string &cert_name = (*si);
3396 downloader_cat.debug(
false)
3397 <<
" " << cert_name;
3399 downloader_cat.debug(
false)
3405 vector_string::const_iterator si;
3406 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3407 const string &cert_name = (*si);
3409 if (match_cert_name(cert_name, hostname)) {
3414 downloader_cat.info()
3415 << _NOTIFY_HTTP_CHANNEL_ID
3416 <<
"Server certificate from " << hostname
3417 <<
" provides wrong name(s):";
3418 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3419 const string &cert_name = (*si);
3420 downloader_cat.info(
false)
3421 <<
" " << cert_name;
3423 downloader_cat.info(
false)
3434 match_cert_name(
const string &cert_name,
const string &hostname) {
3440 pattern.set_case_sensitive(
false);
3441 pattern.set_nomatch_chars(
".");
3442 return pattern.matches(hostname);
3449 string HTTPChannel::
3450 get_x509_name_component(X509_NAME *name,
int nid) {
3451 ASN1_OBJECT *obj = OBJ_nid2obj(nid);
3453 if (obj ==
nullptr) {
3458 int i = X509_NAME_get_index_by_OBJ(name, obj, -1);
3463 ASN1_STRING *data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
3464 return string((
char *)data->data, data->length);
3474 _proxy_auth = _client->select_auth(_proxy,
true, _proxy_realm);
3475 _proxy_username = string();
3476 if (_proxy_auth !=
nullptr) {
3477 _proxy_realm = _proxy_auth->get_realm();
3478 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
3481 if (_method == HTTPEnum::M_connect) {
3489 _www_auth = _client->select_auth(_request.get_url(),
false, _www_realm);
3490 _www_username = string();
3491 if (_www_auth !=
nullptr) {
3492 _www_realm = _www_auth->get_realm();
3493 _www_username = _client->select_username(_request.get_url(),
false, _www_realm);
3496 string request_path;
3497 if (_proxy_serves_document) {
3502 request_path = url_no_username.
get_url();
3507 request_path = _request.get_url().get_path_and_query();
3512 if (request_path.empty()) {
3516 ostringstream stream;
3519 << _method <<
" " << request_path <<
" " 3520 << _client->get_http_version_string() <<
"\r\n";
3522 if (_client->get_http_version() >= HTTPEnum::HV_11) {
3524 if (_request.get_url().has_port() && _request.get_url().is_default_port()) {
3529 string server = _request.get_url().get_server();
3530 if (server.find(
':') != string::npos) {
3531 stream <<
"Host: [" << server <<
"]";
3533 stream <<
"Host: " << server;
3536 stream <<
"Host: " << _request.get_url().get_server_and_port();
3539 if (!get_persistent_connection()) {
3541 <<
"Connection: close\r\n";
3545 if (_last_byte_requested != 0) {
3547 <<
"Range: bytes=" << _first_byte_requested <<
"-" 3548 << _last_byte_requested <<
"\r\n";
3550 }
else if (_first_byte_requested != 0) {
3552 <<
"Range: bytes=" << _first_byte_requested <<
"-\r\n";
3555 switch (_request.get_request_mode()) {
3556 case DocumentSpec::RM_any:
3558 if (_first_byte_requested != 0) {
3562 if (_request.has_tag()) {
3564 <<
"If-Range: " << _request.get_tag().get_string() <<
"\r\n";
3565 }
else if (_request.has_date()) {
3567 <<
"If-Range: " << _request.get_date().get_string() <<
"\r\n";
3572 case DocumentSpec::RM_equal:
3574 if (_request.has_tag()) {
3576 <<
"If-Match: " << _request.get_tag().get_string() <<
"\r\n";
3578 if (_request.has_date()) {
3580 <<
"If-Unmodified-Since: " << _request.get_date().get_string()
3585 case DocumentSpec::RM_newer:
3587 if (_request.has_tag()) {
3589 <<
"If-None-Match: " << _request.get_tag().get_string() <<
"\r\n";
3591 if (_request.has_date()) {
3593 <<
"If-Modified-Since: " << _request.get_date().get_string()
3598 case DocumentSpec::RM_equal_or_newer:
3600 if (_request.has_date()) {
3605 <<
"If-Modified-Since: " << (_request.get_date() - 1).get_string()
3611 switch (_request.get_cache_control()) {
3612 case DocumentSpec::CC_allow_cache:
3616 case DocumentSpec::CC_revalidate:
3619 <<
"Cache-Control: max-age=0\r\n";
3622 case DocumentSpec::CC_no_cache:
3625 <<
"Cache-Control: no-cache\r\n" 3626 <<
"Pragma: no-cache\r\n";
3630 _client->send_cookies(stream, _request.get_url());
3632 if (!_body.empty()) {
3634 <<
"Content-Type: " << _content_type <<
"\r\n" 3635 <<
"Content-Length: " << _body.length() <<
"\r\n";
3638 _header = stream.str();
3648 make_proxy_request_text() {
3649 _proxy_request_text = _proxy_header;
3651 if (_proxy_auth !=
nullptr && !_proxy_username.empty()) {
3652 _proxy_request_text +=
"Proxy-Authorization: ";
3653 _proxy_request_text +=
3654 _proxy_auth->generate(HTTPEnum::M_connect, _request.get_url().get_server_and_port(),
3655 _proxy_username, _body);
3656 _proxy_request_text +=
"\r\n";
3659 _proxy_request_text +=
"\r\n";
3667 make_request_text() {
3668 _request_text = _header;
3670 if (_proxy_serves_document &&
3671 _proxy_auth !=
nullptr && !_proxy_username.empty()) {
3672 _request_text +=
"Proxy-Authorization: ";
3674 _proxy_auth->generate(_method, _request.get_url().get_url(), _proxy_username, _body);
3675 _request_text +=
"\r\n";
3678 if (_www_auth !=
nullptr && !_www_username.empty()) {
3679 string authorization =
3680 _request_text +=
"Authorization: ";
3682 _www_auth->generate(_method, _request.get_url().get_path_and_query(), _www_username, _body);
3683 _request_text +=
"\r\n";
3686 _request_text += _send_extra_headers;
3687 _request_text +=
"\r\n";
3688 _request_text += _body;
3704 if (downloader_cat.is_debug()) {
3705 downloader_cat.debug()
3706 << _NOTIFY_HTTP_CHANNEL_ID
3707 <<
"resetting for new server " 3719 store_header_field(
const string &field_name,
const string &field_value) {
3720 std::pair<Headers::iterator, bool> insert_result =
3721 _headers.insert(Headers::value_type(field_name, field_value));
3723 if (!insert_result.second) {
3726 Headers::iterator hi = insert_result.first;
3727 (*hi).second +=
", ";
3728 (*hi).second += field_value;
3731 if (field_name ==
"set-cookie") {
3732 _client->set_cookie(HTTPCookie(field_value, _request.get_url()));
3741 show_send(
const string &message) {
3743 size_t newline = message.find(
'\n', start);
3744 while (newline != string::npos) {
3746 downloader_cat.spam()
3747 <<
"send: " << message.substr(start, newline - start - 1) <<
"\n";
3748 start = newline + 1;
3749 newline = message.find(
'\n', start);
3752 if (start < message.length()) {
3753 downloader_cat.spam()
3754 <<
"send: " << message.substr(start) <<
" (no newline)\n";
3764 reset_download_to() {
3765 _started_download =
false;
3766 close_download_stream();
3767 _download_dest = DD_none;
3775 close_download_stream() {
3776 if (_download_to_stream !=
nullptr) {
3777 _download_to_stream->flush();
3778 if (_download_dest == DD_file) {
3782 _download_to_ramfile =
nullptr;
3783 _download_to_stream =
nullptr;
3792 if (downloader_cat.is_spam()) {
3793 downloader_cat.spam()
3794 << _NOTIFY_HTTP_CHANNEL_ID
3795 <<
"reset_to_new.\n";
3806 reset_body_stream() {
3807 if (_owns_body_stream) {
3808 if (_body_stream !=
nullptr) {
3809 close_read_body(_body_stream);
3810 nassertv(_body_stream ==
nullptr && !_owns_body_stream);
3813 _body_stream =
nullptr;
3822 close_connection() {
3823 reset_body_stream();
3826 _working_get = string();
3837 more_useful_status_code(
int a,
int b) {
3838 if (a >= 100 && b >= 100) {
3851 int series_a = (a / 100);
3852 int series_b = (b / 100);
3855 return (series_a < series_b);
3858 if (a < 100 && b < 100) {
3866 return (a > SC_http_error_watermark);
3870 return (b < SC_http_error_watermark);
3878 operator << (ostream &out, HTTPChannel::State state) {
3880 return out << (int)state;
3883 case HTTPChannel::S_new:
3884 return out <<
"new";
3886 case HTTPChannel::S_try_next_proxy:
3887 return out <<
"try_next_proxy";
3889 case HTTPChannel::S_connecting:
3890 return out <<
"connecting";
3892 case HTTPChannel::S_connecting_wait:
3893 return out <<
"connecting_wait";
3895 case HTTPChannel::S_http_proxy_ready:
3896 return out <<
"http_proxy_ready";
3898 case HTTPChannel::S_http_proxy_request_sent:
3899 return out <<
"http_proxy_request_sent";
3901 case HTTPChannel::S_http_proxy_reading_header:
3902 return out <<
"http_proxy_reading_header";
3904 case HTTPChannel::S_socks_proxy_greet:
3905 return out <<
"socks_proxy_greet";
3907 case HTTPChannel::S_socks_proxy_greet_reply:
3908 return out <<
"socks_proxy_greet_reply";
3910 case HTTPChannel::S_socks_proxy_connect:
3911 return out <<
"socks_proxy_connect";
3913 case HTTPChannel::S_socks_proxy_connect_reply:
3914 return out <<
"socks_proxy_connect_reply";
3916 case HTTPChannel::S_setup_ssl:
3917 return out <<
"setup_ssl";
3919 case HTTPChannel::S_ssl_handshake:
3920 return out <<
"ssl_handshake";
3922 case HTTPChannel::S_ready:
3923 return out <<
"ready";
3925 case HTTPChannel::S_request_sent:
3926 return out <<
"request_sent";
3928 case HTTPChannel::S_reading_header:
3929 return out <<
"reading_header";
3931 case HTTPChannel::S_start_direct_file_read:
3932 return out <<
"start_direct_file_read";
3934 case HTTPChannel::S_read_header:
3935 return out <<
"read_header";
3937 case HTTPChannel::S_begin_body:
3938 return out <<
"begin_body";
3940 case HTTPChannel::S_reading_body:
3941 return out <<
"reading_body";
3943 case HTTPChannel::S_read_body:
3944 return out <<
"read_body";
3946 case HTTPChannel::S_read_trailer:
3947 return out <<
"read_trailer";
3949 case HTTPChannel::S_failure:
3950 return out <<
"failure";
3953 return out <<
"invalid state(" << (int)state <<
")";
3957 #endif // HAVE_OPENSSL PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for a URL, e.g.
A hierarchy of directories and files that appears to be one continuous file system,...
A container for an "entity tag" from an HTTP server.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_binary()
Indicates that the filename represents a binary file.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as a floating- point type.
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
The name of a file, such as a texture file or an Egg file.
An in-memory buffer specifically designed for downloading files to memory.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
get_path
Returns the path specified by the URL, or "/" if no path is specified.
A container for an HTTP-legal time/date indication.
get_authority
Returns the authority specified by the URL (this includes username, server, and/or port),...
bool is_local() const
Returns true if the filename is local, e.g.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
set_username
Replaces the username part of the URL specification.
get_port
Returns the port number specified by the URL, or the default port if not specified.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const std::string & get_url() const
Returns the complete URL specification.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_server
Returns the server name specified by the URL, if any.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A descriptor that refers to a particular version of a document.
TypeHandle is the identifier used to differentiate C++ class types.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_server_and_port
Returns a string consisting of the server name, followed by a colon, followed by the port number.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class can be used to test for string matches against standard Unix- shell filename globbing conv...