32#if defined(WIN32_VC) || defined(WIN64_VC)
41using std::ostringstream;
46#define _NOTIFY_HTTP_CHANNEL_ID "[" << this << "] "
52HTTPChannel(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_dest = DD_none;
121 _download_to_ramfile =
nullptr;
122 _download_to_stream =
nullptr;
130 if (downloader_cat.is_debug()) {
131 downloader_cat.debug()
132 << _NOTIFY_HTTP_CHANNEL_ID
145get_status_string()
const {
146 switch (_status_entry._status_code) {
148 return "Connection in progress";
150 case SC_internal_error:
151 return "Internal error";
153 case SC_no_connection:
154 return "No connection";
157 return "Timeout on connection";
159 case SC_lost_connection:
160 return "Lost connection";
162 case SC_non_http_response:
163 return "Non-HTTP response";
165 case SC_invalid_http:
166 return "Could not understand HTTP response";
168 case SC_socks_invalid_version:
169 return "Unsupported SOCKS version";
171 case SC_socks_no_acceptable_login_method:
172 return "No acceptable SOCKS login method";
174 case SC_socks_refused:
175 return "SOCKS proxy refused connection";
177 case SC_socks_no_connection:
178 return "SOCKS proxy unable to connect";
180 case SC_ssl_internal_failure:
181 return "SSL internal failure";
183 case SC_ssl_no_handshake:
184 return "No SSL handshake";
186 case SC_http_error_watermark:
188 return "Internal error";
190 case SC_ssl_invalid_server_certificate:
191 return "SSL invalid server certificate";
193 case SC_ssl_unexpected_server:
194 return "Unexpected SSL server";
196 case SC_download_open_error:
197 return "Error opening file";
199 case SC_download_write_error:
200 return "Error writing to disk";
202 case SC_download_invalid_range:
203 return "Invalid subrange requested";
206 return _status_entry._status_string;
214get_header_value(
const string &key)
const {
215 Headers::const_iterator hi = _headers.find(
downcase(key));
216 if (hi != _headers.end()) {
228will_close_connection()
const {
229 if (get_http_version() < HTTPEnum::HV_11) {
234 string connection = get_header_value(
"Connection");
235 if (
downcase(connection) ==
"close") {
240 if (connection.empty() && !get_persistent_connection()) {
260std::streamsize HTTPChannel::
261get_file_size()
const {
262 if (_got_file_size) {
264 }
else if (_got_transfer_file_size) {
265 return _transfer_file_size;
266 }
else if (_got_expected_file_size) {
267 return _expected_file_size;
278write_headers(ostream &out)
const {
279 Headers::const_iterator hi;
280 for (hi = _headers.begin(); hi != _headers.end(); ++hi) {
281 out << (*hi).first <<
": " << (*hi).second <<
"\n";
297 if (downloader_cat.is_spam()) {
298 downloader_cat.spam()
299 << _NOTIFY_HTTP_CHANNEL_ID
303 if (_state == _done_state || _state == S_failure) {
304 clear_extra_headers();
305 if (!reached_done_state()) {
310 if (_started_download) {
311 if (_wanted_nonblocking && _download_throttle) {
313 double elapsed = now - _last_run_time;
314 if (elapsed < _seconds_per_update) {
319 int num_potential_updates = (int)(elapsed / _seconds_per_update);
320 _last_run_time = now;
321 _bytes_requested += _bytes_per_update * num_potential_updates;
322 if (downloader_cat.is_spam()) {
323 downloader_cat.spam()
324 << _NOTIFY_HTTP_CHANNEL_ID
325 <<
"elapsed = " << elapsed <<
" num_potential_updates = "
326 << num_potential_updates <<
" bytes_requested = "
327 << _bytes_requested <<
"\n";
331 bool repeat_later =
false;
332 switch (_download_dest) {
338 repeat_later = run_download_to_file();
342 repeat_later = run_download_to_ram();
346 repeat_later = run_download_to_stream();
364 if (_state == _done_state) {
365 return reached_done_state();
373 if (_bio.is_null() && _state != S_try_next_proxy) {
374 if (_connect_count > http_max_connect_count) {
379 downloader_cat.warning()
380 << _NOTIFY_HTTP_CHANNEL_ID
381 <<
"Too many lost connections, giving up.\n";
382 _status_entry._status_code = SC_lost_connection;
389 if (_proxy.empty()) {
394 _bio =
new BioPtr(url);
395 _source =
new BioStreamPtr(
new BioStream(_bio));
397 _bio->set_nbio(
true);
400 if (downloader_cat.is_debug()) {
401 if (_connect_count > 0) {
402 downloader_cat.debug()
403 << _NOTIFY_HTTP_CHANNEL_ID
404 <<
"Reconnecting to " << _bio->get_server_name() <<
" port "
405 << _bio->get_port() <<
"\n";
407 downloader_cat.debug()
408 << _NOTIFY_HTTP_CHANNEL_ID
409 <<
"Connecting to " << _bio->get_server_name() <<
" port "
410 << _bio->get_port() <<
"\n";
414 _state = S_connecting;
415 _started_connecting_time =
429 case S_try_next_proxy:
430 repeat_later = run_try_next_proxy();
434 repeat_later = run_connecting();
437 case S_connecting_wait:
438 repeat_later = run_connecting_wait();
441 case S_http_proxy_ready:
442 repeat_later = run_http_proxy_ready();
445 case S_http_proxy_request_sent:
446 repeat_later = run_http_proxy_request_sent();
449 case S_http_proxy_reading_header:
450 repeat_later = run_http_proxy_reading_header();
453 case S_socks_proxy_greet:
454 repeat_later = run_socks_proxy_greet();
457 case S_socks_proxy_greet_reply:
458 repeat_later = run_socks_proxy_greet_reply();
461 case S_socks_proxy_connect:
462 repeat_later = run_socks_proxy_connect();
465 case S_socks_proxy_connect_reply:
466 repeat_later = run_socks_proxy_connect_reply();
470 repeat_later = run_setup_ssl();
473 case S_ssl_handshake:
474 repeat_later = run_ssl_handshake();
478 repeat_later = run_ready();
482 repeat_later = run_request_sent();
485 case S_reading_header:
486 repeat_later = run_reading_header();
489 case S_start_direct_file_read:
490 repeat_later = run_start_direct_file_read();
494 repeat_later = run_read_header();
498 repeat_later = run_begin_body();
502 repeat_later = run_reading_body();
506 repeat_later = run_read_body();
510 repeat_later = run_read_trailer();
514 downloader_cat.warning()
515 << _NOTIFY_HTTP_CHANNEL_ID
516 <<
"Unhandled state " << _state <<
"\n";
520 if (_state == _done_state || _state == S_failure) {
521 clear_extra_headers();
523 return reached_done_state();
525 thread_consider_yield();
526 }
while (!repeat_later || _bio.is_null());
554ISocketStream *HTTPChannel::
558 if ((_state != S_read_header && _state != S_begin_body) || _source.is_null()) {
562 string transfer_coding =
downcase(get_header_value(
"Transfer-Encoding"));
564 ISocketStream *result;
565 if (transfer_coding ==
"chunked") {
569 _state = S_reading_body;
571 result =
new IChunkedStream(_source,
this);
578 _state = S_reading_body;
580 result =
new IIdentityStream(_source,
this, _got_file_size, _file_size);
583 result->_channel =
this;
584 _body_stream = result;
585 _owns_body_stream =
false;
597close_read_body(istream *stream)
const {
598 if (stream !=
nullptr) {
603#if !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
605 (*global_operator_delete)(stream);
637download_to_file(
const Filename &filename,
bool subdocument_resumes) {
639 _download_to_filename = filename;
641 _subdocument_resumes = subdocument_resumes;
643 _download_dest = DD_file;
645 if (_wanted_nonblocking && _state != S_read_header) {
652 if (!open_download_file()) {
659 return is_download_complete() && is_valid();
686download_to_ram(
Ramfile *ramfile,
bool subdocument_resumes) {
687 nassertr(ramfile !=
nullptr,
false);
690 _download_to_ramfile = ramfile;
691 _download_dest = DD_ram;
692 _subdocument_resumes = (subdocument_resumes && _first_byte_delivered != 0);
694 if (_wanted_nonblocking && _state != S_read_header) {
701 if (!open_download_file()) {
708 return is_download_complete() && is_valid();
736download_to_stream(ostream *strm,
bool subdocument_resumes) {
738 _download_to_stream = strm;
739 _download_to_stream->clear();
740 _subdocument_resumes = subdocument_resumes;
742 _download_dest = DD_stream;
744 if (_wanted_nonblocking && _state != S_read_header) {
751 if (!open_download_file()) {
758 return is_download_complete() && is_valid();
769SocketStream *HTTPChannel::
771 if (!is_connection_ready()) {
775 BioStream *stream = _source->get_stream();
776 _source->set_stream(
nullptr);
779 if (downloader_cat.is_debug()) {
780 downloader_cat.debug()
781 << _NOTIFY_HTTP_CHANNEL_ID
782 <<
"passing ownership of connection to caller.\n";
793downcase(
const string &s) {
795 result.reserve(s.size());
796 string::const_iterator p;
797 for (p = s.begin(); p != s.end(); ++p) {
798 result += tolower(*p);
807body_stream_destructs(ISocketStream *stream) {
808 if (stream == _body_stream) {
809 if (_state == S_reading_body) {
810 switch (_body_stream->get_read_state()) {
811 case ISocketStream::RS_complete:
812 finished_body(
false);
815 case ISocketStream::RS_error:
816 _state = HTTPChannel::S_failure;
817 _status_entry._status_code = HTTPChannel::SC_lost_connection;
824 _body_stream =
nullptr;
825 _owns_body_stream =
false;
835reached_done_state() {
845 if (_state == S_failure) {
849 if (!_status_list.empty()) {
850 _status_list.push_back(_status_entry);
851 if (downloader_cat.is_debug()) {
852 downloader_cat.debug()
853 << _NOTIFY_HTTP_CHANNEL_ID
854 <<
"Reexamining failure responses.\n";
857 if (downloader_cat.is_debug()) {
858 downloader_cat.debug()
859 << _NOTIFY_HTTP_CHANNEL_ID
860 <<
" " << 0 <<
". " << _status_list[0]._status_code <<
" "
861 << _status_list[0]._status_string <<
"\n";
863 for (
size_t i = 1; i < _status_list.size(); i++) {
864 if (downloader_cat.is_debug()) {
865 downloader_cat.debug()
866 << _NOTIFY_HTTP_CHANNEL_ID
867 <<
" " << i <<
". " << _status_list[i]._status_code <<
" "
868 << _status_list[i]._status_string <<
"\n";
870 if (more_useful_status_code(_status_list[i]._status_code,
871 _status_list[best_i]._status_code)) {
875 if (downloader_cat.is_debug()) {
876 downloader_cat.debug()
877 << _NOTIFY_HTTP_CHANNEL_ID
878 <<
"chose index " << best_i <<
", above.\n";
880 _status_entry = _status_list[best_i];
881 _status_list.clear();
888 _status_list.clear();
890 if (_download_dest == DD_none) {
897 if (_body_stream ==
nullptr) {
898 if (downloader_cat.is_debug()) {
899 downloader_cat.debug()
900 << _NOTIFY_HTTP_CHANNEL_ID
901 <<
"Unable to download body: " << _request.get_url() <<
"\n";
906 _owns_body_stream =
true;
907 if (_state != S_reading_body) {
910 _started_download =
true;
912 _done_state = S_read_trailer;
925run_try_next_proxy() {
926 if (_proxy_next_index < _proxies.size()) {
929 _status_list.push_back(_status_entry);
930 _status_entry = StatusEntry();
933 _proxy = _proxies[_proxy_next_index];
934 _proxy_auth =
nullptr;
938 _state = S_connecting;
954 _status_entry = StatusEntry();
956 if (!_bio->connect()) {
957 if (_bio->should_retry()) {
958 _state = S_connecting_wait;
961 downloader_cat.info()
962 << _NOTIFY_HTTP_CHANNEL_ID
963 <<
"Could not connect to " << _bio->get_server_name() <<
" port "
964 << _bio->get_port() <<
"\n";
965 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
966 _status_entry._status_code = SC_no_connection;
967 _state = S_try_next_proxy;
971 if (downloader_cat.is_debug()) {
972 downloader_cat.debug()
973 << _NOTIFY_HTTP_CHANNEL_ID
974 <<
"Connected to " << _bio->get_server_name() <<
" port "
975 << _bio->get_port() <<
"\n";
978 if (_proxy_tunnel_now) {
979 if (_proxy.get_scheme() ==
"socks") {
980 _state = S_socks_proxy_greet;
982 _state = S_http_proxy_ready;
987 _state = S_setup_ssl;
1001run_connecting_wait() {
1003 BIO_get_fd(*_bio, &fd);
1005 downloader_cat.warning()
1006 << _NOTIFY_HTTP_CHANNEL_ID
1007 <<
"nonblocking socket BIO has no file descriptor.\n";
1009 _status_entry._status_code = SC_internal_error;
1010 _state = S_try_next_proxy;
1014 if (downloader_cat.is_spam()) {
1015 downloader_cat.spam()
1016 << _NOTIFY_HTTP_CHANNEL_ID
1017 <<
"waiting to connect to " << _request.get_url().get_server_and_port() <<
".\n";
1023 if (get_blocking_connect()) {
1026 tv.tv_sec = (int)_connect_timeout;
1027 tv.tv_usec = (int)((_connect_timeout - tv.tv_sec) * 1000000.0);
1033 int errcode = select(fd + 1,
nullptr, &wset,
nullptr, &tv);
1035 downloader_cat.warning()
1036 << _NOTIFY_HTTP_CHANNEL_ID
1037 <<
"Error in select.\n";
1039 _status_entry._status_code = SC_internal_error;
1040 _state = S_try_next_proxy;
1046 if (get_blocking_connect() ||
1048 _started_connecting_time > get_connect_timeout())) {
1050 downloader_cat.info()
1051 << _NOTIFY_HTTP_CHANNEL_ID
1052 <<
"Timeout connecting to "
1053 << _request.get_url().get_server_and_port()
1054 <<
" for " << _request.get_url()
1056 _status_entry._status_code = SC_timeout;
1057 _state = S_try_next_proxy;
1064 _state = S_connecting;
1075run_http_proxy_ready() {
1077 nassertr(!_proxy_request_text.empty(),
false);
1078 if (!server_send(_proxy_request_text,
false)) {
1083 _state = S_http_proxy_request_sent;
1095run_http_proxy_request_sent() {
1098 if (!server_getline_failsafe(line)) {
1103 while (line.empty()) {
1104 if (!server_getline_failsafe(line)) {
1109 if (!parse_http_response(line)) {
1113 _state = S_http_proxy_reading_header;
1114 _current_field_name = string();
1115 _current_field_value = string();
1117 _got_file_size =
false;
1118 _got_transfer_file_size =
false;
1127run_http_proxy_reading_header() {
1128 if (parse_http_header()) {
1132 _redirect = get_header_value(
"Location");
1136 _server_response_has_no_body =
1137 (get_status_code() / 100 == 1 ||
1138 get_status_code() == 204 ||
1139 get_status_code() == 304);
1141 int last_status = _last_status_code;
1142 _last_status_code = get_status_code();
1144 if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
1146 string authenticate_request = get_header_value(
"Proxy-Authenticate");
1147 _proxy_auth = _client->generate_auth(_proxy,
true, authenticate_request);
1148 if (_proxy_auth !=
nullptr) {
1149 _proxy_realm = _proxy_auth->get_realm();
1150 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
1151 if (!_proxy_username.empty()) {
1152 make_proxy_request_text();
1155 _state = S_begin_body;
1167 if (get_status_code() != 407) {
1168 _status_entry._status_code += 1000;
1171 _state = S_try_next_proxy;
1176 make_request_text();
1179 _state = S_setup_ssl;
1192run_socks_proxy_greet() {
1193 static const char socks_greeting[] = {
1202 static const int socks_greeting_len =
sizeof(socks_greeting);
1203 if (!server_send(
string(socks_greeting, socks_greeting_len),
true)) {
1209 _state = S_socks_proxy_greet_reply;
1217run_socks_proxy_greet_reply() {
1221 if (!server_get_failsafe(reply, 2)) {
1225 if (reply[0] != 0x05) {
1227 downloader_cat.info()
1228 << _NOTIFY_HTTP_CHANNEL_ID
1229 <<
"Rejecting Socks version " << (int)reply[0] <<
"\n";
1230 _status_entry._status_code = SC_socks_invalid_version;
1231 _state = S_try_next_proxy;
1235 if (reply[1] == (
char)0xff) {
1236 downloader_cat.info()
1237 << _NOTIFY_HTTP_CHANNEL_ID
1238 <<
"Socks server does not accept our available login methods.\n";
1239 _status_entry._status_code = SC_socks_no_acceptable_login_method;
1240 _state = S_try_next_proxy;
1244 if (reply[1] == 0x00) {
1246 _state = S_socks_proxy_connect;
1251 downloader_cat.info()
1252 << _NOTIFY_HTTP_CHANNEL_ID
1253 <<
"Socks server accepted unrequested login method "
1254 << (int)reply[1] <<
"\n";
1255 _status_entry._status_code = SC_socks_no_acceptable_login_method;
1256 _state = S_try_next_proxy;
1264run_socks_proxy_connect() {
1265 static const char socks_connect[] = {
1271 static const int socks_connect_len =
sizeof(socks_connect);
1273 string hostname = _request.get_url().get_server();
1274 int port = _request.get_url().get_port();
1276 if (downloader_cat.is_debug()) {
1277 downloader_cat.debug()
1278 << _NOTIFY_HTTP_CHANNEL_ID
1279 <<
"Requesting SOCKS5 connection to "
1280 << _request.get_url().get_server_and_port() <<
"\n";
1284 string(socks_connect, socks_connect_len) +
1285 string(1, (
char)hostname.length()) +
1287 string(1, (
char)((port >> 8) & 0xff)) +
1288 string(1, (
char)(port & 0xff));
1290 if (!server_send(connect,
true)) {
1295 _state = S_socks_proxy_connect_reply;
1303run_socks_proxy_connect_reply() {
1307 if (!server_get_failsafe(reply, 2)) {
1311 if (reply[0] != 0x05) {
1313 downloader_cat.info()
1314 << _NOTIFY_HTTP_CHANNEL_ID
1315 <<
"Rejecting Socks version " << (int)reply[0] <<
"\n";
1317 _status_entry._status_code = SC_socks_invalid_version;
1318 _state = S_try_next_proxy;
1322 if (reply[1] != 0x00) {
1323 downloader_cat.info()
1324 << _NOTIFY_HTTP_CHANNEL_ID
1325 <<
"Connection refused, SOCKS code " << (int)reply[1] <<
"\n";
1347 _status_entry._status_code = SC_socks_no_connection;
1351 _status_entry._status_code = SC_socks_refused;
1355 _state = S_try_next_proxy;
1360 _working_get = reply;
1361 if (!server_get_failsafe(reply, 5)) {
1366 int total_bytes = 6;
1374 total_bytes += (
unsigned int)reply[4];
1382 downloader_cat.info()
1383 << _NOTIFY_HTTP_CHANNEL_ID
1384 <<
"Unsupported SOCKS address type: " << (int)reply[3] <<
"\n";
1385 _status_entry._status_code = SC_socks_invalid_version;
1386 _state = S_try_next_proxy;
1391 _working_get = reply;
1392 if (!server_get_failsafe(reply, total_bytes)) {
1396 if (downloader_cat.is_debug()) {
1398 string connect_host;
1404 strm << (
unsigned int)(
unsigned char)reply[4] <<
"."
1405 << (
unsigned int)(
unsigned char)reply[5] <<
"."
1406 << (
unsigned int)(
unsigned char)reply[6] <<
"."
1407 << (
unsigned int)(
unsigned char)reply[7];
1408 connect_host = strm.str();
1413 connect_host = string(&reply[5], (
unsigned int)reply[4]);
1419 sprintf(buf,
"[%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx"
1420 ":%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx]",
1421 reply[4], reply[5], reply[6], reply[7], reply[8], reply[9],
1422 reply[10], reply[11], reply[12], reply[13], reply[14],
1423 reply[15], reply[16], reply[17], reply[18], reply[19]);
1430 (((
unsigned int)(
unsigned char)reply[total_bytes - 2]) << 8) |
1431 ((
unsigned int)(
unsigned char)reply[total_bytes - 1]);
1433 downloader_cat.debug()
1434 << _NOTIFY_HTTP_CHANNEL_ID
1435 << _proxy <<
" directed us to " << connect_host <<
":"
1436 << connect_port <<
"\n";
1440 _state = S_setup_ssl;
1454 _sbio = BIO_new_ssl(_client->get_ssl_ctx(),
true);
1455 BIO_push(_sbio, *_bio);
1458 BIO_get_ssl(_sbio, &ssl);
1459 nassertr(ssl !=
nullptr,
false);
1463 string cipher_list = _cipher_list;
1464 if (!cipher_list.empty()) {
1465 size_t space = cipher_list.find(
" ");
1466 if (space != string::npos) {
1467 cipher_list = cipher_list.substr(0, space);
1471 if (downloader_cat.is_debug()) {
1472 downloader_cat.debug()
1473 << _NOTIFY_HTTP_CHANNEL_ID
1474 <<
"Setting ssl-cipher-list '" << cipher_list <<
"'\n";
1476 int result = SSL_set_cipher_list(ssl, cipher_list.c_str());
1478 downloader_cat.error()
1479 << _NOTIFY_HTTP_CHANNEL_ID
1480 <<
"Invalid cipher list: '" << cipher_list <<
"'\n";
1481 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
1482 _status_entry._status_code = SC_ssl_internal_failure;
1487 string hostname = _request.get_url().get_server();
1488 result = SSL_set_tlsext_host_name(ssl, hostname.c_str());
1490 downloader_cat.error()
1491 << _NOTIFY_HTTP_CHANNEL_ID
1492 <<
"Could not set TLS SNI hostname to '" << hostname <<
"'\n";
1504 if (_client->load_client_certificate()) {
1505 SSL_use_certificate(ssl, _client->_client_certificate_pub);
1506 SSL_use_PrivateKey(ssl, _client->_client_certificate_priv);
1507 if (!SSL_check_private_key(ssl)) {
1508 downloader_cat.warning()
1509 << _NOTIFY_HTTP_CHANNEL_ID
1510 <<
"Client private key does not match public key!\n";
1514 if (downloader_cat.is_spam()) {
1515 downloader_cat.spam()
1516 << _NOTIFY_HTTP_CHANNEL_ID
1517 <<
"SSL Ciphers available:\n";
1520 name = SSL_get_cipher_list(ssl, pri);
1521 while (name !=
nullptr) {
1522 downloader_cat.spam()
1523 << _NOTIFY_HTTP_CHANNEL_ID
1524 <<
" " << pri + 1 <<
". " << name <<
"\n";
1526 name = SSL_get_cipher_list(ssl, pri);
1530 if (downloader_cat.is_debug()) {
1531 downloader_cat.debug()
1532 << _NOTIFY_HTTP_CHANNEL_ID
1533 <<
"performing SSL handshake\n";
1535 _state = S_ssl_handshake;
1538 _started_connecting_time =
1549run_ssl_handshake() {
1550 if (BIO_do_handshake(_sbio) <= 0) {
1551 if (BIO_should_retry(_sbio)) {
1554 _started_connecting_time;
1555 if (elapsed <= get_connect_timeout() + _extra_ssl_handshake_time) {
1562 downloader_cat.info()
1563 << _NOTIFY_HTTP_CHANNEL_ID
1564 <<
"Could not establish SSL handshake with "
1565 << _request.get_url().get_server_and_port() <<
"\n";
1566 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
1571 if (!_cipher_list.empty()) {
1573 size_t space = _cipher_list.find(
" ");
1574 if (space != string::npos) {
1575 while (space < _cipher_list.length() && _cipher_list[space] ==
' ') {
1578 _cipher_list = _cipher_list.substr(space);
1579 if (!_cipher_list.empty()) {
1582 _state = S_connecting;
1589 _cipher_list = _client->get_cipher_list();
1590 _status_entry._status_code = SC_ssl_no_handshake;
1596 BIO_get_ssl(_sbio, &ssl);
1597 nassertr(ssl !=
nullptr,
false);
1599 if (!_nonblocking) {
1600 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
1603 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1604 if (cipher ==
nullptr) {
1605 downloader_cat.warning()
1606 << _NOTIFY_HTTP_CHANNEL_ID
1607 <<
"No current cipher on SSL connection.\n";
1609 if (downloader_cat.is_debug()) {
1610 downloader_cat.debug()
1611 << _NOTIFY_HTTP_CHANNEL_ID
1612 <<
"Using cipher " << SSL_CIPHER_get_name((SSL_CIPHER *) cipher) <<
"\n";
1618 _bio->set_bio(_sbio);
1621 X509 *cert = SSL_get_peer_certificate(ssl);
1622 if (cert ==
nullptr) {
1623 downloader_cat.info()
1624 << _NOTIFY_HTTP_CHANNEL_ID
1625 <<
"No certificate was presented by server.\n";
1628 _status_entry._status_code = SC_ssl_invalid_server_certificate;
1633 X509_NAME *subject = X509_get_subject_name(cert);
1634 if (downloader_cat.is_debug()) {
1635 string org_name = get_x509_name_component(subject, NID_organizationName);
1636 string org_unit_name = get_x509_name_component(subject, NID_organizationalUnitName);
1637 string common_name = get_x509_name_component(subject, NID_commonName);
1639 downloader_cat.debug()
1640 << _NOTIFY_HTTP_CHANNEL_ID
1641 <<
"Server is " << common_name <<
" from " << org_unit_name
1642 <<
" / " << org_name <<
"\n";
1644 if (downloader_cat.is_spam()) {
1645 downloader_cat.spam()
1646 << _NOTIFY_HTTP_CHANNEL_ID
1647 <<
"Received certificate from server:\n" << std::flush;
1648 X509_print_fp(stderr, cert);
1653 bool cert_preapproved =
false;
1654 bool cert_name_preapproved =
false;
1655 check_preapproved_server_certificate(cert, cert_preapproved, cert_name_preapproved);
1658 long verify_result = SSL_get_verify_result(ssl);
1659 bool cert_valid =
true;
1661 if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) {
1662 downloader_cat.info()
1663 << _NOTIFY_HTTP_CHANNEL_ID
1664 <<
"Expired certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1665 if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
1669 }
else if (verify_result == X509_V_ERR_CERT_NOT_YET_VALID) {
1670 downloader_cat.info()
1671 << _NOTIFY_HTTP_CHANNEL_ID
1672 <<
"Premature certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1673 if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
1677 }
else if (verify_result == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
1678 verify_result == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
1679 downloader_cat.info()
1680 << _NOTIFY_HTTP_CHANNEL_ID
1681 <<
"Self-signed certificate from " << _request.get_url().get_server_and_port() <<
"\n";
1682 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
1686 }
else if (verify_result != X509_V_OK) {
1687 downloader_cat.info()
1688 << _NOTIFY_HTTP_CHANNEL_ID
1689 <<
"Unable to verify identity of " << _request.get_url().get_server_and_port()
1690 <<
", verify error code " << verify_result <<
"\n";
1691 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
1697 _status_entry._status_code = SC_ssl_invalid_server_certificate;
1702 if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_name_preapproved) {
1704 if (!validate_server_name(cert)) {
1705 _status_entry._status_code = SC_ssl_unexpected_server;
1728 if (!_request_text.empty()) {
1729 if (!server_send(_request_text,
false)) {
1735 _state = S_request_sent;
1748 if (!server_getline_failsafe(line)) {
1753 while (line.empty()) {
1754 if (!server_getline_failsafe(line)) {
1759 if (!parse_http_response(line)) {
1764 _state = S_reading_header;
1765 _current_field_name = string();
1766 _current_field_value = string();
1768 _got_file_size =
false;
1769 _got_transfer_file_size =
false;
1779run_reading_header() {
1780 if (parse_http_header()) {
1781 if (_bio.is_null()) {
1782 downloader_cat.info()
1783 << _NOTIFY_HTTP_CHANNEL_ID
1784 <<
"Connection lost while reading HTTP response.\n";
1785 if (_response_type == RT_http_hangup) {
1787 _status_entry._status_code = SC_lost_connection;
1788 _state = S_try_next_proxy;
1792 _response_type = RT_http_hangup;
1799 if (elapsed > get_http_timeout()) {
1801 downloader_cat.info()
1802 << _NOTIFY_HTTP_CHANNEL_ID
1803 <<
"Timeout waiting for "
1804 << _request.get_url().get_server_and_port()
1805 <<
" in run_reading_header (" << elapsed
1806 <<
" seconds elapsed).\n";
1807 _status_entry._status_code = SC_timeout;
1808 _state = S_try_next_proxy;
1813 _response_type = RT_http_complete;
1817 clear_extra_headers();
1819 _server_response_has_no_body =
1820 (get_status_code() / 100 == 1 ||
1821 get_status_code() == 204 ||
1822 get_status_code() == 304 ||
1823 _method == HTTPEnum::M_head);
1826 if (get_status_code() == 206) {
1827 string content_range = get_header_value(
"Content-Range");
1828 if (content_range.empty()) {
1829 downloader_cat.warning()
1830 << _NOTIFY_HTTP_CHANNEL_ID
1831 <<
"Got 206 response without Content-Range header!\n";
1832 _status_entry._status_code = SC_invalid_http;
1837 if (!parse_content_range(content_range)) {
1838 downloader_cat.warning()
1839 << _NOTIFY_HTTP_CHANNEL_ID
1840 <<
"Couldn't parse Content-Range: " << content_range <<
"\n";
1841 _status_entry._status_code = SC_invalid_http;
1848 _first_byte_delivered = 0;
1849 _last_byte_delivered = 0;
1851 if (downloader_cat.is_debug()) {
1852 if (_first_byte_requested != 0 || _last_byte_requested != 0 ||
1853 _first_byte_delivered != 0 || _last_byte_delivered != 0) {
1854 downloader_cat.debug()
1855 << _NOTIFY_HTTP_CHANNEL_ID
1856 <<
"Requested byte range " << _first_byte_requested
1857 <<
" to " << _last_byte_delivered
1858 <<
"; server delivers range " << _first_byte_delivered
1859 <<
" to " << _last_byte_delivered
1866 string tag = get_header_value(
"ETag");
1870 string date = get_header_value(
"Last-Modified");
1871 if (!date.empty()) {
1872 _document_spec.set_date(
HTTPDate(date));
1878 if (_server_response_has_no_body) {
1880 reset_download_to();
1883 if (!open_download_file()) {
1887 _got_expected_file_size =
false;
1888 _got_file_size =
false;
1889 _got_transfer_file_size =
false;
1891 string content_length = get_header_value(
"Content-Length");
1892 if (!content_length.empty()) {
1893 _file_size = atoi(content_length.c_str());
1894 _got_file_size =
true;
1896 }
else if (get_status_code() == 206) {
1899 _file_size = _last_byte_delivered - _first_byte_delivered + 1;
1900 _got_file_size =
true;
1902 _redirect = get_header_value(
"Location");
1907 if (_redirect.has_path() && !_redirect.has_authority()) {
1909 Filename path = _redirect.get_path();
1912 _redirect.set_path(
Filename(rel_to, path));
1918 _state = S_read_header;
1920 if (_server_response_has_no_body && will_close_connection()) {
1927 int last_status = _last_status_code;
1928 _last_status_code = get_status_code();
1930 if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
1932 string authenticate_request = get_header_value(
"Proxy-Authenticate");
1934 _client->generate_auth(_proxy,
true, authenticate_request);
1935 if (_proxy_auth !=
nullptr) {
1936 _proxy_realm = _proxy_auth->get_realm();
1937 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
1938 if (!_proxy_username.empty()) {
1939 make_request_text();
1942 _state = S_begin_body;
1948 if (get_status_code() == 401 && last_status != 401) {
1950 string authenticate_request = get_header_value(
"WWW-Authenticate");
1951 _www_auth = _client->generate_auth(_request.get_url(),
false, authenticate_request);
1952 if (_www_auth !=
nullptr) {
1953 _www_realm = _www_auth->get_realm();
1954 _www_username = _client->select_username(_request.get_url(),
false, _www_realm);
1955 if (!_www_username.empty()) {
1956 make_request_text();
1959 _state = S_begin_body;
1965 if ((get_status_code() == 300 ||
1966 get_status_code() == 301 ||
1967 get_status_code() == 302 ||
1968 get_status_code() == 303 ||
1969 get_status_code() == 307) && !get_redirect().empty()) {
1977 if (_method == HTTPEnum::M_post) {
1978 _method = HTTPEnum::M_get;
1982 if (_method == HTTPEnum::M_get || _method == HTTPEnum::M_head) {
1984 URLSpec new_url = get_redirect();
1985 if (find(_redirect_trail.begin(), _redirect_trail.end(),
1986 new_url) != _redirect_trail.end()) {
1987 downloader_cat.warning()
1988 << _NOTIFY_HTTP_CHANNEL_ID
1989 <<
"cycle detected in redirect to " << new_url <<
"\n";
1992 _redirect_trail.push_back(new_url);
1994 if (downloader_cat.is_debug()) {
1995 downloader_cat.debug()
1996 << _NOTIFY_HTTP_CHANNEL_ID
1997 <<
"following redirect to " << new_url <<
"\n";
1999 if (_request.get_url().has_username()) {
2000 new_url.
set_username(_request.get_url().get_username());
2002 reset_url(_request.get_url(), new_url);
2003 _request.set_url(new_url);
2004 _want_ssl = _request.get_url().is_ssl();
2007 make_request_text();
2010 _state = S_begin_body;
2016 if (_state == S_read_header &&
2017 ((get_status_code() / 100) == 4 || (get_status_code() / 100) == 5) &&
2018 _proxy_serves_document && _proxy_next_index < _proxies.size()) {
2025 _state = S_try_next_proxy;
2038run_start_direct_file_read() {
2039 _state = S_read_header;
2040 if (!open_download_file()) {
2060 _state = S_begin_body;
2070 if (will_close_connection()) {
2073 if (downloader_cat.is_debug()) {
2074 downloader_cat.debug()
2075 << _NOTIFY_HTTP_CHANNEL_ID
2076 <<
"resetting to begin body; server would close anyway.\n";
2082 if (_server_response_has_no_body) {
2084 _state = S_read_trailer;
2086 }
else if (get_file_size() > (
int)_skip_body_size) {
2090 if (downloader_cat.is_debug()) {
2091 downloader_cat.debug()
2092 << _NOTIFY_HTTP_CHANNEL_ID
2093 <<
"Dropping connection rather than skipping past "
2094 << get_file_size() <<
" bytes.\n";
2100 if (_body_stream ==
nullptr) {
2101 if (downloader_cat.is_debug()) {
2102 downloader_cat.debug()
2103 << _NOTIFY_HTTP_CHANNEL_ID
2104 <<
"Unable to skip body.\n";
2109 _owns_body_stream =
true;
2110 if (_state != S_reading_body) {
2111 reset_body_stream();
2129 if (will_close_connection()) {
2132 if (downloader_cat.is_debug()) {
2133 downloader_cat.debug()
2134 << _NOTIFY_HTTP_CHANNEL_ID
2135 <<
"resetting to read body; server would close anyway.\n";
2142 if (_body_stream ==
nullptr || !_owns_body_stream) {
2144 if (downloader_cat.is_debug()) {
2145 downloader_cat.debug()
2146 << _NOTIFY_HTTP_CHANNEL_ID
2147 <<
"resetting, not in skip-body mode.\n";
2154 std::getline(*_body_stream, line);
2155 while (!_body_stream->fail() && !_body_stream->eof()) {
2156 if (downloader_cat.is_spam()) {
2157 downloader_cat.spam()
2158 << _NOTIFY_HTTP_CHANNEL_ID
2159 <<
"skip: " << line <<
"\n";
2161 std::getline(*_body_stream, line);
2164 if (!_body_stream->is_closed()) {
2169 reset_body_stream();
2172 nassertr(_state != S_reading_body,
false);
2187 if (will_close_connection()) {
2190 if (downloader_cat.is_debug()) {
2191 downloader_cat.debug()
2192 << _NOTIFY_HTTP_CHANNEL_ID
2193 <<
"resetting to read body; server would close anyway.\n";
2201 if (!server_getline(line)) {
2204 while (!line.empty()) {
2205 if (!server_getline(line)) {
2210 _state = S_read_trailer;
2220 if (will_close_connection()) {
2223 if (downloader_cat.is_debug()) {
2224 downloader_cat.debug()
2225 << _NOTIFY_HTTP_CHANNEL_ID
2226 <<
"resetting to read trailer; server would close anyway.\n";
2241run_download_to_file() {
2242 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2244 bool do_throttle = _wanted_nonblocking && _download_throttle;
2246 static const size_t buffer_size = 4096;
2247 char buffer[buffer_size];
2249 size_t remaining_this_pass = buffer_size;
2251 remaining_this_pass = _bytes_per_update;
2254 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2255 size_t count = _body_stream->gcount();
2256 while (count != 0) {
2257 _download_to_stream->write(buffer, count);
2258 _bytes_downloaded += count;
2260 nassertr(count <= remaining_this_pass,
false);
2261 remaining_this_pass -= count;
2262 if (remaining_this_pass == 0) {
2268 thread_consider_yield();
2269 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2270 count = _body_stream->gcount();
2273 if (_download_to_stream->fail()) {
2274 downloader_cat.warning()
2275 << _NOTIFY_HTTP_CHANNEL_ID
2276 <<
"Error writing to " << _download_to_filename <<
"\n";
2277 _status_entry._status_code = SC_download_write_error;
2279 reset_download_to();
2283 _download_to_stream->flush();
2285 if (_body_stream->is_closed()) {
2287 reset_body_stream();
2288 close_download_stream();
2289 _started_download =
false;
2302run_download_to_ram() {
2303 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2304 nassertr(_download_to_ramfile !=
nullptr,
false);
2306 bool do_throttle = _wanted_nonblocking && _download_throttle;
2308 static const size_t buffer_size = 4096;
2309 char buffer[buffer_size];
2311 size_t remaining_this_pass = buffer_size;
2313 remaining_this_pass = _bytes_per_update;
2316 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2317 size_t count = _body_stream->gcount();
2318 while (count != 0) {
2319 _download_to_ramfile->_data += string(buffer, count);
2320 _bytes_downloaded += count;
2322 nassertr(count <= remaining_this_pass,
false);
2323 remaining_this_pass -= count;
2324 if (remaining_this_pass == 0) {
2330 thread_consider_yield();
2331 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2332 count = _body_stream->gcount();
2335 if (_body_stream->is_closed()) {
2337 reset_body_stream();
2338 close_download_stream();
2339 _started_download =
false;
2352run_download_to_stream() {
2353 nassertr(_body_stream !=
nullptr && _owns_body_stream,
false);
2355 bool do_throttle = _wanted_nonblocking && _download_throttle;
2357 static const size_t buffer_size = 4096;
2358 char buffer[buffer_size];
2360 size_t remaining_this_pass = buffer_size;
2362 remaining_this_pass = _bytes_per_update;
2365 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2366 size_t count = _body_stream->gcount();
2367 while (count != 0) {
2368 _download_to_stream->write(buffer, count);
2369 _bytes_downloaded += count;
2371 nassertr(count <= remaining_this_pass,
false);
2372 remaining_this_pass -= count;
2373 if (remaining_this_pass == 0) {
2379 thread_consider_yield();
2380 _body_stream->read(buffer, min(buffer_size, remaining_this_pass));
2381 count = _body_stream->gcount();
2384 if (_download_to_stream->fail()) {
2385 downloader_cat.warning()
2386 << _NOTIFY_HTTP_CHANNEL_ID
2387 <<
"Error writing to stream\n";
2388 _status_entry._status_code = SC_download_write_error;
2390 reset_download_to();
2394 _download_to_stream->flush();
2396 if (_body_stream->is_closed()) {
2398 reset_body_stream();
2399 close_download_stream();
2400 _started_download =
false;
2414begin_request(HTTPEnum::Method method,
const DocumentSpec &url,
2415 const string &body,
bool nonblocking,
2416 size_t first_byte,
size_t last_byte) {
2418 downloader_cat.info()
2419 << _NOTIFY_HTTP_CHANNEL_ID
2420 <<
"begin " << method <<
" " << url <<
"\n";
2422 reset_for_new_request();
2424 _wanted_nonblocking = nonblocking;
2425#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
2433 _proxy_next_index = 0;
2434 if (get_allow_proxy()) {
2435 _client->get_proxies_for_url(url.get_url(), _proxies);
2441 if (!_bio.is_null() && !_proxies.empty() && !_proxy.empty()) {
2442 Proxies::iterator pi = find(_proxies.begin(), _proxies.end(), _proxy);
2443 if (pi != _proxies.end()) {
2445 _proxies.insert(_proxies.begin(), _proxy);
2450 if (_proxy_next_index < _proxies.size()) {
2451 new_proxy = _proxies[_proxy_next_index];
2452 _proxy_next_index++;
2456 if (_proxy != new_proxy) {
2458 _proxy_auth =
nullptr;
2459 if (downloader_cat.is_debug()) {
2460 downloader_cat.debug()
2461 << _NOTIFY_HTTP_CHANNEL_ID
2462 <<
"resetting to change proxy to " << _proxy <<
"\n";
2468 if (_nonblocking != nonblocking) {
2469 _nonblocking = nonblocking;
2470 if (downloader_cat.is_debug()) {
2471 downloader_cat.debug()
2472 << _NOTIFY_HTTP_CHANNEL_ID
2473 <<
"resetting to change nonblocking state to " << _nonblocking <<
".\n";
2478 reset_url(_request.get_url(), url.get_url());
2485 _want_ssl = _request.get_url().is_ssl();
2487 _first_byte_requested = first_byte;
2488 _last_byte_requested = last_byte;
2494 if (_request.get_url().get_scheme() ==
"file") {
2499 _bio =
new BioPtr(_request.get_url());
2500 if (_bio->get_bio() !=
nullptr) {
2502 _source =
new BioStreamPtr(
new BioStream(_bio));
2503 _status_entry._status_code = 200;
2504 _state = S_start_direct_file_read;
2508 BIO_get_fp(_bio->get_bio(), &fp);
2509 if (fp !=
nullptr) {
2510 if (fseek(fp, 0, SEEK_END) == 0) {
2511 _file_size = ftell(fp);
2512 _got_file_size =
true;
2513 fseek(fp, 0, SEEK_SET);
2519 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
2520 _status_entry._status_code = SC_no_connection;
2526 if (_state == S_failure || (_state < S_read_header && _state != S_ready)) {
2527 if (downloader_cat.is_debug()) {
2528 downloader_cat.debug()
2529 << _NOTIFY_HTTP_CHANNEL_ID
2530 <<
"resetting to clear previous request.\n";
2535 if (downloader_cat.is_debug()) {
2536 downloader_cat.debug()
2537 << _NOTIFY_HTTP_CHANNEL_ID
2538 <<
"resetting old connection: "
2544 }
else if (_state == S_read_header) {
2546 _state = S_begin_body;
2550 if (_method == HTTPEnum::M_connect) {
2551 _done_state = S_ready;
2553 _done_state = S_read_header;
2565 _proxy_tunnel_now =
false;
2566 _proxy_serves_document =
false;
2568 if (!_proxy.empty()) {
2575 (get_proxy_tunnel() || _want_ssl ||
2576 _method == HTTPEnum::M_connect || _proxy.get_scheme() ==
"socks");
2580 _proxy_serves_document = !_proxy_tunnel_now;
2584 make_request_text();
2586 if (_proxy_tunnel_now) {
2589 ostringstream request;
2591 <<
"CONNECT " << _request.get_url().get_server_and_port()
2592 <<
" " << _client->get_http_version_string() <<
"\r\n";
2593 if (_client->get_http_version() >= HTTPEnum::HV_11) {
2595 <<
"Host: " << _request.get_url().get_server_and_port() <<
"\r\n";
2597 _proxy_header = request.str();
2598 make_proxy_request_text();
2601 _proxy_header = string();
2602 _proxy_request_text = string();
2612reset_for_new_request() {
2613 if (downloader_cat.is_spam()) {
2614 downloader_cat.spam()
2615 << _NOTIFY_HTTP_CHANNEL_ID
2616 <<
"reset_for_new_request.\n";
2619 reset_download_to();
2620 reset_body_stream();
2622 _last_status_code = 0;
2623 _status_entry = StatusEntry();
2625 _response_type = RT_none;
2626 _redirect_trail.clear();
2627 _bytes_downloaded = 0;
2628 _bytes_requested = 0;
2640finished_body(
bool has_trailer) {
2641 if (will_close_connection() && _download_dest == DD_none) {
2642 if (downloader_cat.is_debug()) {
2643 downloader_cat.debug()
2644 << _NOTIFY_HTTP_CHANNEL_ID
2645 <<
"resetting to finish body; server would close anyway.\n";
2651 _state = HTTPChannel::S_read_body;
2653 _state = HTTPChannel::S_read_trailer;
2667open_download_file() {
2668 _subdocument_resumes = (_subdocument_resumes && _first_byte_delivered != 0);
2670 if (_download_dest == DD_file) {
2672 _download_to_stream = vfs->
open_write_file(_download_to_filename,
false, !_subdocument_resumes);
2673 if (_download_to_stream ==
nullptr) {
2674 downloader_cat.info()
2675 << _NOTIFY_HTTP_CHANNEL_ID
2676 <<
"Could not open " << _download_to_filename <<
" for writing.\n";
2677 _status_entry._status_code = SC_download_open_error;
2683 if (_subdocument_resumes) {
2684 if (_download_dest == DD_file) {
2688 _download_to_stream->seekp(0, std::ios::end);
2689 if (_first_byte_delivered > (
size_t)_download_to_stream->tellp()) {
2690 downloader_cat.info()
2691 << _NOTIFY_HTTP_CHANNEL_ID
2692 <<
"Invalid starting position of byte " << _first_byte_delivered
2693 <<
" within " << _download_to_filename <<
" (which has "
2694 << _download_to_stream->tellp() <<
" bytes)\n";
2695 close_download_stream();
2696 _status_entry._status_code = SC_download_invalid_range;
2701 _download_to_stream->seekp(_first_byte_delivered);
2703 }
else if (_download_dest == DD_ram) {
2704 if (_first_byte_delivered > _download_to_ramfile->_data.length()) {
2705 downloader_cat.info()
2706 << _NOTIFY_HTTP_CHANNEL_ID
2707 <<
"Invalid starting position of byte " << _first_byte_delivered
2708 <<
" within Ramfile (which has "
2709 << _download_to_ramfile->_data.length() <<
" bytes)\n";
2710 close_download_stream();
2711 _status_entry._status_code = SC_download_invalid_range;
2716 if (_first_byte_delivered == 0) {
2717 _download_to_ramfile->_data = string();
2719 _download_to_ramfile->_data =
2720 _download_to_ramfile->_data.substr(0, _first_byte_delivered);
2722 }
else if (_download_dest == DD_stream) {
2726 _download_to_stream->seekp(0, std::ios::end);
2727 if (_first_byte_delivered > (
size_t)_download_to_stream->tellp()) {
2728 downloader_cat.info()
2729 << _NOTIFY_HTTP_CHANNEL_ID
2730 <<
"Invalid starting position of byte " << _first_byte_delivered
2731 <<
" within stream (which has "
2732 << _download_to_stream->tellp() <<
" bytes)\n";
2733 close_download_stream();
2734 _status_entry._status_code = SC_download_invalid_range;
2739 _download_to_stream->seekp(_first_byte_delivered);
2746 if (_download_dest == DD_file || _download_dest == DD_stream) {
2747 _download_to_stream->seekp(0);
2748 }
else if (_download_dest == DD_ram) {
2749 _download_to_ramfile->_data = string();
2763server_getline(
string &str) {
2764 nassertr(!_source.is_null(),
false);
2765 int ch = (*_source)->get();
2766 while (ch != EOF && !(*_source)->fail()) {
2771 _working_get = string();
2775 size_t p = str.length();
2776 while (p > 0 && isspace(str[p - 1])) {
2779 str = str.substr(0, p);
2781 if (downloader_cat.is_spam()) {
2782 downloader_cat.spam()
2783 << _NOTIFY_HTTP_CHANNEL_ID
2784 <<
"recv: " << str <<
"\n";
2793 _working_get += (char)ch;
2795 ch = (*_source)->get();
2808server_getline_failsafe(
string &str) {
2809 if (!server_getline(str)) {
2810 if (_bio.is_null()) {
2812 if (_response_type == RT_hangup) {
2814 _status_entry._status_code = SC_lost_connection;
2815 _state = S_try_next_proxy;
2819 _response_type = RT_hangup;
2826 if (elapsed > get_http_timeout()) {
2828 downloader_cat.info()
2829 << _NOTIFY_HTTP_CHANNEL_ID
2830 <<
"Timeout waiting for "
2831 << _request.get_url().get_server_and_port()
2832 <<
" in server_getline_failsafe (" << elapsed
2833 <<
" seconds elapsed).\n";
2834 _status_entry._status_code = SC_timeout;
2835 _state = S_try_next_proxy;
2851server_get(
string &str,
size_t num_bytes) {
2852 nassertr(!_source.is_null(),
false);
2853 int ch = (*_source)->get();
2854 while (ch != EOF && !(*_source)->fail()) {
2855 _working_get += (char)ch;
2856 if (_working_get.length() >= num_bytes) {
2858 _working_get = string();
2862 ch = (*_source)->get();
2875server_get_failsafe(
string &str,
size_t num_bytes) {
2876 if (!server_get(str, num_bytes)) {
2877 if (_bio.is_null()) {
2879 if (_response_type == RT_hangup) {
2881 _status_entry._status_code = SC_lost_connection;
2882 _state = S_try_next_proxy;
2886 _response_type = RT_hangup;
2893 if (elapsed > get_http_timeout()) {
2895 downloader_cat.info()
2896 << _NOTIFY_HTTP_CHANNEL_ID
2897 <<
"Timeout waiting for "
2898 << _request.get_url().get_server_and_port()
2899 <<
" in server_get_failsafe (" << elapsed
2900 <<
" seconds elapsed).\n";
2901 _status_entry._status_code = SC_timeout;
2902 _state = S_try_next_proxy;
2922server_send(
const string &str,
bool secret) {
2923 nassertr(str.length() > _sent_so_far,
true);
2928 size_t bytes_to_send = str.length() - _sent_so_far;
2930 BIO_write(*_bio, str.data() + _sent_so_far, bytes_to_send);
2932 if (write_count <= 0) {
2933 if (BIO_should_retry(*_bio)) {
2938 if (downloader_cat.is_debug()) {
2939 downloader_cat.debug()
2940 << _NOTIFY_HTTP_CHANNEL_ID
2941 <<
"Lost connection to server unexpectedly during write.\n";
2947 if (downloader_cat.is_spam()) {
2948 downloader_cat.spam()
2949 << _NOTIFY_HTTP_CHANNEL_ID
2950 <<
"wrote " << write_count <<
" bytes to " << _bio <<
"\n";
2954 if (!secret && downloader_cat.is_spam()) {
2955 show_send(str.substr(0, write_count));
2959 if (write_count < (
int)bytes_to_send) {
2960 _sent_so_far += write_count;
2975parse_http_response(
const string &line) {
2977 if (line.length() < 5 || line.substr(0, 5) !=
string(
"HTTP/")) {
2979 _status_entry._status_code = SC_non_http_response;
2980 if (_response_type == RT_non_http) {
2982 _state = S_try_next_proxy;
2987 if (downloader_cat.is_debug()) {
2988 downloader_cat.debug()
2989 << _NOTIFY_HTTP_CHANNEL_ID
2990 <<
"got non-HTTP response, resetting.\n";
2993 _response_type = RT_non_http;
3000 while (p < line.length() && !isspace(line[p])) {
3003 _http_version_string = line.substr(0, p);
3004 _http_version = HTTPClient::parse_http_version_string(_http_version_string);
3006 while (p < line.length() && isspace(line[p])) {
3010 while (q < line.length() && !isspace(line[q])) {
3013 string status_code = line.substr(p, q - p);
3014 _status_entry._status_code = atoi(status_code.c_str());
3016 while (q < line.length() && isspace(line[q])) {
3019 _status_entry._status_string = line.substr(q, line.length() - q);
3029parse_http_header() {
3031 if (!server_getline(line)) {
3035 while (!line.empty()) {
3036 if (isspace(line[0])) {
3039 while (p < line.length() && isspace(line[p])) {
3042 _current_field_value += line.substr(p - 1);
3046 if (!_current_field_name.empty()) {
3047 store_header_field(_current_field_name, _current_field_value);
3048 _current_field_value = string();
3051 size_t colon = line.find(
':');
3052 if (colon != string::npos) {
3053 _current_field_name =
downcase(line.substr(0, colon));
3054 size_t p = colon + 1;
3055 while (p < line.length() && isspace(line[p])) {
3058 _current_field_value = line.substr(p);
3062 if (!server_getline(line)) {
3068 if (!_current_field_name.empty()) {
3069 store_header_field(_current_field_name, _current_field_value);
3070 _current_field_value = string();
3082parse_content_range(
const string &content_range) {
3085 while (p < content_range.length() && !isspace(content_range[p])) {
3089 string units = content_range.substr(0, p);
3090 while (p < content_range.length() && isspace(content_range[p])) {
3094 if (units ==
"bytes") {
3095 const char *c_str = content_range.c_str();
3097 if (p < content_range.length() && isdigit(content_range[p])) {
3098 long first_byte = strtol(c_str + p, &endptr, 10);
3100 if (p < content_range.length() && content_range[p] ==
'-') {
3102 if (p < content_range.length() && isdigit(content_range[p])) {
3103 long last_byte = strtol(c_str + p, &endptr, 10);
3106 if (last_byte >= first_byte) {
3107 _first_byte_delivered = first_byte;
3108 _last_byte_delivered = last_byte;
3127 nassertv(!_source.is_null());
3128 if ((*_source)->is_closed()) {
3129 if (downloader_cat.is_debug()) {
3130 downloader_cat.debug()
3131 << _NOTIFY_HTTP_CHANNEL_ID
3132 <<
"Lost connection to server unexpectedly during read.\n";
3329check_preapproved_server_certificate(X509 *cert,
bool &cert_preapproved,
3330 bool &cert_name_preapproved)
const {
3331 return _client->check_preapproved_server_certificate(_request.get_url(),
3332 cert, cert_preapproved,
3333 cert_name_preapproved);
3341validate_server_name(X509 *cert) {
3342 string hostname = _request.get_url().get_server();
3344 vector_string cert_names;
3348 STACK_OF(GENERAL_NAME) *subject_alt_names =
3349 (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(cert, NID_subject_alt_name,
nullptr,
nullptr);
3350 if (subject_alt_names !=
nullptr) {
3351 int num_alts = sk_GENERAL_NAME_num(subject_alt_names);
3352 for (
int i = 0; i < num_alts; ++i) {
3354 const GENERAL_NAME *alt_name =
3355 sk_GENERAL_NAME_value(subject_alt_names, i);
3357 if (alt_name->type == GEN_DNS) {
3358 char *buffer =
nullptr;
3359 int len = ASN1_STRING_to_UTF8((
unsigned char**)&buffer,
3362 cert_names.push_back(
string(buffer, len));
3364 if (buffer !=
nullptr) {
3365 OPENSSL_free(buffer);
3371 if (cert_names.empty()) {
3374 X509_NAME *xname = X509_get_subject_name(cert);
3375 if (xname !=
nullptr) {
3376 string common_name = get_x509_name_component(xname, NID_commonName);
3377 cert_names.push_back(common_name);
3381 if (cert_names.empty()) {
3382 downloader_cat.info()
3383 << _NOTIFY_HTTP_CHANNEL_ID
3384 <<
"Server certificate from " << hostname
3385 <<
" provides no name.\n";
3389 if (downloader_cat.is_debug()) {
3390 downloader_cat.debug()
3391 << _NOTIFY_HTTP_CHANNEL_ID
3392 <<
"Server certificate from " << hostname
3393 <<
" provides name(s):";
3394 vector_string::const_iterator si;
3395 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3396 const string &cert_name = (*si);
3397 downloader_cat.debug(
false)
3398 <<
" " << cert_name;
3400 downloader_cat.debug(
false)
3406 vector_string::const_iterator si;
3407 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3408 const string &cert_name = (*si);
3410 if (match_cert_name(cert_name, hostname)) {
3415 downloader_cat.info()
3416 << _NOTIFY_HTTP_CHANNEL_ID
3417 <<
"Server certificate from " << hostname
3418 <<
" provides wrong name(s):";
3419 for (si = cert_names.begin(); si != cert_names.end(); ++si) {
3420 const string &cert_name = (*si);
3421 downloader_cat.info(
false)
3422 <<
" " << cert_name;
3424 downloader_cat.info(
false)
3435match_cert_name(
const string &cert_name,
const string &hostname) {
3441 pattern.set_case_sensitive(
false);
3442 pattern.set_nomatch_chars(
".");
3443 return pattern.matches(hostname);
3451get_x509_name_component(X509_NAME *name,
int nid) {
3452 ASN1_OBJECT *obj = OBJ_nid2obj(nid);
3454 if (obj ==
nullptr) {
3459 int i = X509_NAME_get_index_by_OBJ(name, obj, -1);
3464 ASN1_STRING *data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
3465 return string((
char *)data->data, data->length);
3475 _proxy_auth = _client->select_auth(_proxy,
true, _proxy_realm);
3476 _proxy_username = string();
3477 if (_proxy_auth !=
nullptr) {
3478 _proxy_realm = _proxy_auth->get_realm();
3479 _proxy_username = _client->select_username(_proxy,
true, _proxy_realm);
3482 if (_method == HTTPEnum::M_connect) {
3490 _www_auth = _client->select_auth(_request.get_url(),
false, _www_realm);
3491 _www_username = string();
3492 if (_www_auth !=
nullptr) {
3493 _www_realm = _www_auth->get_realm();
3494 _www_username = _client->select_username(_request.get_url(),
false, _www_realm);
3497 string request_path;
3498 if (_proxy_serves_document) {
3503 request_path = url_no_username.
get_url();
3508 request_path = _request.get_url().get_path_and_query();
3513 if (request_path.empty()) {
3517 ostringstream stream;
3520 << _method <<
" " << request_path <<
" "
3521 << _client->get_http_version_string() <<
"\r\n";
3523 if (_client->get_http_version() >= HTTPEnum::HV_11) {
3525 if (_request.get_url().has_port() && _request.get_url().is_default_port()) {
3530 string server = _request.get_url().get_server();
3531 if (server.find(
':') != string::npos) {
3532 stream <<
"Host: [" << server <<
"]";
3534 stream <<
"Host: " << server;
3537 stream <<
"Host: " << _request.get_url().get_server_and_port();
3540 if (!get_persistent_connection()) {
3542 <<
"Connection: close\r\n";
3546 if (_last_byte_requested != 0) {
3548 <<
"Range: bytes=" << _first_byte_requested <<
"-"
3549 << _last_byte_requested <<
"\r\n";
3551 }
else if (_first_byte_requested != 0) {
3553 <<
"Range: bytes=" << _first_byte_requested <<
"-\r\n";
3556 switch (_request.get_request_mode()) {
3557 case DocumentSpec::RM_any:
3559 if (_first_byte_requested != 0) {
3563 if (_request.has_tag()) {
3565 <<
"If-Range: " << _request.get_tag().get_string() <<
"\r\n";
3566 }
else if (_request.has_date()) {
3568 <<
"If-Range: " << _request.get_date().get_string() <<
"\r\n";
3573 case DocumentSpec::RM_equal:
3575 if (_request.has_tag()) {
3577 <<
"If-Match: " << _request.get_tag().get_string() <<
"\r\n";
3579 if (_request.has_date()) {
3581 <<
"If-Unmodified-Since: " << _request.get_date().get_string()
3586 case DocumentSpec::RM_newer:
3588 if (_request.has_tag()) {
3590 <<
"If-None-Match: " << _request.get_tag().get_string() <<
"\r\n";
3592 if (_request.has_date()) {
3594 <<
"If-Modified-Since: " << _request.get_date().get_string()
3599 case DocumentSpec::RM_equal_or_newer:
3601 if (_request.has_date()) {
3606 <<
"If-Modified-Since: " << (_request.get_date() - 1).get_string()
3612 switch (_request.get_cache_control()) {
3613 case DocumentSpec::CC_allow_cache:
3617 case DocumentSpec::CC_revalidate:
3620 <<
"Cache-Control: max-age=0\r\n";
3623 case DocumentSpec::CC_no_cache:
3626 <<
"Cache-Control: no-cache\r\n"
3627 <<
"Pragma: no-cache\r\n";
3631 _client->send_cookies(stream, _request.get_url());
3633 if (!_body.empty()) {
3635 <<
"Content-Type: " << _content_type <<
"\r\n"
3636 <<
"Content-Length: " << _body.length() <<
"\r\n";
3639 _header = stream.str();
3649make_proxy_request_text() {
3650 _proxy_request_text = _proxy_header;
3652 if (_proxy_auth !=
nullptr && !_proxy_username.empty()) {
3653 _proxy_request_text +=
"Proxy-Authorization: ";
3654 _proxy_request_text +=
3655 _proxy_auth->generate(HTTPEnum::M_connect, _request.get_url().get_server_and_port(),
3656 _proxy_username, _body);
3657 _proxy_request_text +=
"\r\n";
3660 _proxy_request_text +=
"\r\n";
3668make_request_text() {
3669 _request_text = _header;
3671 if (_proxy_serves_document &&
3672 _proxy_auth !=
nullptr && !_proxy_username.empty()) {
3673 _request_text +=
"Proxy-Authorization: ";
3675 _proxy_auth->generate(_method, _request.get_url().get_url(), _proxy_username, _body);
3676 _request_text +=
"\r\n";
3679 if (_www_auth !=
nullptr && !_www_username.empty()) {
3680 string authorization =
3681 _request_text +=
"Authorization: ";
3683 _www_auth->generate(_method, _request.get_url().get_path_and_query(), _www_username, _body);
3684 _request_text +=
"\r\n";
3687 _request_text += _send_extra_headers;
3688 _request_text +=
"\r\n";
3689 _request_text += _body;
3705 if (downloader_cat.is_debug()) {
3706 downloader_cat.debug()
3707 << _NOTIFY_HTTP_CHANNEL_ID
3708 <<
"resetting for new server "
3720store_header_field(
const string &field_name,
const string &field_value) {
3721 std::pair<Headers::iterator, bool> insert_result =
3722 _headers.insert(Headers::value_type(field_name, field_value));
3724 if (!insert_result.second) {
3727 Headers::iterator hi = insert_result.first;
3728 (*hi).second +=
", ";
3729 (*hi).second += field_value;
3732 if (field_name ==
"set-cookie") {
3733 _client->set_cookie(HTTPCookie(field_value, _request.get_url()));
3742show_send(
const string &message) {
3744 size_t newline = message.find(
'\n', start);
3745 while (newline != string::npos) {
3747 downloader_cat.spam()
3748 <<
"send: " << message.substr(start, newline - start - 1) <<
"\n";
3749 start = newline + 1;
3750 newline = message.find(
'\n', start);
3753 if (start < message.length()) {
3754 downloader_cat.spam()
3755 <<
"send: " << message.substr(start) <<
" (no newline)\n";
3765reset_download_to() {
3766 _started_download =
false;
3767 close_download_stream();
3768 _download_dest = DD_none;
3776close_download_stream() {
3777 if (_download_to_stream !=
nullptr) {
3778 _download_to_stream->flush();
3779 if (_download_dest == DD_file) {
3783 _download_to_ramfile =
nullptr;
3784 _download_to_stream =
nullptr;
3793 if (downloader_cat.is_spam()) {
3794 downloader_cat.spam()
3795 << _NOTIFY_HTTP_CHANNEL_ID
3796 <<
"reset_to_new.\n";
3807reset_body_stream() {
3808 if (_owns_body_stream) {
3809 if (_body_stream !=
nullptr) {
3810 close_read_body(_body_stream);
3811 nassertv(_body_stream ==
nullptr && !_owns_body_stream);
3814 _body_stream =
nullptr;
3824 reset_body_stream();
3827 _working_get = string();
3838more_useful_status_code(
int a,
int b) {
3839 if (a >= 100 && b >= 100) {
3852 int series_a = (a / 100);
3853 int series_b = (b / 100);
3856 return (series_a < series_b);
3859 if (a < 100 && b < 100) {
3867 return (a > SC_http_error_watermark);
3871 return (b < SC_http_error_watermark);
3879operator << (ostream &out, HTTPChannel::State state) {
3881 return out << (int)state;
3884 case HTTPChannel::S_new:
3885 return out <<
"new";
3887 case HTTPChannel::S_try_next_proxy:
3888 return out <<
"try_next_proxy";
3890 case HTTPChannel::S_connecting:
3891 return out <<
"connecting";
3893 case HTTPChannel::S_connecting_wait:
3894 return out <<
"connecting_wait";
3896 case HTTPChannel::S_http_proxy_ready:
3897 return out <<
"http_proxy_ready";
3899 case HTTPChannel::S_http_proxy_request_sent:
3900 return out <<
"http_proxy_request_sent";
3902 case HTTPChannel::S_http_proxy_reading_header:
3903 return out <<
"http_proxy_reading_header";
3905 case HTTPChannel::S_socks_proxy_greet:
3906 return out <<
"socks_proxy_greet";
3908 case HTTPChannel::S_socks_proxy_greet_reply:
3909 return out <<
"socks_proxy_greet_reply";
3911 case HTTPChannel::S_socks_proxy_connect:
3912 return out <<
"socks_proxy_connect";
3914 case HTTPChannel::S_socks_proxy_connect_reply:
3915 return out <<
"socks_proxy_connect_reply";
3917 case HTTPChannel::S_setup_ssl:
3918 return out <<
"setup_ssl";
3920 case HTTPChannel::S_ssl_handshake:
3921 return out <<
"ssl_handshake";
3923 case HTTPChannel::S_ready:
3924 return out <<
"ready";
3926 case HTTPChannel::S_request_sent:
3927 return out <<
"request_sent";
3929 case HTTPChannel::S_reading_header:
3930 return out <<
"reading_header";
3932 case HTTPChannel::S_start_direct_file_read:
3933 return out <<
"start_direct_file_read";
3935 case HTTPChannel::S_read_header:
3936 return out <<
"read_header";
3938 case HTTPChannel::S_begin_body:
3939 return out <<
"begin_body";
3941 case HTTPChannel::S_reading_body:
3942 return out <<
"reading_body";
3944 case HTTPChannel::S_read_body:
3945 return out <<
"read_body";
3947 case HTTPChannel::S_read_trailer:
3948 return out <<
"read_trailer";
3950 case HTTPChannel::S_failure:
3951 return out <<
"failure";
3954 return out <<
"invalid state(" << (int)state <<
")";
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as a floating- point type.
A descriptor that refers to a particular version of a document.
The name of a file, such as a texture file or an Egg file.
void set_binary()
Indicates that the filename represents a binary file.
bool is_local() const
Returns true if the filename is local, e.g.
std::string get_dirname() const
Returns the directory part of the filename.
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
A container for an HTTP-legal time/date indication.
A container for an "entity tag" from an HTTP server.
An in-memory buffer specifically designed for downloading files to memory.
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
TypeHandle is the identifier used to differentiate C++ class types.
A container for a URL, e.g.
const std::string & get_url() const
Returns the complete URL specification.
get_server
Returns the server name specified by the URL, if any.
get_path
Returns the path specified by the URL, or "/" if no path is specified.
get_server_and_port
Returns a string consisting of the server name, followed by a colon, followed by the port number.
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
get_port
Returns the port number specified by the URL, or the default port if not specified.
set_username
Replaces the username part of the URL specification.
get_authority
Returns the authority specified by the URL (this includes username, server, and/or port),...
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
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,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.