1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Refactor address list lookup to include all returned addresses.

sckHostLookup() only returned the first address record returned from getaddrinfo(). The new AddressInfo object provides a full list of values returned from getaddrinfo(). Freeing the list is also handled by the object so there is no longer a need for FINALLY blocks to ensure the list is freed.

Add the selected address to the client/server names for debugging purposes.

This code does not attempt to connect to multiple addresses. It just lays the groundwork for a future commit to do so.
This commit is contained in:
David Steele 2023-09-10 10:36:58 -04:00 committed by GitHub
parent f42d927d2d
commit edbd520c81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 383 additions and 85 deletions

View File

@ -133,6 +133,7 @@ SRCS = \
common/io/limitRead.c \
common/io/server.c \
common/io/session.c \
common/io/socket/address.c \
common/io/socket/client.c \
common/io/socket/common.c \
common/io/socket/server.c \

View File

@ -0,0 +1,187 @@
/***********************************************************************************************************************************
Address Info
***********************************************************************************************************************************/
#include "build.auto.h"
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include "common/debug.h"
#include "common/io/socket/address.h"
#include "common/log.h"
#include "common/memContext.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct AddressInfo
{
AddressInfoPub pub; // Publicly accessible variables
};
/***********************************************************************************************************************************
Free addrinfo linked list allocated by getaddrinfo()
***********************************************************************************************************************************/
static void
addrInfoFreeResource(THIS_VOID)
{
THIS(AddressInfo);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(ADDRESS_INFO, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
freeaddrinfo(*(struct addrinfo **)lstGet(this->pub.list, 0));
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
FN_EXTERN AddressInfo *
addrInfoNew(const String *const host, unsigned int port)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_END();
ASSERT(host != NULL);
ASSERT(port != 0);
OBJ_NEW_BEGIN(AddressInfo, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{
*this = (AddressInfo)
{
.pub =
{
.host = strDup(host),
.port = port,
.list = lstNewP(sizeof(struct addrinfo *)),
},
};
MEM_CONTEXT_TEMP_BEGIN()
{
// Set hints that narrow the type of address we are looking for -- we'll take ipv4 or ipv6
struct addrinfo hints = (struct addrinfo)
{
.ai_family = AF_UNSPEC,
.ai_flags = AI_PASSIVE | AI_NUMERICSERV,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
// Convert the port to a zero-terminated string for use with getaddrinfo()
char portZ[CVT_BASE10_BUFFER_SIZE];
cvtUIntToZ(port, portZ, sizeof(portZ));
// Do the lookup
struct addrinfo *result;
int error;
if ((error = getaddrinfo(strZ(host), portZ, &hints, &result)) != 0)
THROW_FMT(HostConnectError, "unable to get address for '%s': [%d] %s", strZ(host), error, gai_strerror(error));
// Set free callback to ensure address info is freed
memContextCallbackSet(objMemContext(this), addrInfoFreeResource, this);
// Convert address linked list to list
lstAdd(this->pub.list, &result);
while (result->ai_next != NULL)
{
lstAdd(this->pub.list, &result->ai_next);
result = result->ai_next;
}
}
MEM_CONTEXT_TEMP_END();
}
OBJ_NEW_END();
FUNCTION_LOG_RETURN(ADDRESS_INFO, this);
}
/***********************************************************************************************************************************
Convert address to a zero-terminated string
***********************************************************************************************************************************/
#define ADDR_INFO_STR_BUFFER_SIZE 48
static void
addrInfoToZ(const struct addrinfo *const addrInfo, char *const address, const size_t addressSize)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, addrInfo);
FUNCTION_TEST_PARAM_P(CHAR, address);
FUNCTION_TEST_PARAM(SIZE, addressSize);
FUNCTION_TEST_END();
if (getnameinfo(addrInfo->ai_addr, addrInfo->ai_addrlen, address, (socklen_t)addressSize, 0, 0, NI_NUMERICHOST) != 0)
{
strncpy(address, "invalid", addressSize);
address[addressSize - 1] = '\0';
}
FUNCTION_TEST_RETURN_VOID();
}
/**********************************************************************************************************************************/
FN_EXTERN String *
addrInfoToName(const String *const host, const unsigned int port, const struct addrinfo *const addrInfo)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, host);
FUNCTION_TEST_PARAM(UINT, port);
FUNCTION_TEST_PARAM_P(VOID, addrInfo);
FUNCTION_TEST_END();
String *const result = strCatFmt(strNew(), "%s:%u", strZ(host), port);
String *const address = addrInfoToStr(addrInfo);
if (!strEq(host, address))
strCatFmt(result, " (%s)", strZ(address));
strFree(address);
FUNCTION_TEST_RETURN(STRING, result);
}
/**********************************************************************************************************************************/
FN_EXTERN String *
addrInfoToStr(const struct addrinfo *const addrInfo)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, addrInfo);
FUNCTION_TEST_END();
char address[ADDR_INFO_STR_BUFFER_SIZE];
addrInfoToZ(addrInfo, address, sizeof(address));
FUNCTION_TEST_RETURN(STRING, strNewZ(address));
}
/**********************************************************************************************************************************/
FN_EXTERN void
addrInfoToLog(const AddressInfo *const this, StringStatic *const debugLog)
{
char address[48];
strStcFmt(debugLog, "{host: ");
strToLog(addrInfoHost(this), debugLog);
strStcFmt(debugLog, ", port: %u, list: [", addrInfoPort(this));
for (unsigned int listIdx = 0; listIdx < addrInfoSize(this); listIdx++)
{
const struct addrinfo *const addrInfo = addrInfoGet(this, listIdx);
if (listIdx != 0)
strStcCat(debugLog, ", ");
addrInfoToZ(addrInfo, address, sizeof(address));
strStcCat(debugLog, address);
}
strStcCat(debugLog, "]}");
}

View File

@ -0,0 +1,91 @@
/***********************************************************************************************************************************
Address Info
Get address info for a host/address. If the input is a host then there may be multiple addresses that map to the host.
***********************************************************************************************************************************/
#ifndef COMMON_IO_SOCKET_ADDRESSINFO_H
#define COMMON_IO_SOCKET_ADDRESSINFO_H
#include <netdb.h>
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
typedef struct AddressInfo AddressInfo;
#include "common/type/list.h"
#include "common/type/object.h"
#include "common/type/string.h"
/***********************************************************************************************************************************
Constructor
***********************************************************************************************************************************/
FN_EXTERN AddressInfo *addrInfoNew(const String *const host, unsigned int port);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct AddressInfoPub
{
const String *host; // Host for address info lookup
unsigned int port; // Port for address info lookup
List *list; // List of addresses for the host
} AddressInfoPub;
// Get address
FN_INLINE_ALWAYS const struct addrinfo *
addrInfoGet(const AddressInfo *const this, const unsigned int index)
{
return *(const struct addrinfo **)lstGet(THIS_PUB(AddressInfo)->list, index);
}
// Get lookup host
FN_INLINE_ALWAYS const String *
addrInfoHost(const AddressInfo *const this)
{
return THIS_PUB(AddressInfo)->host;
}
// Get lookup port
FN_INLINE_ALWAYS unsigned int
addrInfoPort(const AddressInfo *const this)
{
return THIS_PUB(AddressInfo)->port;
}
// Size of address list
FN_INLINE_ALWAYS unsigned int
addrInfoSize(const AddressInfo *const this)
{
return lstSize(THIS_PUB(AddressInfo)->list);
}
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
FN_INLINE_ALWAYS void
addrInfoFree(AddressInfo *const this)
{
objFree(this);
}
/***********************************************************************************************************************************
Helper functions
***********************************************************************************************************************************/
// Convert address info to string
FN_EXTERN String *addrInfoToStr(const struct addrinfo *addrInfo);
// Generate name for the host/port/address combination (address is omitted if it equals host)
FN_EXTERN String *addrInfoToName(const String *host, unsigned int port, const struct addrinfo *addrInfo);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
FN_EXTERN void addrInfoToLog(const AddressInfo *this, StringStatic *debugLog);
#define FUNCTION_LOG_ADDRESS_INFO_TYPE \
AddressInfo *
#define FUNCTION_LOG_ADDRESS_INFO_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_OBJECT_FORMAT(value, addrInfoToLog, buffer, bufferSize)
#endif

View File

@ -10,6 +10,7 @@ Socket Client
#include "common/debug.h"
#include "common/io/client.h"
#include "common/io/socket/address.h"
#include "common/io/socket/client.h"
#include "common/io/socket/common.h"
#include "common/io/socket/session.h"
@ -83,22 +84,14 @@ sckClientOpen(THIS_VOID)
TRY_BEGIN()
{
// Get an address for the host. We are only going to try the first address returned.
struct addrinfo *addressFound = sckHostLookup(this->host, this->port);
const struct addrinfo *const addressFound = addrInfoGet(addrInfoNew(this->host, this->port), 0);
// Connect to the host
TRY_BEGIN()
{
fd = socket(addressFound->ai_family, addressFound->ai_socktype, addressFound->ai_protocol);
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
fd = socket(addressFound->ai_family, addressFound->ai_socktype, addressFound->ai_protocol);
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
sckOptionSet(fd);
sckConnect(fd, this->host, this->port, addressFound, waitRemaining(wait));
}
FINALLY()
{
freeaddrinfo(addressFound);
}
TRY_END();
sckOptionSet(fd);
sckConnect(fd, this->host, this->port, addressFound, waitRemaining(wait));
// Create the session
MEM_CONTEXT_PRIOR_BEGIN()
@ -106,6 +99,10 @@ sckClientOpen(THIS_VOID)
result = sckSessionNew(ioSessionRoleClient, fd, this->host, this->port, this->timeoutSession);
}
MEM_CONTEXT_PRIOR_END();
// Update client name to include address
strTrunc(this->name);
strCat(this->name, addrInfoToName(this->host, this->port, addressFound));
}
CATCH_ANY()
{
@ -176,7 +173,7 @@ sckClientNew(const String *const host, const unsigned int port, const TimeMSec t
{
.host = strDup(host),
.port = port,
.name = strNewFmt("%s:%u", strZ(host), port),
.name = strCatFmt(strNew(), "%s:%u", strZ(host), port),
.timeoutConnect = timeoutConnect,
.timeoutSession = timeoutSession,
};

View File

@ -10,6 +10,7 @@ Socket Common Functions
#include "common/debug.h"
#include "common/io/fd.h"
#include "common/io/socket/address.h"
#include "common/io/socket/common.h"
#include "common/log.h"
#include "common/wait.h"
@ -55,41 +56,6 @@ sckInit(bool block, bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle,
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
FN_EXTERN struct addrinfo *
sckHostLookup(const String *const host, unsigned int port)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_END();
ASSERT(host != NULL);
ASSERT(port != 0);
// Set hints that narrow the type of address we are looking for -- we'll take ipv4 or ipv6
struct addrinfo hints = (struct addrinfo)
{
.ai_family = AF_UNSPEC,
.ai_flags = AI_PASSIVE,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
// Convert the port to a zero-terminated string for use with getaddrinfo()
char portZ[CVT_BASE10_BUFFER_SIZE];
cvtUIntToZ(port, portZ, sizeof(portZ));
// Do the lookup
struct addrinfo *result;
int error;
if ((error = getaddrinfo(strZ(host), portZ, &hints, &result)) != 0)
THROW_FMT(HostConnectError, "unable to get address for '%s': [%d] %s", strZ(host), error, gai_strerror(error));
FUNCTION_LOG_RETURN_P(VOID, result);
}
/**********************************************************************************************************************************/
FN_EXTERN void
sckOptionSet(int fd)
@ -204,7 +170,7 @@ sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo
{
// Wait for write-ready
if (!fdReadyWrite(fd, timeout))
THROW_FMT(HostConnectError, "timeout connecting to '%s:%u'", strZ(host), port);
THROW_FMT(HostConnectError, "timeout connecting to '%s'", strZ(addrInfoToName(host, port, hostAddress)));
// Check for success or error. If the connection was successful this will set errNo to 0.
socklen_t errNoLen = sizeof(errNo);
@ -215,7 +181,10 @@ sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo
// Throw error if it is still set
if (errNo != 0)
THROW_SYS_ERROR_CODE_FMT(errNo, HostConnectError, "unable to connect to '%s:%u'", strZ(host), port);
{
THROW_SYS_ERROR_CODE_FMT(
errNo, HostConnectError, "unable to connect to '%s'", strZ(addrInfoToName(host, port, hostAddress)));
}
}
FUNCTION_LOG_RETURN_VOID();

View File

@ -15,9 +15,6 @@ Functions
// Initialize settings for socket connections (some are used only for TCP)
FN_EXTERN void sckInit(bool block, bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle, int tcpKeepAliveInterval);
// Get address info for a host/address. The caller is responsible for freeing addrinfo.
FN_EXTERN struct addrinfo *sckHostLookup(const String *const host, unsigned int port);
// Set options on a socket
FN_EXTERN void sckOptionSet(int fd);

View File

@ -10,6 +10,7 @@ Socket Server
#include "common/debug.h"
#include "common/io/server.h"
#include "common/io/socket/address.h"
#include "common/io/socket/common.h"
#include "common/io/socket/server.h"
#include "common/io/socket/session.h"
@ -159,39 +160,35 @@ sckServerNew(const String *const address, const unsigned int port, const TimeMSe
{
.address = strDup(address),
.port = port,
.name = strNewFmt("%s:%u", strZ(address), port),
.name = strCatFmt(strNew(), "%s:%u", strZ(address), port),
.timeout = timeout,
};
// Lookup address
struct addrinfo *const addressFound = sckHostLookup(this->address, this->port);
const struct addrinfo *const addressFound = addrInfoGet(addrInfoNew(this->address, this->port), 0);
TRY_BEGIN()
{
// Create socket
THROW_ON_SYS_ERROR(
(this->socket = socket(addressFound->ai_family, SOCK_STREAM, 0)) == -1, FileOpenError, "unable to create socket");
// Create socket
THROW_ON_SYS_ERROR(
(this->socket = socket(addressFound->ai_family, SOCK_STREAM, 0)) == -1, FileOpenError, "unable to create socket");
// Set the address as reusable so we can bind again quickly after a restart or crash
int reuseAddr = 1;
// Set the address as reusable so we can bind again quickly after a restart or crash
int reuseAddr = 1;
THROW_ON_SYS_ERROR(
setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1, ProtocolError,
"unable to set SO_REUSEADDR");
THROW_ON_SYS_ERROR(
setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1, ProtocolError,
"unable to set SO_REUSEADDR");
// Ensure file descriptor is closed
memContextCallbackSet(objMemContext(this), sckServerFreeResource, this);
// Ensure file descriptor is closed
memContextCallbackSet(objMemContext(this), sckServerFreeResource, this);
// Bind the address
THROW_ON_SYS_ERROR(
bind(this->socket, addressFound->ai_addr, addressFound->ai_addrlen) == -1, FileOpenError,
"unable to bind socket");
}
FINALLY()
{
freeaddrinfo(addressFound);
}
TRY_END();
// Update server name to include address
strTrunc(this->name);
strCat(this->name, addrInfoToName(this->address, this->port, addressFound));
// Bind the address
THROW_ON_SYS_ERROR(
bind(this->socket, addressFound->ai_addr, addressFound->ai_addrlen) == -1, FileOpenError,
"unable to bind socket");
// Listen for client connections. It might be a good idea to make the backlog configurable but this value seems OK for now.
THROW_ON_SYS_ERROR(listen(this->socket, 100) == -1, FileOpenError, "unable to listen on socket");

View File

@ -199,6 +199,7 @@ src_pgbackrest = [
'common/io/limitRead.c',
'common/io/server.c',
'common/io/session.c',
'common/io/socket/address.c',
'common/io/socket/client.c',
'common/io/socket/common.c',
'common/io/socket/server.c',

View File

@ -362,7 +362,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: io-tls
total: 5
total: 6
feature: SOCKET
harness: server
harness:
@ -380,6 +380,7 @@ unit:
- common/io/tls/common
- common/io/tls/server
- common/io/tls/session
- common/io/socket/address
- common/io/socket/client
- common/io/socket/common
- common/io/socket/server

View File

@ -306,7 +306,7 @@ testRun(void)
TEST_ERROR_FMT(
httpRequestResponse(httpRequestNewP(client, STRDEF("GET"), STRDEF("/")), false), HostConnectError,
"unable to connect to 'localhost:%u': [111] Connection refused",
"unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
hrnServerPort(0));
HRN_FORK_BEGIN()

View File

@ -151,6 +151,60 @@ testRun(void)
THROW_ON_SYS_ERROR_FMT(
chmod(HRN_SERVER_CLIENT_KEY, 0600) == -1, FileModeError, "unable to set mode on " HRN_SERVER_CLIENT_KEY);
// *****************************************************************************************************************************
if (testBegin("AddressInfo"))
{
#ifdef TEST_CONTAINER_REQUIRED
HRN_SYSTEM("echo \"127.0.0.1 test-addr-loop.pgbackrest.org\" | sudo tee -a /etc/hosts > /dev/null");
HRN_SYSTEM("echo \"::1 test-addr-loop.pgbackrest.org\" | sudo tee -a /etc/hosts > /dev/null");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("lookup address info");
AddressInfo *addrInfo = NULL;
TEST_ASSIGN(addrInfo, addrInfoNew(STRDEF("test-addr-loop.pgbackrest.org"), 443), "addr list");
TEST_RESULT_STR_Z(addrInfoHost(addrInfo), "test-addr-loop.pgbackrest.org", "check host");
TEST_RESULT_UINT(addrInfoPort(addrInfo), 443, "check port");
TEST_RESULT_UINT(addrInfoSize(addrInfo), 2, "check size");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("addrInfoToLog");
char logBuf[STACK_TRACE_PARAM_MAX];
TEST_RESULT_VOID(FUNCTION_LOG_OBJECT_FORMAT(addrInfo, addrInfoToLog, logBuf, sizeof(logBuf)), "addrInfoToLog");
TEST_RESULT_Z(
logBuf,
zNewFmt(
"{host: {\"test-addr-loop.pgbackrest.org\"}, port: 443, list: [%s, %s]}",
strZ(addrInfoToStr(addrInfoGet(addrInfo, 0))), strZ(addrInfoToStr(addrInfoGet(addrInfo, 1)))),
"check log");
// Munge address so it is invalid
(*(struct addrinfo **)lstGet(addrInfo->pub.list, 0))->ai_addr = NULL;
TEST_RESULT_VOID(FUNCTION_LOG_OBJECT_FORMAT(addrInfo, addrInfoToLog, logBuf, sizeof(logBuf)), "addrInfoToLog");
TEST_RESULT_Z(
logBuf,
zNewFmt(
"{host: {\"test-addr-loop.pgbackrest.org\"}, port: 443, list: [invalid, %s]}",
strZ(addrInfoToStr(addrInfoGet(addrInfo, 1)))),
"check log");
TEST_RESULT_STR_Z(addrInfoToStr(addrInfoGet(addrInfo, 0)), "invalid", "check invalid");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("free");
TEST_RESULT_VOID(addrInfoFree(addrInfo), "free");
#endif
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("addrInfoToStr (invalid)");
struct addrinfo addrInfoInvalid = {0};
TEST_RESULT_STR_Z(addrInfoToStr(&addrInfoInvalid), "invalid", "check invalid");
}
// *****************************************************************************************************************************
if (testBegin("Socket Common"))
{
@ -314,7 +368,7 @@ testRun(void)
TEST_ASSIGN(client, sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), "new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
hrnServerPort(0));
// This address should not be in use in a test environment -- if it is the test will fail
@ -397,7 +451,7 @@ testRun(void)
client, tlsClientNewP(sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), STRDEF("X"), 100, 100, true),
"new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
hrnServerPort(0));
// -------------------------------------------------------------------------------------------------------------------------
@ -577,7 +631,8 @@ testRun(void)
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true,
.caPath = STRDEF("/bogus"))),
CryptoError,
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
"unable to verify certificate presented by 'localhost:%u (127.0.0.1)': [20] unable to get local issuer"
" certificate",
hrnServerPort(0));
// -----------------------------------------------------------------------------------------------------------------
@ -632,7 +687,8 @@ testRun(void)
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true,
.caFile = STRDEF(HRN_SERVER_CERT))),
CryptoError,
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
"unable to verify certificate presented by 'localhost:%u (127.0.0.1)': [20] unable to get local issuer"
" certificate",
hrnServerPort(0));
// -----------------------------------------------------------------------------------------------------------------
@ -680,7 +736,8 @@ testRun(void)
STRDEF(TEST_PATH "/server-cn-only.crt"), 5000);
IoSession *socketSession = NULL;
TEST_RESULT_STR(ioServerName(socketServer), strNewFmt("localhost:%u", hrnServerPort(0)), "socket server name");
TEST_RESULT_STR(
ioServerName(socketServer), strNewFmt("localhost:%u (127.0.0.1)", hrnServerPort(0)), "socket server name");
TEST_RESULT_STR_Z(ioServerName(tlsServer), "localhost", "tls server name");
// Invalid client cert