diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index 38610791..6769ebb9 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -8,7 +8,8 @@ #include #include #include -#include + +#include #ifdef _WIN32 # include "winsock2.h" @@ -33,6 +34,11 @@ namespace wvcdm { namespace { +// Number of attempts to identify an Internet host and a service should the +// host's nameserver be temporarily unavailable. See getaddrinfo(3) for +// more info. +constexpr size_t kMaxNameserverAttempts = 2; + // Helper function to tokenize a string. This makes it easier to avoid silly // parsing bugs that creep in easily when each part of the string is parsed // with its own piece of code. @@ -71,6 +77,17 @@ bool IsRetryableSslError(int ssl_error) { ssl_error != SSL_ERROR_SSL; } +// Ensures that the SSL library is only initialized once. +void InitSslLibrary() { + static bool ssl_initialized = false; + static std::mutex ssl_init_mutex; + std::lock_guard guard(ssl_init_mutex); + if (!ssl_initialized) { + SSL_library_init(); + ssl_initialized = true; + } +} + #if 0 // unused, may be useful for debugging SSL-related issues. void ShowServerCertificate(const SSL* ssl) { @@ -211,7 +228,7 @@ HttpSocket::HttpSocket(const std::string& url) : socket_fd_(-1), ssl_(nullptr), ssl_ctx_(nullptr) { valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_, &resource_path_); - SSL_library_init(); + InitSslLibrary(); } HttpSocket::~HttpSocket() { CloseSocket(); } @@ -237,6 +254,12 @@ void HttpSocket::CloseSocket() { bool HttpSocket::Connect(int timeout_in_ms) { if (!valid_url_) { + LOGE("URL is invalid"); + return false; + } + + if (socket_fd_ != -1) { + LOGE("Socket already connected"); return false; } @@ -259,24 +282,42 @@ bool HttpSocket::Connect(int timeout_in_ms) { hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; - struct addrinfo* addr_info = nullptr; - int ret = - getaddrinfo(domain_name_.c_str(), port_.c_str(), &hints, &addr_info); + + int ret = EAI_AGAIN; + for (size_t attempt = 1; + attempt <= kMaxNameserverAttempts && ret == EAI_AGAIN; ++attempt) { + if (attempt > 1) { + LOGW( + "Nameserver is temporarily unavailable, waiting to try again: " + "attempt = %zu", + attempt); + sleep(1); + } + ret = getaddrinfo(domain_name_.c_str(), port_.c_str(), &hints, &addr_info); + } + if (ret != 0) { - LOGE("getaddrinfo failed, errno = %d", ret); + if (ret == EAI_SYSTEM) { + // EAI_SYSTEM implies an underlying system issue. Error is + // specified by |errno|. + LOGE("getaddrinfo failed due to system error: errno = %d", GetError()); + } else { + // Error is specified by return value. + LOGE("getaddrinfo failed: ret = %d", ret); + } return false; } - // get a socket + // Open a socket. socket_fd_ = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol); if (socket_fd_ < 0) { - LOGE("cannot open socket, errno = %d", GetError()); + LOGE("Cannot open socket: errno = %d", GetError()); return false; } - // set the socket in non-blocking mode + // Set the socket in non-blocking mode. #ifdef _WIN32 u_long mode = 1; // Non-blocking mode. if (ioctlsocket(socket_fd_, FIONBIO, &mode) != 0) { @@ -285,7 +326,7 @@ bool HttpSocket::Connect(int timeout_in_ms) { return false; } #else - int original_flags = fcntl(socket_fd_, F_GETFL, 0); + const int original_flags = fcntl(socket_fd_, F_GETFL, 0); if (original_flags == -1) { LOGE("fcntl error, errno = %d", errno); CloseSocket(); @@ -301,9 +342,10 @@ bool HttpSocket::Connect(int timeout_in_ms) { // connect to the server ret = connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen); freeaddrinfo(addr_info); + addr_info = nullptr; if (ret == 0) { - // connected right away. + // Connected right away. } else { if (GetError() != ERROR_ASYNC_COMPLETE) { // failed right away. @@ -336,6 +378,8 @@ bool HttpSocket::Connect(int timeout_in_ms) { return false; } + // |BIO_NOCLOSE| prevents closing the socket from being closed when + // the BIO is freed. BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE); if (!a_bio) { LOGE("BIO_new_socket error"); @@ -354,9 +398,9 @@ bool HttpSocket::Connect(int timeout_in_ms) { CloseSocket(); return false; } - bool for_read = ssl_err == SSL_ERROR_WANT_READ; + const bool for_read = (ssl_err == SSL_ERROR_WANT_READ); if (!SocketWait(socket_fd_, for_read, timeout_in_ms)) { - LOGE("cannot connect to %s", domain_name_.c_str()); + LOGE("Cannot connect securely to %s", domain_name_.c_str()); CloseSocket(); return false; } diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 456eba49..2f89e07a 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -87,8 +87,8 @@ void UrlRequest::Reconnect() { if (socket_.Connect(kConnectTimeoutMs)) { is_connected_ = true; } else { - LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(), - socket_.port()); + LOGE("Failed to connect: url = %s, port = %d, attempt = %u", + socket_.domain_name().c_str(), socket_.port(), i); } } } @@ -148,7 +148,7 @@ bool UrlRequest::PostRequestWithPath(const std::string& path, // buffer to store length of data as a string char data_size_buffer[32] = {0}; - snprintf(data_size_buffer, sizeof(data_size_buffer), "%zd", data.size()); + snprintf(data_size_buffer, sizeof(data_size_buffer), "%zu", data.size()); request.append("Content-Length: "); request.append(data_size_buffer); // appends size of data @@ -158,7 +158,8 @@ bool UrlRequest::PostRequestWithPath(const std::string& path, request.append(data); - int ret = socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs); + const int ret = + socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs); LOGV("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str()); return ret != -1; }