diff --git a/panda/src/downloader/config_downloader.cxx b/panda/src/downloader/config_downloader.cxx index 55754154ef..f072d33500 100644 --- a/panda/src/downloader/config_downloader.cxx +++ b/panda/src/downloader/config_downloader.cxx @@ -26,6 +26,9 @@ const float downloader_frequency = const float downloader_bandwidth = config_downloader.GetFloat("downloader-bandwidth", 3600.0); +const int downloader_timeout = + config_downloader.GetInt("downloader-timeout", 2); + const int decompressor_buffer_size = config_downloader.GetInt("decompressor-buffer-size", 4096); diff --git a/panda/src/downloader/config_downloader.h b/panda/src/downloader/config_downloader.h index cbd505e6e5..273175819e 100644 --- a/panda/src/downloader/config_downloader.h +++ b/panda/src/downloader/config_downloader.h @@ -14,6 +14,7 @@ NotifyCategoryDecl(downloader, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); extern const int downloader_buffer_size; extern const float downloader_frequency; extern const float downloader_bandwidth; +extern const int downloader_timeout; extern const int decompressor_buffer_size; extern const float decompressor_frequency; diff --git a/panda/src/downloader/downloader.cxx b/panda/src/downloader/downloader.cxx index 67b19415ea..ca63c0bc33 100644 --- a/panda/src/downloader/downloader.cxx +++ b/panda/src/downloader/downloader.cxx @@ -178,9 +178,6 @@ connect_to_server(void) { _connected = false; } - // Make socket non blocking - //if (setsockopt(_socket, SOL_SOCKET, - return _connected; } @@ -336,6 +333,94 @@ process_request() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: Downloader::safe_send +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool Downloader:: +safe_send(int socket, const char *data, int length, long timeout) { + if (length == 0) { + downloader_cat.error() + << "Downloader::safe_send() - requested 0 length send!" << endl; + return false; + } + int bytes = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + fd_set wset; + FD_ZERO(&wset); + while (bytes < length) { + FD_SET(socket, &wset); + int sret = select(socket + 1, NULL, &wset, NULL, &tv); + if (sret == 0) { + downloader_cat.error() + << "Downloader::safe_send() - select timed out after: " + << timeout << " seconds" << endl; + return false; + } else if (sret == -1) { + downloader_cat.error() + << "Downloader::safe_send() - error: " << strerror(errno) << endl; + return false; + } + int ret = send(socket, data, length, 0); + if (ret > 0) + bytes += ret; + else { + downloader_cat.error() + << "Downloader::safe_send() - error: " << strerror(errno) << endl; + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::safe_receive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int Downloader:: +safe_receive(int socket, char *data, int length, long timeout) { + if (length == 0) { + downloader_cat.error() + << "Downloader::safe_receive() - requested 0 length receive!" << endl; + return 0; + } + int bytes = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + fd_set rset; + FD_ZERO(&rset); + while (bytes < length) { + FD_SET(socket, &rset); + int sret = select(socket + 1, &rset, NULL, NULL, &tv); + if (sret == 0) { + downloader_cat.error() + << "Downloader::safe_receive() - select timed out after: " + << timeout << " seconds" << endl; + return bytes; + } else if (sret == -1) { + downloader_cat.error() + << "Downloader::safe_receive() - error: " << strerror(errno) << endl; + return bytes; + } + int ret = recv(socket, data, length, 0); + if (ret > 0) + bytes += ret; + else if (ret == 0) + return 0; + else { + downloader_cat.error() + << "Downloader::safe_receive() - error: " << strerror(errno) << endl; + return bytes; + } + } + return bytes; +} + //////////////////////////////////////////////////////////////////// // Function: Downloader::download // Access: Private @@ -383,27 +468,17 @@ download(const string &file_name, Filename file_dest, int outlen = request.size(); downloader_cat.debug() << "Downloader::download() - Sending request:\n" << request << endl; - if (send(_socket, request.c_str(), outlen, 0) != outlen) { - downloader_cat.error() - << "Downloader::download() - Error sending HTTP request: " - << request << "\n" << "error: " << strerror(errno) << endl; + if (safe_send(_socket, request.c_str(), outlen, + (long)downloader_timeout) == false) return false; - } // Loop at the requested frequency until the download completes DownloadStatus status(_buffer->_buffer, event_name, first_byte, last_byte, total_bytes, partial_content, id); bool got_any_data = false; - // MPG - Get *safe* socket read code from Roger - // send() can write fewer than the requested # of bytes, so we need to - // loop on send. - // recv() can block forever, so we probably want to use a non-blocking - // socket and then check for EWOULDBLOCK error (which wouldn't be an - // error for us in this case - we should just go back to sleep for - // a while). for (;;) { - if (_download_enabled) { + if (_download_enabled) { // Ensure that these don't change while we're computing read_size _bandwidth_frequency_lock.lock(); int read_size = (int)(_bandwidth * _frequency); @@ -420,7 +495,8 @@ download(const string &file_name, Filename file_dest, } // Grab the next chunk - int ans = recv(_socket, status._next_in, read_size, 0); + int ans = safe_receive(_socket, status._next_in, read_size, + (long)downloader_timeout); if (ans < 0) { downloader_cat.error() @@ -514,7 +590,7 @@ parse_header(DownloadStatus &status) { if (status._first_byte == 0) client_download_bytes += 1; if (client_download_bytes != server_download_bytes) { - downloader_cat.error() + downloader_cat.error() << "Downloader::parse_header() - server size = " << server_download_bytes << ", client size = " << client_download_bytes << " (" @@ -544,6 +620,12 @@ parse_header(DownloadStatus &status) { p = nl + 2; } + if (status._total_bytes == 0) { + downloader_cat.error() + << "Downloader::parse_header() - Total bytes == 0!" << endl; + return false; + } + return true; } diff --git a/panda/src/downloader/downloader.h b/panda/src/downloader/downloader.h index 027d3bdc6d..6fa0c66925 100644 --- a/panda/src/downloader/downloader.h +++ b/panda/src/downloader/downloader.h @@ -83,6 +83,8 @@ private: bool parse_header(DownloadStatus &status); bool write_to_disk(DownloadStatus &status); bool connect_to_server(void); + bool safe_send(int socket, const char *data, int length, long timeout); + int safe_receive(int socket, char *data, int length, long timeout); typedef TokenBoard DownloaderTokenBoard; DownloaderTokenBoard *_token_board; diff --git a/panda/src/testbed/Sources.pp b/panda/src/testbed/Sources.pp index 471bb646d2..84c26089c2 100644 --- a/panda/src/testbed/Sources.pp +++ b/panda/src/testbed/Sources.pp @@ -29,6 +29,9 @@ #define SOURCES \ downloader_test.cxx + #define USE_ZLIB yes + #define TARGET_IF_ZLIB yes + #end test_bin_target #begin test_bin_target