1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-13 01:00:23 +02:00

Move file descriptor read/write ready into IoRead/IoWrite.

Move sckSessionReadyRead()/Write() into the IoRead/IoWrite interfaces. This is a more logical place for them and the alternative would be to add them to the IoSession interface, which does not seem like a good idea.

This is mostly a refactor, but a big change is the select() logic in fdRead.c has been replaced by ioReadReady(). This was duplicated code that was being used by our protocol but not TLS. Since we have not had any problems with requiring poll() in the field this seems like a good time to remove our dependence on select().

Also, IoFdWrite now requires a timeout so update where required, mostly in the tests.
This commit is contained in:
David Steele
2020-08-08 11:23:37 -04:00
committed by GitHub
parent 111d33c123
commit 4d22d6eeca
38 changed files with 431 additions and 245 deletions

View File

@ -78,6 +78,7 @@
<release-development-list> <release-development-list>
<release-item> <release-item>
<commit subject="Add IoClient and IoSession interfaces."/> <commit subject="Add IoClient and IoSession interfaces."/>
<commit subject="Move file descriptor read/write ready into IoRead/IoWrite."/>
<release-item-contributor-list> <release-item-contributor-list>
<release-item-reviewer id="stephen.frost"/> <release-item-reviewer id="stephen.frost"/>

View File

@ -67,6 +67,7 @@ SRCS = \
common/io/bufferRead.c \ common/io/bufferRead.c \
common/io/bufferWrite.c \ common/io/bufferWrite.c \
common/io/client.c \ common/io/client.c \
common/io/fd.c \
common/io/fdRead.c \ common/io/fdRead.c \
common/io/fdWrite.c \ common/io/fdWrite.c \
common/io/filter/buffer.c \ common/io/filter/buffer.c \

View File

@ -32,7 +32,7 @@ cmdLocal(int fdRead, int fdWrite)
String *name = strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u", cfgOptionUInt(cfgOptProcess)); String *name = strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u", cfgOptionUInt(cfgOptProcess));
IoRead *read = ioFdReadNew(name, fdRead, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000)); IoRead *read = ioFdReadNew(name, fdRead, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(name, fdWrite); IoWrite *write = ioFdWriteNew(name, fdWrite, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
ioWriteOpen(write); ioWriteOpen(write);
ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_LOCAL_STR, read, write); ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_LOCAL_STR, read, write);

View File

@ -28,7 +28,7 @@ cmdRemote(int fdRead, int fdWrite)
String *name = strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u", cfgOptionUInt(cfgOptProcess)); String *name = strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u", cfgOptionUInt(cfgOptProcess));
IoRead *read = ioFdReadNew(name, fdRead, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000)); IoRead *read = ioFdReadNew(name, fdRead, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(name, fdWrite); IoWrite *write = ioFdWriteNew(name, fdWrite, (TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
ioWriteOpen(write); ioWriteOpen(write);
ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_REMOTE_STR, read, write); ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_REMOTE_STR, read, write);

View File

@ -193,7 +193,8 @@ cmdStorageGet(void)
{ {
TRY_BEGIN() TRY_BEGIN()
{ {
result = storageGetProcess(ioFdWriteNew(STRDEF("stdout"), STDOUT_FILENO)); result = storageGetProcess(
ioFdWriteNew(STRDEF("stdout"), STDOUT_FILENO, (TimeMSec)(cfgOptionDbl(cfgOptIoTimeout) * 1000)));
} }
// Ignore write errors because it's possible (even likely) that this output is being piped to something like head which // Ignore write errors because it's possible (even likely) that this output is being piped to something like head which
// will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken // will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken

View File

@ -194,7 +194,8 @@ cmdStorageList(void)
{ {
TRY_BEGIN() TRY_BEGIN()
{ {
storageListRender(ioFdWriteNew(STRDEF("stdout"), STDOUT_FILENO)); storageListRender(
ioFdWriteNew(STRDEF("stdout"), STDOUT_FILENO, (TimeMSec)(cfgOptionDbl(cfgOptIoTimeout) * 1000)));
} }
// Ignore write errors because it's possible (even likely) that this output is being piped to something like head which // Ignore write errors because it's possible (even likely) that this output is being piped to something like head which
// will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken // will exit when it gets what it needs and leave us writing to a broken pipe. It would be better to just ignore the broken

View File

@ -354,7 +354,7 @@ execOpen(Exec *this)
// Assign file descriptors to io interfaces // Assign file descriptors to io interfaces
this->ioReadFd = ioFdReadNew(strNewFmt("%s read", strZ(this->name)), this->fdRead, this->timeout); this->ioReadFd = ioFdReadNew(strNewFmt("%s read", strZ(this->name)), this->fdRead, this->timeout);
this->ioWriteFd = ioFdWriteNew(strNewFmt("%s write", strZ(this->name)), this->fdWrite); this->ioWriteFd = ioFdWriteNew(strNewFmt("%s write", strZ(this->name)), this->fdWrite, this->timeout);
ioWriteOpen(this->ioWriteFd); ioWriteOpen(this->ioWriteFd);
// Create wrapper interfaces that check process state // Create wrapper interfaces that check process state

102
src/common/io/fd.c Normal file
View File

@ -0,0 +1,102 @@
/***********************************************************************************************************************************
File Descriptor Functions
***********************************************************************************************************************************/
#include "build.auto.h"
#include <poll.h>
#include "common/debug.h"
#include "common/io/fd.h"
#include "common/log.h"
#include "common/wait.h"
/***********************************************************************************************************************************
Use poll() to determine when data is ready to read/write on a socket. Retry after EINTR with whatever time is left on the timer.
***********************************************************************************************************************************/
// Helper to determine when poll() should be retried
static bool
fdReadyRetry(int pollResult, int errNo, bool first, TimeMSec *timeout, TimeMSec timeEnd)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INT, pollResult);
FUNCTION_TEST_PARAM(INT, errNo);
FUNCTION_TEST_PARAM(BOOL, first);
FUNCTION_TEST_PARAM_P(TIME_MSEC, timeout);
FUNCTION_TEST_PARAM(TIME_MSEC, timeEnd);
FUNCTION_TEST_END();
ASSERT(timeout != NULL);
// No retry by default
bool result = false;
// Process errors
if (pollResult == -1)
{
// Don't error on an interrupt. If the interrupt lasts long enough there may be a timeout, though.
if (errNo != EINTR)
THROW_SYS_ERROR_CODE(errNo, KernelError, "unable to poll socket");
// Always retry on the first iteration
if (first)
{
result = true;
}
// Else retry if there is time left
else
{
TimeMSec timeCurrent = timeMSec();
if (timeEnd > timeCurrent)
{
*timeout = timeEnd - timeCurrent;
result = true;
}
}
}
FUNCTION_TEST_RETURN(result);
}
bool
fdReady(int fd, bool read, bool write, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(INT, fd);
FUNCTION_LOG_PARAM(BOOL, read);
FUNCTION_LOG_PARAM(BOOL, write);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(fd >= 0);
ASSERT(read || write);
ASSERT(timeout < INT_MAX);
// Poll settings
struct pollfd inputFd = {.fd = fd};
if (read)
inputFd.events |= POLLIN;
if (write)
inputFd.events |= POLLOUT;
// Wait for ready or timeout
TimeMSec timeEnd = timeMSec() + timeout;
bool first = true;
// Initialize result and errno to look like a retryable error. We have no good way to test this function with interrupts so this
// at least ensures that the condition is retried.
int result = -1;
int errNo = EINTR;
while (fdReadyRetry(result, errNo, first, &timeout, timeEnd))
{
result = poll(&inputFd, 1, (int)timeout);
errNo = errno;
first = false;
}
FUNCTION_LOG_RETURN(BOOL, result > 0);
}

29
src/common/io/fd.h Normal file
View File

@ -0,0 +1,29 @@
/***********************************************************************************************************************************
File Descriptor Functions
***********************************************************************************************************************************/
#ifndef COMMON_IO_FD_H
#define COMMON_IO_FD_H
#include "common/time.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Wait until the file descriptor is ready to read/write or timeout
bool fdReady(int fd, bool read, bool write, TimeMSec timeout);
// Wait until the file descriptor is ready to read or timeout
__attribute__((always_inline)) static inline bool
fdReadyRead(int fd, TimeMSec timeout)
{
return fdReady(fd, true, false, timeout);
}
// Wait until the file descriptor is ready to write or timeout
__attribute__((always_inline)) static inline bool
fdReadyWrite(int fd, TimeMSec timeout)
{
return fdReady(fd, false, true, timeout);
}
#endif

View File

@ -3,10 +3,10 @@ File Descriptor Io Read
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#include "build.auto.h" #include "build.auto.h"
#include <sys/select.h>
#include <unistd.h> #include <unistd.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/io/fd.h"
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/read.intern.h" #include "common/io/read.intern.h"
#include "common/log.h" #include "common/log.h"
@ -33,6 +33,37 @@ Macros for function logging
#define FUNCTION_LOG_IO_FD_READ_FORMAT(value, buffer, bufferSize) \ #define FUNCTION_LOG_IO_FD_READ_FORMAT(value, buffer, bufferSize) \
objToLog(value, "IoFdRead", buffer, bufferSize) objToLog(value, "IoFdRead", buffer, bufferSize)
/***********************************************************************************************************************************
Are there bytes ready to read immediately?
***********************************************************************************************************************************/
static bool
ioFdReadReady(THIS_VOID, bool error)
{
THIS(IoFdRead);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_FD_READ, this);
FUNCTION_LOG_PARAM(BOOL, error);
FUNCTION_LOG_END();
ASSERT(this != NULL);
bool result = true;
// Check if the file descriptor is ready to read
if (!fdReadyRead(this->fd, this->timeout))
{
// Error if requested
if (error)
THROW_FMT(FileReadError, "timeout after %" PRIu64 "ms waiting for read from '%s'", this->timeout, strZ(this->name));
// File descriptor is not ready to read
result = false;
}
FUNCTION_LOG_RETURN(BOOL, result);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Read data from the file descriptor Read data from the file descriptor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -58,25 +89,8 @@ ioFdRead(THIS_VOID, Buffer *buffer, bool block)
{ {
do do
{ {
// Initialize the file descriptor set used for select // Check if there is data to be read and error if not
fd_set selectSet; ioFdReadReady(this, true);
FD_ZERO(&selectSet);
// We know the file descriptor is not negative because it passed error handling, so it is safe to cast to unsigned
FD_SET((unsigned int)this->fd, &selectSet);
// Initialize timeout struct used for select. Recreate this structure each time since Linux (at least) will modify it.
struct timeval timeoutSelect;
timeoutSelect.tv_sec = (time_t)(this->timeout / MSEC_PER_SEC);
timeoutSelect.tv_usec = (time_t)(this->timeout % MSEC_PER_SEC * 1000);
// Determine if there is data to be read
int result = select(this->fd + 1, &selectSet, NULL, NULL, &timeoutSelect);
THROW_ON_SYS_ERROR_FMT(result == -1, FileReadError, "unable to select from %s", strZ(this->name));
// If no data read after time allotted then error
if (!result)
THROW_FMT(FileReadError, "unable to read data from %s after %" PRIu64 "ms", strZ(this->name), this->timeout);
// Read and handle errors // Read and handle errors
THROW_ON_SYS_ERROR_FMT( THROW_ON_SYS_ERROR_FMT(
@ -156,7 +170,7 @@ ioFdReadNew(const String *name, int fd, TimeMSec timeout)
.timeout = timeout, .timeout = timeout,
}; };
this = ioReadNewP(driver, .block = true, .eof = ioFdReadEof, .fd = ioFdReadFd, .read = ioFdRead); this = ioReadNewP(driver, .block = true, .eof = ioFdReadEof, .fd = ioFdReadFd, .read = ioFdRead, .ready = ioFdReadReady);
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();

View File

@ -6,6 +6,7 @@ File Descriptor Io Write
#include <unistd.h> #include <unistd.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/io/fd.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/write.intern.h" #include "common/io/write.intern.h"
#include "common/log.h" #include "common/log.h"
@ -20,6 +21,7 @@ typedef struct IoFdWrite
MemContext *memContext; // Object memory context MemContext *memContext; // Object memory context
const String *name; // File descriptor name for error messages const String *name; // File descriptor name for error messages
int fd; // File descriptor to write to int fd; // File descriptor to write to
TimeMSec timeout; // Timeout for write operation
} IoFdWrite; } IoFdWrite;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
@ -30,6 +32,37 @@ Macros for function logging
#define FUNCTION_LOG_IO_FD_WRITE_FORMAT(value, buffer, bufferSize) \ #define FUNCTION_LOG_IO_FD_WRITE_FORMAT(value, buffer, bufferSize) \
objToLog(value, "IoFdWrite", buffer, bufferSize) objToLog(value, "IoFdWrite", buffer, bufferSize)
/***********************************************************************************************************************************
// Can bytes be written immediately?
***********************************************************************************************************************************/
static bool
ioFdWriteReady(THIS_VOID, bool error)
{
THIS(IoFdWrite);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_FD_WRITE, this);
FUNCTION_LOG_PARAM(BOOL, error);
FUNCTION_LOG_END();
ASSERT(this != NULL);
bool result = true;
// Check if the file descriptor is ready to write
if (!fdReadyWrite(this->fd, this->timeout))
{
// Error if requested
if (error)
THROW_FMT(FileWriteError, "timeout after %" PRIu64 "ms waiting for write to '%s'", this->timeout, strZ(this->name));
// File descriptor is not ready to write
result = false;
}
FUNCTION_LOG_RETURN(BOOL, result);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Write to the file descriptor Write to the file descriptor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -71,10 +104,11 @@ ioFdWriteFd(const THIS_VOID)
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
IoWrite * IoWrite *
ioFdWriteNew(const String *name, int fd) ioFdWriteNew(const String *name, int fd, TimeMSec timeout)
{ {
FUNCTION_LOG_BEGIN(logLevelTrace); FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(INT, fd); FUNCTION_LOG_PARAM(INT, fd);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
IoWrite *this = NULL; IoWrite *this = NULL;
@ -88,9 +122,10 @@ ioFdWriteNew(const String *name, int fd)
.memContext = memContextCurrent(), .memContext = memContextCurrent(),
.name = strDup(name), .name = strDup(name),
.fd = fd, .fd = fd,
.timeout = timeout,
}; };
this = ioWriteNewP(driver, .fd = ioFdWriteFd, .write = ioFdWrite); this = ioWriteNewP(driver, .fd = ioFdWriteFd, .ready = ioFdWriteReady, .write = ioFdWrite);
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();

View File

@ -7,11 +7,12 @@ Write to a file descriptor using the IoWrite interface.
#define COMMON_IO_FDWRITE_H #define COMMON_IO_FDWRITE_H
#include "common/io/write.h" #include "common/io/write.h"
#include "common/time.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
IoWrite *ioFdWriteNew(const String *name, int fd); IoWrite *ioFdWriteNew(const String *name, int fd, TimeMSec timeout);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Helper functions Helper functions

View File

@ -294,6 +294,25 @@ ioReadLine(IoRead *this)
FUNCTION_LOG_RETURN(STRING, ioReadLineParam(this, false)); FUNCTION_LOG_RETURN(STRING, ioReadLineParam(this, false));
} }
/**********************************************************************************************************************************/
bool
ioReadReady(IoRead *this, IoReadReadyParam param)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_READ, this);
FUNCTION_LOG_PARAM(BOOL, param.error);
FUNCTION_LOG_END();
ASSERT(this != NULL);
bool result = true;
if (this->interface.ready != NULL)
result = this->interface.ready(this->driver, param.error);
FUNCTION_LOG_RETURN(BOOL, result);
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
void void
ioReadClose(IoRead *this) ioReadClose(IoRead *this)

View File

@ -35,6 +35,19 @@ String *ioReadLine(IoRead *this);
// Read linefeed-terminated string and optionally error on eof // Read linefeed-terminated string and optionally error on eof
String *ioReadLineParam(IoRead *this, bool allowEof); String *ioReadLineParam(IoRead *this, bool allowEof);
// Are there bytes ready to read immediately? There are no guarantees on how much data is available to read but it must be at least
// one byte.
typedef struct IoReadReadyParam
{
VAR_PARAM_HEADER;
bool error; // Error when read not ready
} IoReadReadyParam;
#define ioReadReadyP(this, ...) \
ioReadReady(this, (IoReadReadyParam){VAR_PARAM_INIT, __VA_ARGS__})
bool ioReadReady(IoRead *this, IoReadReadyParam param);
// Close the IO // Close the IO
void ioReadClose(IoRead *this); void ioReadClose(IoRead *this);

View File

@ -21,6 +21,10 @@ typedef struct IoReadInterface
bool (*open)(void *driver); bool (*open)(void *driver);
int (*fd)(const void *driver); int (*fd)(const void *driver);
size_t (*read)(void *driver, Buffer *buffer, bool block); size_t (*read)(void *driver, Buffer *buffer, bool block);
// Are there bytes ready to read immediately? There are no guarantees on how much data is available to read but it must be at
// least one byte. Optionally error when read is not ready.
bool (*ready)(void *driver, bool error);
} IoReadInterface; } IoReadInterface;
#define ioReadNewP(driver, ...) \ #define ioReadNewP(driver, ...) \

View File

@ -6,11 +6,10 @@ Socket Common Functions
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/io/fd.h"
#include "common/io/socket/common.h" #include "common/io/socket/common.h"
#include "common/log.h" #include "common/log.h"
#include "common/wait.h" #include "common/wait.h"
@ -168,7 +167,7 @@ sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo
if (sckConnectInProgress(errNo)) if (sckConnectInProgress(errNo))
{ {
// Wait for write-ready // Wait for write-ready
if (!sckReadyWrite(fd, timeout)) if (!fdReadyWrite(fd, timeout))
THROW_FMT(HostConnectError, "timeout connecting to '%s:%u'", strZ(host), port); THROW_FMT(HostConnectError, "timeout connecting to '%s:%u'", strZ(host), port);
// Check for success or error. If the connection was successful this will set errNo to 0. // Check for success or error. If the connection was successful this will set errNo to 0.
@ -185,106 +184,3 @@ sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
} }
/***********************************************************************************************************************************
Use poll() to determine when data is ready to read/write on a socket. Retry after EINTR with whatever time is left on the timer.
***********************************************************************************************************************************/
// Helper to determine when poll() should be retried
static bool
sckReadyRetry(int pollResult, int errNo, bool first, TimeMSec *timeout, TimeMSec timeEnd)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INT, pollResult);
FUNCTION_TEST_PARAM(INT, errNo);
FUNCTION_TEST_PARAM(BOOL, first);
FUNCTION_TEST_PARAM_P(TIME_MSEC, timeout);
FUNCTION_TEST_PARAM(TIME_MSEC, timeEnd);
FUNCTION_TEST_END();
ASSERT(timeout != NULL);
// No retry by default
bool result = false;
// Process errors
if (pollResult == -1)
{
// Don't error on an interrupt. If the interrupt lasts long enough there may be a timeout, though.
if (errNo != EINTR)
THROW_SYS_ERROR_CODE(errNo, KernelError, "unable to poll socket");
// Always retry on the first iteration
if (first)
{
result = true;
}
// Else retry if there is time left
else
{
TimeMSec timeCurrent = timeMSec();
if (timeEnd > timeCurrent)
{
*timeout = timeEnd - timeCurrent;
result = true;
}
}
}
FUNCTION_TEST_RETURN(result);
}
bool
sckReady(int fd, bool read, bool write, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(INT, fd);
FUNCTION_LOG_PARAM(BOOL, read);
FUNCTION_LOG_PARAM(BOOL, write);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(fd >= 0);
ASSERT(read || write);
ASSERT(timeout < INT_MAX);
// Poll settings
struct pollfd inputFd = {.fd = fd};
if (read)
inputFd.events |= POLLIN;
if (write)
inputFd.events |= POLLOUT;
// Wait for ready or timeout
TimeMSec timeEnd = timeMSec() + timeout;
bool first = true;
// Initialize result and errno to look like a retryable error. We have no good way to test this function with interrupts so this
// at least ensures that the condition is retried.
int result = -1;
int errNo = EINTR;
while (sckReadyRetry(result, errNo, first, &timeout, timeEnd))
{
result = poll(&inputFd, 1, (int)timeout);
errNo = errno;
first = false;
}
FUNCTION_LOG_RETURN(BOOL, result > 0);
}
bool
sckReadyRead(int fd, TimeMSec timeout)
{
return sckReady(fd, true, false, timeout);
}
bool
sckReadyWrite(int fd, TimeMSec timeout)
{
return sckReady(fd, false, true, timeout);
}

View File

@ -21,9 +21,4 @@ void sckOptionSet(int fd);
// Connect socket to an IP address // Connect socket to an IP address
void sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo *hostAddress, TimeMSec timeout); void sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo *hostAddress, TimeMSec timeout);
// Wait until the socket is ready to read/write or timeout
bool sckReady(int fd, bool read, bool write, TimeMSec timeout);
bool sckReadyRead(int fd, TimeMSec timeout);
bool sckReadyWrite(int fd, TimeMSec timeout);
#endif #endif

View File

@ -7,6 +7,8 @@ Socket Session
#include "common/debug.h" #include "common/debug.h"
#include "common/log.h" #include "common/log.h"
#include "common/io/fdRead.h"
#include "common/io/fdWrite.h"
#include "common/io/socket/client.h" #include "common/io/socket/client.h"
#include "common/io/socket/common.h" #include "common/io/socket/common.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -24,11 +26,16 @@ struct SocketSession
String *host; // Hostname or IP address String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on unsigned int port; // Port to connect to host on
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.) TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
IoRead *read; // IoRead interface to the file descriptor
IoWrite *write; // IoWrite interface to the file descriptor
}; };
OBJECT_DEFINE_MOVE(SOCKET_SESSION); OBJECT_DEFINE_MOVE(SOCKET_SESSION);
OBJECT_DEFINE_GET(Fd, , SOCKET_SESSION, int, fd); OBJECT_DEFINE_GET(Fd, , SOCKET_SESSION, int, fd);
OBJECT_DEFINE_GET(IoRead, , SOCKET_SESSION, IoRead *, read);
OBJECT_DEFINE_GET(IoWrite, , SOCKET_SESSION, IoWrite *, write);
OBJECT_DEFINE_GET(Type, const, SOCKET_SESSION, SocketSessionType, type); OBJECT_DEFINE_GET(Type, const, SOCKET_SESSION, SocketSessionType, type);
OBJECT_DEFINE_FREE(SOCKET_SESSION); OBJECT_DEFINE_FREE(SOCKET_SESSION);
@ -63,6 +70,8 @@ sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int p
{ {
this = memNew(sizeof(SocketSession)); this = memNew(sizeof(SocketSession));
String *name = strNewFmt("%s:%u", strZ(host), port);
*this = (SocketSession) *this = (SocketSession)
{ {
.memContext = MEM_CONTEXT_NEW(), .memContext = MEM_CONTEXT_NEW(),
@ -71,53 +80,18 @@ sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int p
.host = strDup(host), .host = strDup(host),
.port = port, .port = port,
.timeout = timeout, .timeout = timeout,
.read = ioFdReadNew(name, fd, timeout),
.write = ioFdWriteNew(name, fd, timeout),
}; };
memContextCallbackSet(this->memContext, sckSessionFreeResource, this); memContextCallbackSet(this->memContext, sckSessionFreeResource, this);
strFree(name);
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(SOCKET_SESSION, this); FUNCTION_LOG_RETURN(SOCKET_SESSION, this);
} }
/**********************************************************************************************************************************/
void
sckSessionReadyRead(SocketSession *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
if (!sckReadyRead(this->fd, this->timeout))
{
THROW_FMT(
ProtocolError, "timeout after %" PRIu64 "ms waiting for read from '%s:%u'", this->timeout, strZ(this->host),
this->port);
}
FUNCTION_LOG_RETURN_VOID();
}
void
sckSessionReadyWrite(SocketSession *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
if (!sckReadyWrite(this->fd, this->timeout))
{
THROW_FMT(
ProtocolError, "timeout after %" PRIu64 "ms waiting for write to '%s:%u'", this->timeout, strZ(this->host), this->port);
}
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
String * String *
sckSessionToLog(const SocketSession *this) sckSessionToLog(const SocketSession *this)

View File

@ -25,6 +25,8 @@ Object type
typedef struct SocketSession SocketSession; typedef struct SocketSession SocketSession;
#include "common/io/read.h"
#include "common/io/write.h"
#include "common/time.h" #include "common/time.h"
#include "common/type/string.h" #include "common/type/string.h"
@ -33,16 +35,21 @@ Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
SocketSession *sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int port, TimeMSec timeout); SocketSession *sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int port, TimeMSec timeout);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Read interface
IoRead *sckSessionIoRead(SocketSession *this);
// Write interface
IoWrite *sckSessionIoWrite(SocketSession *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Move to a new parent mem context // Move to a new parent mem context
SocketSession *sckSessionMove(SocketSession *this, MemContext *parentNew); SocketSession *sckSessionMove(SocketSession *this, MemContext *parentNew);
// Check if there is data ready to read/write on the socket
void sckSessionReadyRead(SocketSession *this);
void sckSessionReadyWrite(SocketSession *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Getters/Setters Getters/Setters
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -137,7 +137,7 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
// Try again after waiting for read ready // Try again after waiting for read ready
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
{ {
sckSessionReadyRead(this->socketSession); ioReadReadyP(sckSessionIoRead(this->socketSession), .error = true);
result = 0; result = 0;
break; break;
} }
@ -145,7 +145,7 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
// Try again after waiting for write ready // Try again after waiting for write ready
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
{ {
sckSessionReadyWrite(this->socketSession); ioWriteReadyP(sckSessionIoWrite(this->socketSession), .error = true);
result = 0; result = 0;
break; break;
} }
@ -221,7 +221,7 @@ tlsSessionRead(THIS_VOID, Buffer *buffer, bool block)
{ {
// If no TLS data pending then check the socket to reduce blocking // If no TLS data pending then check the socket to reduce blocking
if (!SSL_pending(this->session)) if (!SSL_pending(this->session))
sckSessionReadyRead(this->socketSession); ioReadReadyP(sckSessionIoRead(this->socketSession), .error = true);
// Read and handle errors. The error queue must be cleared before this operation. // Read and handle errors. The error queue must be cleared before this operation.
ERR_clear_error(); ERR_clear_error();

View File

@ -143,6 +143,25 @@ ioWriteLine(IoWrite *this, const Buffer *buffer)
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
} }
/**********************************************************************************************************************************/
bool
ioWriteReady(IoWrite *this, IoWriteReadyParam param)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_WRITE, this);
FUNCTION_LOG_PARAM(BOOL, param.error);
FUNCTION_LOG_END();
ASSERT(this != NULL);
bool result = true;
if (this->interface.ready != NULL)
result = this->interface.ready(this->driver, param.error);
FUNCTION_LOG_RETURN(BOOL, result);
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
void void
ioWriteStr(IoWrite *this, const String *string) ioWriteStr(IoWrite *this, const String *string)

View File

@ -31,6 +31,18 @@ void ioWrite(IoWrite *this, const Buffer *buffer);
// Write linefeed-terminated buffer // Write linefeed-terminated buffer
void ioWriteLine(IoWrite *this, const Buffer *buffer); void ioWriteLine(IoWrite *this, const Buffer *buffer);
// Can bytes be written immediately? There are no guarantees on how much data can be written but it must be at least one byte.
typedef struct IoWriteReadyParam
{
VAR_PARAM_HEADER;
bool error; // Error when write not ready
} IoWriteReadyParam;
#define ioWriteReadyP(this, ...) \
ioWriteReady(this, (IoWriteReadyParam){VAR_PARAM_INIT, __VA_ARGS__})
bool ioWriteReady(IoWrite *this, IoWriteReadyParam param);
// Write string // Write string
void ioWriteStr(IoWrite *this, const String *string); void ioWriteStr(IoWrite *this, const String *string);

View File

@ -14,6 +14,11 @@ typedef struct IoWriteInterface
void (*close)(void *driver); void (*close)(void *driver);
int (*fd)(const void *driver); int (*fd)(const void *driver);
void (*open)(void *driver); void (*open)(void *driver);
// Can bytes be written immediately? There are no guarantees on how much data can be written but it must be at least one byte.
// Optionally error when write is not ready.
bool (*ready)(void *driver, bool error);
void (*write)(void *driver, const Buffer *buffer); void (*write)(void *driver, const Buffer *buffer);
} IoWriteInterface; } IoWriteInterface;

View File

@ -226,6 +226,7 @@ unit:
coverage: coverage:
common/io/bufferRead: full common/io/bufferRead: full
common/io/bufferWrite: full common/io/bufferWrite: full
common/io/fd: full
common/io/fdRead: full common/io/fdRead: full
common/io/fdWrite: full common/io/fdWrite: full
common/io/filter/buffer: full common/io/filter/buffer: full
@ -250,6 +251,10 @@ unit:
common/io/socket/common: full common/io/socket/common: full
common/io/socket/session: full common/io/socket/session: full
include:
- common/io/fdRead
- common/io/read
# ---------------------------------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------------------------
- name: io-http - name: io-http
total: 5 total: 5

View File

@ -540,7 +540,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
lockAcquire(cfgOptionStr(cfgOptLockPath), cfgOptionStr(cfgOptStanza), cfgLockType(), 30000, true); lockAcquire(cfgOptionStr(cfgOptLockPath), cfgOptionStr(cfgOptStanza), cfgLockType(), 30000, true);
@ -558,7 +558,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Wait for the child to acquire the lock // Wait for the child to acquire the lock

View File

@ -152,7 +152,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
int lockFd = open(strZ(strNewFmt("%s/empty" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0); int lockFd = open(strZ(strNewFmt("%s/empty" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0);
@ -175,7 +175,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Wait for the child to acquire the lock // Wait for the child to acquire the lock
@ -209,7 +209,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
int lockFd = open(strZ(strNewFmt("%s/empty" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0); int lockFd = open(strZ(strNewFmt("%s/empty" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0);
@ -232,7 +232,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Wait for the child to acquire the lock // Wait for the child to acquire the lock
@ -261,7 +261,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
TEST_RESULT_BOOL( TEST_RESULT_BOOL(
@ -280,7 +280,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Wait for the child to acquire the lock // Wait for the child to acquire the lock
@ -309,7 +309,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
int lockFd = open(strZ(strNewFmt("%s/badpid" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0); int lockFd = open(strZ(strNewFmt("%s/badpid" LOCK_FILE_EXT, strZ(lockPath))), O_RDONLY, 0);
@ -333,7 +333,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Wait for the child to acquire the lock // Wait for the child to acquire the lock

View File

@ -46,7 +46,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_LOCAL_STR, read, write); ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_LOCAL_STR, read, write);

View File

@ -45,7 +45,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_REMOTE_STR, read, write); ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_REMOTE_STR, read, write);
@ -81,7 +81,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = NULL; ProtocolClient *client = NULL;
@ -117,7 +117,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
TEST_ERROR( TEST_ERROR(
@ -149,7 +149,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = NULL; ProtocolClient *client = NULL;
@ -187,7 +187,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
storagePutP(storageNewWriteP(storageData, strNew("lock/all" STOP_FILE_EXT)), NULL); storagePutP(storageNewWriteP(storageData, strNew("lock/all" STOP_FILE_EXT)), NULL);

View File

@ -100,7 +100,7 @@ testRun(void)
TEST_ERROR(execFreeResource(exec), ExecuteError, "sleep did not exit when expected"); TEST_ERROR(execFreeResource(exec), ExecuteError, "sleep did not exit when expected");
TEST_ERROR(ioReadLine(execIoRead(exec)), FileReadError, "unable to select from sleep read: [9] Bad file descriptor"); TEST_ERROR(ioReadLine(execIoRead(exec)), FileReadError, "unable to read from sleep read: [9] Bad file descriptor");
ioWriteStrLine(execIoWrite(exec), strNew("")); ioWriteStrLine(execIoWrite(exec), strNew(""));
TEST_ERROR(ioWriteFlush(execIoWrite(exec)), FileWriteError, "unable to write to sleep write: [9] Bad file descriptor"); TEST_ERROR(ioWriteFlush(execIoWrite(exec)), FileWriteError, "unable to write to sleep write: [9] Bad file descriptor");

View File

@ -199,7 +199,7 @@ testRun(void)
HARNESS_FORK_PARENT_BEGIN() HARNESS_FORK_PARENT_BEGIN()
{ {
hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0))); hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000));
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("create client"); TEST_TITLE("create client");

View File

@ -2,6 +2,7 @@
Test IO Test IO
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#include <fcntl.h> #include <fcntl.h>
#include <netdb.h>
#include "common/type/json.h" #include "common/type/json.h"
@ -278,6 +279,7 @@ testRun(void)
"create io read object"); "create io read object");
TEST_RESULT_BOOL(ioReadOpen(read), true, " open io object"); TEST_RESULT_BOOL(ioReadOpen(read), true, " open io object");
TEST_RESULT_BOOL(ioReadReadyP(read), true, "read defaults to ready");
TEST_RESULT_UINT(ioRead(read, buffer), 2, " read 2 bytes"); TEST_RESULT_UINT(ioRead(read, buffer), 2, " read 2 bytes");
TEST_RESULT_BOOL(ioReadEof(read), false, " no eof"); TEST_RESULT_BOOL(ioReadEof(read), false, " no eof");
TEST_RESULT_VOID(ioReadClose(read), " close io object"); TEST_RESULT_VOID(ioReadClose(read), " close io object");
@ -483,6 +485,7 @@ testRun(void)
"create io write object"); "create io write object");
TEST_RESULT_VOID(ioWriteOpen(write), " open io object"); TEST_RESULT_VOID(ioWriteOpen(write), " open io object");
TEST_RESULT_BOOL(ioWriteReadyP(write), true, "write defaults to ready");
TEST_RESULT_BOOL(testIoWriteOpenCalled, true, " check io object open"); TEST_RESULT_BOOL(testIoWriteOpenCalled, true, " check io object open");
TEST_RESULT_VOID(ioWriteStr(write, STRDEF("ABC")), " write 3 bytes"); TEST_RESULT_VOID(ioWriteStr(write, STRDEF("ABC")), " write 3 bytes");
TEST_RESULT_VOID(ioWriteClose(write), " close io object"); TEST_RESULT_VOID(ioWriteClose(write), " close io object");
@ -538,8 +541,9 @@ testRun(void)
{ {
IoWrite *write = NULL; IoWrite *write = NULL;
TEST_ASSIGN(write, ioFdWriteNew(strNew("write test"), HARNESS_FORK_CHILD_WRITE()), "move write"); TEST_ASSIGN(write, ioFdWriteNew(strNew("write test"), HARNESS_FORK_CHILD_WRITE(), 1000), "move write");
ioWriteOpen(write); ioWriteOpen(write);
TEST_RESULT_BOOL(ioWriteReadyP(write), true, "write is ready");
TEST_RESULT_INT(ioWriteFd(write), ((IoFdWrite *)write->driver)->fd, "check write fd"); TEST_RESULT_INT(ioWriteFd(write), ((IoFdWrite *)write->driver)->fd, "check write fd");
// Write a line to be read // Write a line to be read
@ -575,7 +579,11 @@ testRun(void)
// Only part of the buffer is written before timeout // Only part of the buffer is written before timeout
Buffer *buffer = bufNew(16); Buffer *buffer = bufNew(16);
TEST_ERROR(ioRead(read, buffer), FileReadError, "unable to read data from read test after 1000ms"); ((IoFdRead *)read->driver)->timeout = 1;
TEST_RESULT_BOOL(ioReadReadyP(read), false, "read is not ready (without throwing error)");
((IoFdRead *)read->driver)->timeout = 1000;
TEST_ERROR(ioRead(read, buffer), FileReadError, "timeout after 1000ms waiting for read from 'read test'");
TEST_RESULT_UINT(bufSize(buffer), 16, "buffer is only partially read"); TEST_RESULT_UINT(bufSize(buffer), 16, "buffer is only partially read");
// Read a buffer that is transmitted in two parts with blocking on the read side // Read a buffer that is transmitted in two parts with blocking on the read side
@ -605,6 +613,68 @@ testRun(void)
int fd = open(strZ(fileName), O_CREAT | O_TRUNC | O_WRONLY, 0700); int fd = open(strZ(fileName), O_CREAT | O_TRUNC | O_WRONLY, 0700);
TEST_RESULT_VOID(ioFdWriteOneStr(fd, strNew("test1\ntest2")), "write string to file"); TEST_RESULT_VOID(ioFdWriteOneStr(fd, strNew("test1\ntest2")), "write string to file");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("fdReadyRetry() edge conditions");
TimeMSec timeout = 5757;
TEST_RESULT_BOOL(fdReadyRetry(-1, EINTR, true, &timeout, 0), true, "first retry does not modify timeout");
TEST_RESULT_UINT(timeout, 5757, " check timeout");
timeout = 0;
TEST_RESULT_BOOL(fdReadyRetry(-1, EINTR, false, &timeout, timeMSec() + 10000), true, "retry before timeout");
TEST_RESULT_BOOL(timeout > 0, true, " check timeout");
TEST_RESULT_BOOL(fdReadyRetry(-1, EINTR, false, &timeout, timeMSec()), false, "no retry after timeout");
TEST_ERROR(fdReadyRetry(-1, EINVAL, true, &timeout, 0), KernelError, "unable to poll socket: [22] Invalid argument");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("write is not ready on bad socket connection");
struct addrinfo hints = (struct addrinfo)
{
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
int result;
const char *hostBad = "172.31.255.255";
struct addrinfo *hostBadAddress;
if ((result = getaddrinfo(hostBad, "7777", &hints, &hostBadAddress)) != 0)
{
THROW_FMT( // {uncoverable - lookup on IP should never fail}
HostConnectError, "unable to get address for '%s': [%d] %s", hostBad, result, gai_strerror(result));
}
TRY_BEGIN()
{
int fd = socket(hostBadAddress->ai_family, hostBadAddress->ai_socktype, hostBadAddress->ai_protocol);
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
// Set socket non-blocking
int flags;
THROW_ON_SYS_ERROR((flags = fcntl(fd, F_GETFL)) == -1, ProtocolError, "unable to get flags");
THROW_ON_SYS_ERROR(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1, ProtocolError, "unable to set O_NONBLOCK");
// Attempt connection
CHECK(connect(fd, hostBadAddress->ai_addr, hostBadAddress->ai_addrlen) == -1);
// Create file descriptor write and wait for timeout
IoWrite *write = NULL;
TEST_ASSIGN(write, ioFdWriteNew(STR(hostBad), fd, 100), "new fd write");
TEST_RESULT_BOOL(ioWriteReadyP(write), false, "write is not ready");
TEST_ERROR(
ioWriteReadyP(write, .error = true), FileWriteError, "timeout after 100ms waiting for write to '172.31.255.255'");
}
FINALLY()
{
// This needs to be freed or valgrind will complain
freeaddrinfo(hostBadAddress);
}
TRY_END();
} }
FUNCTION_HARNESS_RESULT_VOID(); FUNCTION_HARNESS_RESULT_VOID();

View File

@ -144,7 +144,8 @@ testRun(void)
TEST_ASSIGN(session, sckSessionNew(sckSessionTypeClient, fd, strNew(hostBad), 7777, 100), "new socket"); TEST_ASSIGN(session, sckSessionNew(sckSessionTypeClient, fd, strNew(hostBad), 7777, 100), "new socket");
TEST_ERROR( TEST_ERROR(
sckSessionReadyWrite(session), ProtocolError, "timeout after 100ms waiting for write to '172.31.255.255:7777'"); ioWriteReadyP(sckSessionIoWrite(session), .error = true), FileWriteError,
"timeout after 100ms waiting for write to '172.31.255.255:7777'");
TEST_RESULT_VOID(sckSessionFree(session), "free socket session"); TEST_RESULT_VOID(sckSessionFree(session), "free socket session");
@ -264,7 +265,7 @@ testRun(void)
HARNESS_FORK_PARENT_BEGIN() HARNESS_FORK_PARENT_BEGIN()
{ {
hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0))); hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 1000));
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("certificate error on invalid ca path"); TEST_TITLE("certificate error on invalid ca path");
@ -379,7 +380,7 @@ testRun(void)
HARNESS_FORK_PARENT_BEGIN() HARNESS_FORK_PARENT_BEGIN()
{ {
hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0))); hrnTlsClientBegin(ioFdWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 1000));
ioBufferSizeSet(12); ioBufferSizeSet(12);
TEST_ASSIGN( TEST_ASSIGN(
@ -392,25 +393,6 @@ testRun(void)
TEST_ASSIGN(session, ioClientOpen(client), "open client"); TEST_ASSIGN(session, ioClientOpen(client), "open client");
TlsSession *tlsSession = (TlsSession *)session->driver; TlsSession *tlsSession = (TlsSession *)session->driver;
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("socket read/write ready");
TimeMSec timeout = 5757;
TEST_RESULT_BOOL(sckReadyRetry(-1, EINTR, true, &timeout, 0), true, "first retry does not modify timeout");
TEST_RESULT_UINT(timeout, 5757, " check timeout");
timeout = 0;
TEST_RESULT_BOOL(sckReadyRetry(-1, EINTR, false, &timeout, timeMSec() + 10000), true, "retry before timeout");
TEST_RESULT_BOOL(timeout > 0, true, " check timeout");
TEST_RESULT_BOOL(sckReadyRetry(-1, EINTR, false, &timeout, timeMSec()), false, "no retry after timeout");
TEST_ERROR(
sckReadyRetry(-1, EINVAL, true, &timeout, 0), KernelError, "unable to poll socket: [22] Invalid argument");
TEST_RESULT_BOOL(sckReadyRead(tlsSession->socketSession->fd, 0), false, "socket is not read ready");
TEST_RESULT_BOOL(sckReadyWrite(tlsSession->socketSession->fd, 100), true, "socket is write ready");
TEST_RESULT_VOID(sckSessionReadyWrite(tlsSession->socketSession), "socket session is write ready");
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("uncovered errors"); TEST_TITLE("uncovered errors");
@ -459,11 +441,11 @@ testRun(void)
hrnTlsServerSleep(500); hrnTlsServerSleep(500);
output = bufNew(12); output = bufNew(12);
tlsSession->socketSession->timeout = 100; ((IoFdRead *)tlsSession->socketSession->read->driver)->timeout = 100;
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioRead(ioSessionIoRead(session), output), ProtocolError, ioRead(ioSessionIoRead(session), output), FileReadError,
"timeout after 100ms waiting for read from '%s:%u'", strZ(hrnTlsServerHost()), hrnTlsServerPort()); "timeout after 100ms waiting for read from '%s:%u'", strZ(hrnTlsServerHost()), hrnTlsServerPort());
tlsSession->socketSession->timeout = 5000; ((IoFdRead *)tlsSession->socketSession->read->driver)->timeout = 5000;
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("second protocol exchange"); TEST_TITLE("second protocol exchange");

View File

@ -26,7 +26,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
StringList *argList = strLstNew(); StringList *argList = strLstNew();
@ -46,7 +46,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("test"), strNew("config"), read, write); ProtocolClient *client = protocolClientNew(strNew("test"), strNew("config"), read, write);

View File

@ -65,7 +65,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Set options // Set options
@ -109,7 +109,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Create client // Create client

View File

@ -172,7 +172,7 @@ testRun(void)
// Setup handler for remote storage protocol // Setup handler for remote storage protocol
IoRead *read = ioFdReadNew(strNew("storage server read"), HARNESS_FORK_CHILD_READ(), 60000); IoRead *read = ioFdReadNew(strNew("storage server read"), HARNESS_FORK_CHILD_READ(), 60000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("storage server write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("storage server write"), HARNESS_FORK_CHILD_WRITE(), 1000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolServer *server = protocolServerNew(strNew("storage test server"), strNew("test"), read, write); ProtocolServer *server = protocolServerNew(strNew("storage test server"), strNew("test"), read, write);
@ -187,7 +187,7 @@ testRun(void)
// Create client // Create client
IoRead *read = ioFdReadNew(strNew("storage client read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 60000); IoRead *read = ioFdReadNew(strNew("storage client read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 60000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("storage client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("storage client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 1000);
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("storage test client"), strNew("test"), read, write); ProtocolClient *client = protocolClientNew(strNew("storage test client"), strNew("test"), read, write);

View File

@ -418,7 +418,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Various bogus greetings // Various bogus greetings
@ -492,7 +492,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Various bogus greetings // Various bogus greetings
@ -598,7 +598,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000); IoRead *read = ioFdReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Check greeting // Check greeting
@ -660,7 +660,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Send greeting // Send greeting
@ -737,7 +737,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 10000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 10000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Greeting with noop // Greeting with noop
@ -763,7 +763,7 @@ testRun(void)
{ {
IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 10000); IoRead *read = ioFdReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 10000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE()); IoWrite *write = ioFdWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE(), 2000);
ioWriteOpen(write); ioWriteOpen(write);
// Greeting with noop // Greeting with noop
@ -807,7 +807,7 @@ testRun(void)
strNewFmt("client %u read", clientIdx), HARNESS_FORK_PARENT_READ_PROCESS(clientIdx), 2000); strNewFmt("client %u read", clientIdx), HARNESS_FORK_PARENT_READ_PROCESS(clientIdx), 2000);
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioFdWriteNew( IoWrite *write = ioFdWriteNew(
strNewFmt("client %u write", clientIdx), HARNESS_FORK_PARENT_WRITE_PROCESS(clientIdx)); strNewFmt("client %u write", clientIdx), HARNESS_FORK_PARENT_WRITE_PROCESS(clientIdx), 2000);
ioWriteOpen(write); ioWriteOpen(write);
TEST_ASSIGN( TEST_ASSIGN(

View File

@ -280,7 +280,7 @@ testRun(void)
HARNESS_FORK_PARENT_BEGIN() HARNESS_FORK_PARENT_BEGIN()
{ {
hrnTlsClientBegin(ioFdWriteNew(strNew("azure client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0))); hrnTlsClientBegin(ioFdWriteNew(strNew("azure client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000));
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("test against local host"); TEST_TITLE("test against local host");

View File

@ -380,7 +380,7 @@ testRun(void)
HARNESS_FORK_PARENT_BEGIN() HARNESS_FORK_PARENT_BEGIN()
{ {
hrnTlsClientBegin(ioFdWriteNew(strNew("s3 client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0))); hrnTlsClientBegin(ioFdWriteNew(strNew("s3 client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0), 2000));
Storage *s3 = storageS3New( Storage *s3 = storageS3New(
path, true, NULL, bucket, endPoint, storageS3UriStyleHost, region, accessKey, secretAccessKey, NULL, 16, 2, path, true, NULL, bucket, endPoint, storageS3UriStyleHost, region, accessKey, secretAccessKey, NULL, 16, 2,