1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Allow I/O read interface to explicitly request blocking reads.

TlsClient introduced a non-blocking read which is required to read protocol messages that are linefeed-terminated rather than a known size. However, in many cases the expected number of bytes is known in advance so in that case it is more efficient to have tlsClientRead() block until all the bytes are read.

Add block parameter to all read functions and use it when a blocking read is required. For most read functions this is a noop, i.e. if the read function never blocks then it can ignore the parameter.

In passing, set the log level of storageNew*() functions to debug to expose more high-level I/O operations.
This commit is contained in:
David Steele 2018-11-23 12:01:36 -05:00
parent 256b727a3d
commit b5690e21a4
20 changed files with 111 additions and 65 deletions

View File

@ -31,6 +31,10 @@
<p>Add interface objects for <proper>libxml2</proper>.</p>
</release-item>
<release-item>
<p>Allow I/O read interface to explicitly request blocking reads.</p>
</release-item>
<release-item>
<p>Require <proper>S3</proper> key options except for <cmd>local</cmd>/<cmd>remote</cmd> commands.</p>
</release-item>

View File

@ -51,11 +51,12 @@ ioBufferReadNew(const Buffer *buffer)
Read data from the buffer
***********************************************************************************************************************************/
size_t
ioBufferRead(IoBufferRead *this, Buffer *buffer)
ioBufferRead(IoBufferRead *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(IO_BUFFER_READ, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_DEBUG_ASSERT(this != NULL);
FUNCTION_DEBUG_ASSERT(buffer != NULL);

View File

@ -21,7 +21,7 @@ IoBufferRead *ioBufferReadNew(const Buffer *buffer);
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
size_t ioBufferRead(IoBufferRead *this, Buffer *buffer);
size_t ioBufferRead(IoBufferRead *this, Buffer *buffer, bool block);
IoBufferRead *ioBufferReadMove(IoBufferRead *this, MemContext *parentNew);
/***********************************************************************************************************************************

View File

@ -59,11 +59,12 @@ struct HttpClient
Read content
***********************************************************************************************************************************/
static size_t
httpClientRead(HttpClient *this, Buffer *buffer)
httpClientRead(HttpClient *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(HTTP_CLIENT, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_ASSERT(buffer != NULL);
@ -361,7 +362,7 @@ httpClientRequest(
do
{
bufResize(result, bufSize(result) + ioBufferSize());
httpClientRead(this, result);
httpClientRead(this, result, true);
}
while (!httpClientEof(this));
}

View File

@ -113,12 +113,12 @@ ioReadEofDriver(const IoRead *this)
Read data from IO and process filters
***********************************************************************************************************************************/
static void
ioReadInternal(IoRead *this, Buffer *buffer, bool relaxed)
ioReadInternal(IoRead *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(IO_READ, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, relaxed);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_ASSERT(buffer != NULL);
@ -142,7 +142,13 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool relaxed)
if (!ioReadEofDriver(this))
{
bufUsedZero(this->input);
this->interface.read(this->driver, this->input);
// If blocking then limit the amount of data requested
if (block && bufRemains(this->input) > bufRemains(buffer))
bufLimitSet(this->input, bufRemains(buffer));
this->interface.read(this->driver, this->input, block);
bufLimitClear(this->input);
}
// Set input to NULL and flush (no need to actually free the buffer here as it will be freed with the mem context)
else
@ -151,8 +157,8 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool relaxed)
// Process the input buffer (or flush if NULL)
ioFilterGroupProcess(this->filterGroup, this->input, buffer);
// Stop if relaxed read -- we don't need to fill the buffer as long as we got some data
if (relaxed && bufUsed(buffer) > bufferUsedBegin)
// Stop if not blocking -- we don't need to fill the buffer as long as we got some data
if (!block && bufUsed(buffer) > bufferUsedBegin)
break;
}
@ -197,7 +203,7 @@ ioRead(IoRead *this, Buffer *buffer)
}
// Read data
ioReadInternal(this, buffer, false);
ioReadInternal(this, buffer, true);
FUNCTION_DEBUG_RESULT(SIZE, outputRemains - bufRemains(buffer));
}
@ -263,7 +269,7 @@ ioReadLine(IoRead *this)
if (ioReadEof(this))
THROW(FileReadError, "unexpected eof while reading line");
ioReadInternal(this, this->output, 1);
ioReadInternal(this, this->output, false);
}
}
while (result == NULL);

View File

@ -12,7 +12,7 @@ Constructor
typedef bool (*IoReadInterfaceEof)(void *driver);
typedef void (*IoReadInterfaceClose)(void *driver);
typedef bool (*IoReadInterfaceOpen)(void *driver);
typedef size_t (*IoReadInterfaceRead)(void *driver, Buffer *buffer);
typedef size_t (*IoReadInterfaceRead)(void *driver, Buffer *buffer, bool block);
typedef struct IoReadInterface
{

View File

@ -377,11 +377,12 @@ tlsClientOpen(TlsClient *this)
Read from the TLS session
***********************************************************************************************************************************/
size_t
tlsClientRead(TlsClient *this, Buffer *buffer)
tlsClientRead(TlsClient *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(TLS_CLIENT, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_ASSERT(this->session != NULL);
@ -391,46 +392,54 @@ tlsClientRead(TlsClient *this, Buffer *buffer)
ssize_t actualBytes = 0;
// If no tls data pending then check the socket
if (!SSL_pending(this->session))
// If blocking read keep reading until buffer is full
do
{
// Initialize the file descriptor set used for select
fd_set selectSet;
FD_ZERO(&selectSet);
// We know the socket is not negative because it passed error handling, so it is safe to cast to unsigned
FD_SET((unsigned int)this->socket, &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->socket + 1, &selectSet, NULL, NULL, &timeoutSelect);
THROW_ON_SYS_ERROR_FMT(result == -1, AssertError, "unable to select from '%s:%u'", strPtr(this->host), this->port);
// If no data read after time allotted then error
if (!result)
// If no tls data pending then check the socket
if (!SSL_pending(this->session))
{
THROW_FMT(
FileReadError, "unable to read data from '%s:%u' after %" PRIu64 "ms",
strPtr(this->host), this->port, this->timeout);
// Initialize the file descriptor set used for select
fd_set selectSet;
FD_ZERO(&selectSet);
// We know the socket is not negative because it passed error handling, so it is safe to cast to unsigned
FD_SET((unsigned int)this->socket, &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->socket + 1, &selectSet, NULL, NULL, &timeoutSelect);
THROW_ON_SYS_ERROR_FMT(result == -1, AssertError, "unable to select from '%s:%u'", strPtr(this->host), this->port);
// If no data read after time allotted then error
if (!result)
{
THROW_FMT(
FileReadError, "unable to read data from '%s:%u' after %" PRIu64 "ms",
strPtr(this->host), this->port, this->timeout);
}
}
// Read and handle errors
size_t expectedBytes = bufRemains(buffer);
actualBytes = SSL_read(this->session, bufRemainsPtr(buffer), (int)expectedBytes);
cryptoError(actualBytes < 0, "unable to read from TLS");
// Update amount of buffer used
bufUsedInc(buffer, (size_t)actualBytes);
// If zero bytes were returned then the connection was closed
if (actualBytes == 0)
{
tlsClientClose(this);
break;
}
}
// Read and handle errors
size_t expectedBytes = bufRemains(buffer);
actualBytes = SSL_read(this->session, bufRemainsPtr(buffer), (int)expectedBytes);
cryptoError(actualBytes < 0, "unable to read from TLS");
// Update amount of buffer used
bufUsedInc(buffer, (size_t)actualBytes);
// If zero bytes were returned then the connection was closed
if (actualBytes == 0)
tlsClientClose(this);
while (block && bufRemains(buffer) > 0);
FUNCTION_DEBUG_RESULT(SIZE, (size_t)actualBytes);
}

View File

@ -36,7 +36,7 @@ TlsClient *tlsClientNew(
Functions
***********************************************************************************************************************************/
void tlsClientOpen(TlsClient *this);
size_t tlsClientRead(TlsClient *this, Buffer *buffer);
size_t tlsClientRead(TlsClient *this, Buffer *buffer, bool block);
void tlsClientWrite(TlsClient *this, const Buffer *buffer);
void tlsClientClose(TlsClient *this);

View File

@ -103,11 +103,12 @@ storageDriverPosixFileReadOpen(StorageDriverPosixFileRead *this)
Read from a file
***********************************************************************************************************************************/
size_t
storageDriverPosixFileRead(StorageDriverPosixFileRead *this, Buffer *buffer)
storageDriverPosixFileRead(StorageDriverPosixFileRead *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(STORAGE_DRIVER_POSIX_FILE_READ, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_DEBUG_ASSERT(this != NULL && this->handle != -1);
FUNCTION_DEBUG_ASSERT(buffer != NULL && !bufFull(buffer));

View File

@ -23,7 +23,7 @@ StorageDriverPosixFileRead *storageDriverPosixFileReadNew(StorageDriverPosix *st
Functions
***********************************************************************************************************************************/
bool storageDriverPosixFileReadOpen(StorageDriverPosixFileRead *this);
size_t storageDriverPosixFileRead(StorageDriverPosixFileRead *this, Buffer *buffer);
size_t storageDriverPosixFileRead(StorageDriverPosixFileRead *this, Buffer *buffer, bool block);
void storageDriverPosixFileReadClose(StorageDriverPosixFileRead *this);
/***********************************************************************************************************************************

View File

@ -101,11 +101,12 @@ storageDriverS3FileReadOpen(StorageDriverS3FileRead *this)
Read from a file
***********************************************************************************************************************************/
size_t
storageDriverS3FileRead(StorageDriverS3FileRead *this, Buffer *buffer)
storageDriverS3FileRead(StorageDriverS3FileRead *this, Buffer *buffer, bool block)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(STORAGE_DRIVER_S3_FILE_READ, this);
FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_DEBUG_PARAM(BOOL, block);
FUNCTION_DEBUG_ASSERT(this != NULL && this->httpClient != NULL);
FUNCTION_DEBUG_ASSERT(buffer != NULL && !bufFull(buffer));

View File

@ -23,7 +23,7 @@ StorageDriverS3FileRead *storageDriverS3FileReadNew(StorageDriverS3 *storage, co
Functions
***********************************************************************************************************************************/
bool storageDriverS3FileReadOpen(StorageDriverS3FileRead *this);
size_t storageDriverS3FileRead(StorageDriverS3FileRead *this, Buffer *buffer);
size_t storageDriverS3FileRead(StorageDriverS3FileRead *this, Buffer *buffer, bool block);
/***********************************************************************************************************************************
Getters

View File

@ -330,7 +330,7 @@ Open a file for reading
StorageFileRead *
storageNewRead(const Storage *this, const String *fileExp, StorageNewReadParam param)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_BEGIN(logLevelDebug);
FUNCTION_DEBUG_PARAM(STORAGE, this);
FUNCTION_DEBUG_PARAM(STRING, fileExp);
FUNCTION_DEBUG_PARAM(BOOL, param.ignoreMissing);
@ -361,7 +361,7 @@ Open a file for writing
StorageFileWrite *
storageNewWrite(const Storage *this, const String *fileExp, StorageNewWriteParam param)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_BEGIN(logLevelDebug);
FUNCTION_DEBUG_PARAM(STORAGE, this);
FUNCTION_DEBUG_PARAM(STRING, fileExp);
FUNCTION_DEBUG_PARAM(MODE, param.modeFile);

2
test/.gitignore vendored
View File

@ -1,6 +1,6 @@
.vagrant
nytprof*
scratch.txt
scratch*
coverage*
ubuntu-bionic-18.04-cloudimg-console.log
profile

View File

@ -126,6 +126,8 @@ P00 DEBUG: storage/storage::storageExists: => false
P00 DEBUG: command/control/control::lockStopTest: => void
P00 DEBUG: command/archive/get/file::archiveGetCheck: (archiveFile: {"700000007000000070000000"})
P00 DEBUG: postgres/interface::pgControlFromFile: (pgPath: {"[TEST_PATH]/db-master/db/base"})
P00 DEBUG: storage/storage::storageNewRead: (this: {type: posix, path: {"/"}, write: false}, fileExp: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, param.ignoreMissing: false, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewRead: => {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}
P00 DEBUG: storage/storage::storageGet: (file: {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}, param.exactSize: 512)
P00 DEBUG: storage/storage::storageGet: => {used: 512, size: 512, limit: <off>}
P00 DEBUG: postgres/interface::pgControlFromFile: => {version: 90400, systemId: 1000000000000000094, walSegmentSize: 16777216, pageChecksum: true}
@ -166,6 +168,8 @@ P00 DEBUG: storage/storage::storageExists: => false
P00 DEBUG: command/control/control::lockStopTest: => void
P00 DEBUG: command/archive/get/file::archiveGetCheck: (archiveFile: {"000000010000000100000001"})
P00 DEBUG: postgres/interface::pgControlFromFile: (pgPath: {"[TEST_PATH]/db-master/db/base"})
P00 DEBUG: storage/storage::storageNewRead: (this: {type: posix, path: {"/"}, write: false}, fileExp: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, param.ignoreMissing: false, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewRead: => {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}
P00 DEBUG: storage/storage::storageGet: (file: {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}, param.exactSize: 512)
P00 DEBUG: storage/storage::storageGet: => {used: 512, size: 512, limit: <off>}
P00 DEBUG: postgres/interface::pgControlFromFile: => {version: 90400, systemId: 1000000000000000094, walSegmentSize: 16777216, pageChecksum: true}
@ -184,8 +188,12 @@ P00 DEBUG: command/archive/common::walSegmentFind: => {"0000000100000001000
P00 DEBUG: command/archive/get/file::archiveGetCheck: => {"9.4-1/0000000100000001/000000010000000100000001-ceb021d9bb41f220511e413b095d2b0d89fec113.gz"}
P00 DEBUG: storage/driver/posix/storage::storageDriverPosixNew: (path: {"/"}, modeFile: 0640, modePath: 0750, write: true, pathExpressionFunction: null)
P00 DEBUG: storage/driver/posix/storage::storageDriverPosixNew: => {StorageDriverPosix}
P00 DEBUG: storage/storage::storageNewWrite: (this: {type: posix, path: {"/"}, write: true}, fileExp: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, param.modeFile: 0000, param.modePath: 0000, param.noCreatePath: true, param.noSyncFile: true, param.noSyncPath: true, param.noAtomic: true, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewWrite: => {type: posix, name: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, modeFile: 0640, modePath: 0750, createPath: false, syncFile: false, syncPath: false, atomic: false}
P00 DEBUG: common/io/filter/group::ioFilterGroupAdd: (this: {inputSame: false, done: true}, filter: {IoFilter})
P00 DEBUG: common/io/filter/group::ioFilterGroupAdd: => void
P00 DEBUG: storage/storage::storageNewRead: (this: {type: posix, path: {"[TEST_PATH]/db-master/repo"}, write: false}, fileExp: {"<REPO:ARCHIVE>/9.4-1/0000000100000001/000000010000000100000001-ceb021d9bb41f220511e413b095d2b0d89fec113.gz"}, param.ignoreMissing: false, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewRead: => {type: posix, name: {"[TEST_PATH]/db-master/repo/archive/db/9.4-1/0000000100000001/000000010000000100000001-ceb021d9bb41f220511e413b095d2b0d89fec113.gz"}, ignoreMissing: false}
P00 DEBUG: storage/storage::storageCopy: (source: {type: posix, name: {"[TEST_PATH]/db-master/repo/archive/db/9.4-1/0000000100000001/000000010000000100000001-ceb021d9bb41f220511e413b095d2b0d89fec113.gz"}, ignoreMissing: false}, destination: {type: posix, name: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, modeFile: 0640, modePath: 0750, createPath: false, syncFile: false, syncPath: false, atomic: false})
P00 DEBUG: storage/storage::storageCopy: => true
P00 DEBUG: command/archive/get/file::archiveGetFile: => 0

View File

@ -543,6 +543,8 @@ P00 DEBUG: storage/storage::storageExists: => false
P00 DEBUG: command/control/control::lockStopTest: => void
P00 DEBUG: command/archive/get/file::archiveGetCheck: (archiveFile: {"000000010000000100000002"})
P00 DEBUG: postgres/interface::pgControlFromFile: (pgPath: {"[TEST_PATH]/db-master/db/base"})
P00 DEBUG: storage/storage::storageNewRead: (this: {type: posix, path: {"/"}, write: false}, fileExp: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, param.ignoreMissing: false, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewRead: => {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}
P00 DEBUG: storage/storage::storageGet: (file: {type: posix, name: {"[TEST_PATH]/db-master/db/base/global/pg_control"}, ignoreMissing: false}, param.exactSize: 512)
P00 DEBUG: storage/storage::storageGet: => {used: 512, size: 512, limit: <off>}
P00 DEBUG: postgres/interface::pgControlFromFile: => {version: 90300, systemId: 1000000000000000093, walSegmentSize: 16777216, pageChecksum: true}
@ -561,8 +563,12 @@ P00 DEBUG: command/archive/common::walSegmentFind: => {"0000000100000001000
P00 DEBUG: command/archive/get/file::archiveGetCheck: => {"9.3-1/0000000100000001/000000010000000100000002-488ba4b8b98acc510bce86b8f16e3c1ed9886a29.gz"}
P00 DEBUG: storage/driver/posix/storage::storageDriverPosixNew: (path: {"/"}, modeFile: 0640, modePath: 0750, write: true, pathExpressionFunction: null)
P00 DEBUG: storage/driver/posix/storage::storageDriverPosixNew: => {StorageDriverPosix}
P00 DEBUG: storage/storage::storageNewWrite: (this: {type: posix, path: {"/"}, write: true}, fileExp: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, param.modeFile: 0000, param.modePath: 0000, param.noCreatePath: true, param.noSyncFile: true, param.noSyncPath: true, param.noAtomic: true, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewWrite: => {type: posix, name: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, modeFile: 0640, modePath: 0750, createPath: false, syncFile: false, syncPath: false, atomic: false}
P00 DEBUG: common/io/filter/group::ioFilterGroupAdd: (this: {inputSame: false, done: true}, filter: {IoFilter})
P00 DEBUG: common/io/filter/group::ioFilterGroupAdd: => void
P00 DEBUG: storage/storage::storageNewRead: (this: {type: posix, path: {"[TEST_PATH]/db-master/repo"}, write: false}, fileExp: {"<REPO:ARCHIVE>/9.3-1/0000000100000001/000000010000000100000002-488ba4b8b98acc510bce86b8f16e3c1ed9886a29.gz"}, param.ignoreMissing: false, param.filterGroup: null)
P00 DEBUG: storage/storage::storageNewRead: => {type: posix, name: {"[TEST_PATH]/db-master/repo/archive/db/9.3-1/0000000100000001/000000010000000100000002-488ba4b8b98acc510bce86b8f16e3c1ed9886a29.gz"}, ignoreMissing: false}
P00 DEBUG: storage/storage::storageCopy: (source: {type: posix, name: {"[TEST_PATH]/db-master/repo/archive/db/9.3-1/0000000100000001/000000010000000100000002-488ba4b8b98acc510bce86b8f16e3c1ed9886a29.gz"}, ignoreMissing: false}, destination: {type: posix, name: {"[TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG"}, modeFile: 0640, modePath: 0750, createPath: false, syncFile: false, syncPath: false, atomic: false})
P00 DEBUG: storage/storage::storageCopy: => true
P00 DEBUG: command/archive/get/file::archiveGetFile: => 0

View File

@ -423,7 +423,7 @@ testRun(void)
strPtr(httpHeaderToLog(httpClientReponseHeader(client))), "{connection: 'close', content-length: '32'}",
" check response headers");
TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "01234567890123456789012345678901", " check response");
TEST_RESULT_UINT(httpClientRead(client, bufNew(1)), 0, " call internal read to check eof");
TEST_RESULT_UINT(httpClientRead(client, bufNew(1), true), 0, " call internal read to check eof");
// Request with content using chunked encoding
TEST_RESULT_VOID(httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, false), "request with chunked encoding");

View File

@ -294,7 +294,7 @@ testRun(void)
TEST_RESULT_VOID(bufUsedZero(buffer), " zero buffer");
TEST_RESULT_BOOL(ioReadEof(ioBufferReadIo(bufferRead)), true, " eof");
TEST_RESULT_BOOL(ioBufferRead(bufferRead, buffer), 0, " eof from driver");
TEST_RESULT_BOOL(ioBufferRead(bufferRead, buffer, true), 0, " eof from driver");
TEST_RESULT_SIZE(ioRead(ioBufferReadIo(bufferRead), buffer), 0, " read 0 bytes");
TEST_RESULT_VOID(ioReadClose(ioBufferReadIo(bufferRead)), " close buffer read object");

View File

@ -67,7 +67,13 @@ testTlsServer(void)
harnessTlsServerAccept();
harnessTlsServerExpect("some protocol info");
harnessTlsServerReply("something:0\nsome content");
harnessTlsServerReply("something:0\n");
sleepMSec(100);
harnessTlsServerReply("some ");
sleepMSec(100);
harnessTlsServerReply("contentAND MORE");
// This will cause the client to disconnect
sleepMSec(500);
@ -180,16 +186,19 @@ testRun(void)
TEST_RESULT_VOID(ioWrite(tlsClientIoWrite(client), input), "write input");
ioWriteFlush(tlsClientIoWrite(client));
Buffer *output = bufNew(12);
TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 12, "read output");
TEST_RESULT_STR(strPtr(strNewBuf(output)), "something:0\n", " check output");
TEST_RESULT_STR(strPtr(ioReadLine(tlsClientIoRead(client))), "something:0", "read line");
TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false");
output = bufNew(12);
Buffer *output = bufNew(12);
TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 12, "read output");
TEST_RESULT_STR(strPtr(strNewBuf(output)), "some content", " check output");
TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false");
output = bufNew(8);
TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 8, "read output");
TEST_RESULT_STR(strPtr(strNewBuf(output)), "AND MORE", " check output");
TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false");
output = bufNew(12);
TEST_ERROR(
ioRead(tlsClientIoRead(client), output), FileReadError,

View File

@ -749,7 +749,7 @@ testRun(void)
TEST_RESULT_INT(bufUsed(outBuffer), 0, " buffer is empty");
TEST_RESULT_VOID(
storageDriverPosixFileRead(storageFileReadDriver(file), outBuffer), " no data to load from driver either");
storageDriverPosixFileRead(storageFileReadDriver(file), outBuffer, true), " no data to load from driver either");
TEST_RESULT_INT(bufUsed(outBuffer), 0, " buffer is empty");
TEST_RESULT_BOOL(bufEq(buffer, expectedBuffer), true, " check file contents (all loaded)");