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

Handle TLS servers that do not close connections gracefully.

Some TLS server implementations will simply close the socket rather than correctly closing the TLS connection. This causes problems when connection: close is specified with no content-length or chunked encoding and we are forced to read to EOF. It is hard to know if this is a real EOF or a network error.

In cases where we can parse the content and (hopefully) ensure it is correct, allow the closed socket to serve as EOF. This is not ideal, but the change in 8e1807c means that currently working servers with this issue will stop working after 2.35 is installed, which seems too risky.
This commit is contained in:
David Steele 2022-03-02 11:38:52 -06:00 committed by GitHub
parent f1bdf3e04b
commit 59a5373cf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 184 additions and 49 deletions

View File

@ -108,6 +108,21 @@
<p>Retry on page validation failure during <cmd>backup</cmd>.</p>
</release-item>
<release-item>
<github-issue id="1638"/>
<github-pull-request id="1677"/>
<release-item-contributor-list>
<release-item-contributor id="david.steele"/>
<!-- Actually tester, but we don't have a tag for that yet -->
<release-item-reviewer id="remi.vidier"/>
<release-item-reviewer id="david.christensen"/>
<release-item-reviewer id="stephen.frost"/>
</release-item-contributor-list>
<p>Handle <proper>TLS</proper> servers that do not close connections gracefully.</p>
</release-item>
<release-item>
<github-pull-request id="1610"/>

View File

@ -37,7 +37,7 @@ cmdServerPing(void)
// Send ping
ProtocolClient *const protocolClient = protocolClientNew(
strNewFmt(PROTOCOL_SERVICE_REMOTE " socket protocol on '%s'", strZ(host)), PROTOCOL_SERVICE_REMOTE_STR,
ioSessionIoRead(tlsSession), ioSessionIoWrite(tlsSession));
ioSessionIoReadP(tlsSession), ioSessionIoWrite(tlsSession));
protocolClientNoExit(protocolClient);
protocolClientNoOp(protocolClient);
protocolClientFree(protocolClient);

View File

@ -29,6 +29,8 @@ STRING_EXTERN(HTTP_HEADER_CONTENT_MD5_STR, HTTP_HEADER_
STRING_EXTERN(HTTP_HEADER_CONTENT_RANGE_STR, HTTP_HEADER_CONTENT_RANGE);
STRING_EXTERN(HTTP_HEADER_CONTENT_TYPE_STR, HTTP_HEADER_CONTENT_TYPE);
STRING_EXTERN(HTTP_HEADER_CONTENT_TYPE_APP_FORM_URL_STR, HTTP_HEADER_CONTENT_TYPE_APP_FORM_URL);
STRING_EXTERN(HTTP_HEADER_CONTENT_TYPE_JSON_STR, HTTP_HEADER_CONTENT_TYPE_JSON);
STRING_EXTERN(HTTP_HEADER_CONTENT_TYPE_XML_STR, HTTP_HEADER_CONTENT_TYPE_XML);
STRING_EXTERN(HTTP_HEADER_ETAG_STR, HTTP_HEADER_ETAG);
STRING_EXTERN(HTTP_HEADER_DATE_STR, HTTP_HEADER_DATE);
STRING_EXTERN(HTTP_HEADER_HOST_STR, HTTP_HEADER_HOST);

View File

@ -49,6 +49,10 @@ HTTP Constants
STRING_DECLARE(HTTP_HEADER_CONTENT_TYPE_STR);
#define HTTP_HEADER_CONTENT_TYPE_APP_FORM_URL "application/x-www-form-urlencoded"
STRING_DECLARE(HTTP_HEADER_CONTENT_TYPE_APP_FORM_URL_STR);
#define HTTP_HEADER_CONTENT_TYPE_JSON "application/json"
STRING_DECLARE(HTTP_HEADER_CONTENT_TYPE_JSON_STR);
#define HTTP_HEADER_CONTENT_TYPE_XML "application/xml"
STRING_DECLARE(HTTP_HEADER_CONTENT_TYPE_XML_STR);
#define HTTP_HEADER_CONTENT_RANGE_BYTES "bytes"
#define HTTP_HEADER_DATE "date"
STRING_DECLARE(HTTP_HEADER_DATE_STR);

View File

@ -76,6 +76,21 @@ httpResponseDone(HttpResponse *this)
/***********************************************************************************************************************************
Read content
***********************************************************************************************************************************/
// Helper to determine if it is OK to accept unexpected EOF, e.g. the server closed the socket with properly closing the TLS
// connection. The idea is that since these types of responses can be validated we should be able to detect a short read.
static bool
httpResponseReadIgnoreUnexpectedEof(const HttpResponse *const this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(HTTP_RESPONSE, this);
FUNCTION_LOG_END();
const String *const contentType = httpHeaderGet(this->pub.header, HTTP_HEADER_CONTENT_TYPE_STR);
FUNCTION_LOG_RETURN(
BOOL, strEq(contentType, HTTP_HEADER_CONTENT_TYPE_XML_STR) || strEq(contentType, HTTP_HEADER_CONTENT_TYPE_JSON_STR));
}
static size_t
httpResponseRead(THIS_VOID, Buffer *buffer, bool block)
{
@ -99,17 +114,20 @@ httpResponseRead(THIS_VOID, Buffer *buffer, bool block)
{
MEM_CONTEXT_TEMP_BEGIN()
{
IoRead *rawRead = httpSessionIoRead(this->session);
// If close was requested and no content specified then the server may send content up until the eof
if (this->closeOnContentEof && !this->contentChunked && this->contentSize == 0)
{
IoRead *const rawRead = httpSessionIoReadP(
this->session, .ignoreUnexpectedEof = httpResponseReadIgnoreUnexpectedEof(this));
ioRead(rawRead, buffer);
this->contentEof = ioReadEof(rawRead);
}
// Else read using specified encoding or size
else
{
IoRead *const rawRead = httpSessionIoReadP(this->session);
do
{
// If chunked content and no content remaining
@ -218,7 +236,7 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
MEM_CONTEXT_TEMP_BEGIN()
{
// Read status
String *status = ioReadLine(httpSessionIoRead(this->session));
String *status = ioReadLine(httpSessionIoReadP(this->session));
// Check status ends with a CR and remove it to make error formatting easier and more accurate
if (!strEndsWith(status, CR_STR))
@ -260,7 +278,7 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
do
{
// Read the next header
String *header = strTrim(ioReadLine(httpSessionIoRead(this->session)));
String *const header = strTrim(ioReadLine(httpSessionIoReadP(this->session)));
// If the header is empty then we have reached the end of the headers
if (strSize(header) == 0)

View File

@ -63,15 +63,16 @@ httpSessionDone(HttpSession *this)
/**********************************************************************************************************************************/
IoRead *
httpSessionIoRead(HttpSession *this)
httpSessionIoRead(HttpSession *const this, const HttpSessionIoReadParam param)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(HTTP_SESSION, this);
FUNCTION_TEST_PARAM(BOOL, param.ignoreUnexpectedEof);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(ioSessionIoRead(this->ioSession));
FUNCTION_TEST_RETURN(ioSessionIoReadP(this->ioSession, .ignoreUnexpectedEof = param.ignoreUnexpectedEof));
}
/**********************************************************************************************************************************/

View File

@ -39,7 +39,16 @@ void httpSessionDone(HttpSession *this);
Getters/Setters
***********************************************************************************************************************************/
// Read interface
IoRead *httpSessionIoRead(HttpSession *this);
typedef struct HttpSessionIoReadParam
{
VAR_PARAM_HEADER;
bool ignoreUnexpectedEof;
} HttpSessionIoReadParam;
#define httpSessionIoReadP(this, ...) \
httpSessionIoRead(this, (HttpSessionIoReadParam){VAR_PARAM_INIT, __VA_ARGS__})
IoRead *httpSessionIoRead(HttpSession *this, HttpSessionIoReadParam param);
// Write interface
IoWrite *httpSessionIoWrite(HttpSession *this);

View File

@ -51,10 +51,19 @@ ioSessionAuthenticated(const IoSession *const this)
int ioSessionFd(IoSession *this);
// Read interface
__attribute__((always_inline)) static inline IoRead *
ioSessionIoRead(IoSession *const this)
typedef struct IoSessionIoReadParam
{
return THIS_PUB(IoSession)->interface->ioRead(THIS_PUB(IoSession)->driver);
VAR_PARAM_HEADER;
bool ignoreUnexpectedEof; // Allow unexpected EOF, e.g. TLS session improperly terminated
} IoSessionIoReadParam;
#define ioSessionIoReadP(this, ...) \
ioSessionIoRead(this, (IoSessionIoReadParam){VAR_PARAM_INIT, __VA_ARGS__})
__attribute__((always_inline)) static inline IoRead *
ioSessionIoRead(IoSession *const this, const IoSessionIoReadParam param)
{
return THIS_PUB(IoSession)->interface->ioRead(THIS_PUB(IoSession)->driver, param.ignoreUnexpectedEof);
}
// Write interface

View File

@ -22,7 +22,7 @@ typedef struct IoSessionInterface
int (*fd)(void *driver);
// IoRead interface for the session
IoRead *(*ioRead)(void *driver);
IoRead *(*ioRead)(void *driver, bool ignoreUnexpectedEof);
// IoWrite interface for the session
IoWrite *(*ioWrite)(void *driver);

View File

@ -106,12 +106,13 @@ sckSessionFd(THIS_VOID)
/**********************************************************************************************************************************/
static IoRead *
sckSessionIoRead(THIS_VOID)
sckSessionIoRead(THIS_VOID, const bool ignoreUnexpectedEof)
{
THIS(SocketSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_SESSION, this);
(void)ignoreUnexpectedEof; // Unused
FUNCTION_TEST_END();
ASSERT(this != NULL);

View File

@ -25,6 +25,7 @@ typedef struct TlsSession
SSL *session; // TLS session on the file descriptor
TimeMSec timeout; // Timeout for any i/o operation (read, write, etc.)
bool shutdownOnClose; // Shutdown the TLS connection when closing the session
bool ignoreUnexpectedEof; // Ignore unexpected EOF when requested
IoRead *read; // Read interface
IoWrite *write; // Write interface
@ -128,18 +129,30 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
{
// The connection was closed
case SSL_ERROR_ZERO_RETURN:
// A syscall failed (this usually indicates unexpected eof)
case SSL_ERROR_SYSCALL:
{
if (!closeOk)
THROW(ProtocolError, "unexpected TLS eof");
// Error on SSL_ERROR_SYSCALL if unexpected EOF is not allowed
if (errorTls == SSL_ERROR_SYSCALL && !this->ignoreUnexpectedEof)
{
THROW_SYS_ERROR_CODE(errorSys, KernelError, "TLS syscall error");
}
// Else close the connection if we are in a state where it is allowed, e.g. not connecting
else
{
if (!closeOk)
THROW(ProtocolError, "unexpected TLS eof");
this->shutdownOnClose = false;
tlsSessionClose(this);
}
this->shutdownOnClose = false;
tlsSessionClose(this);
break;
}
// Try again after waiting for read ready
case SSL_ERROR_WANT_READ:
ioReadReadyP(ioSessionIoRead(this->ioSession), .error = true);
ioReadReadyP(ioSessionIoReadP(this->ioSession), .error = true);
result = 0;
break;
@ -149,10 +162,6 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
result = 0;
break;
// A syscall failed (this usually indicates unexpected eof)
case SSL_ERROR_SYSCALL:
THROW_SYS_ERROR_CODE(errorSys, KernelError, "TLS syscall error");
// Any other error that we cannot handle
default:
{
@ -220,7 +229,7 @@ tlsSessionRead(THIS_VOID, Buffer *buffer, bool block)
{
// If no TLS data pending then check the io to reduce blocking
if (!SSL_pending(this->session))
ioReadReadyP(ioSessionIoRead(this->ioSession), .error = true);
ioReadReadyP(ioSessionIoReadP(this->ioSession), .error = true);
// Read and handle errors. The error queue must be cleared before this operation.
ERR_clear_error();
@ -292,16 +301,19 @@ tlsSessionEof(THIS_VOID)
/**********************************************************************************************************************************/
static IoRead *
tlsSessionIoRead(THIS_VOID)
tlsSessionIoRead(THIS_VOID, const bool ignoreUnexpectedEof)
{
THIS(TlsSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TLS_SESSION, this);
FUNCTION_TEST_PARAM(BOOL, ignoreUnexpectedEof);
FUNCTION_TEST_END();
ASSERT(this != NULL);
this->ignoreUnexpectedEof = ignoreUnexpectedEof;
FUNCTION_TEST_RETURN(this->read);
}

View File

@ -373,7 +373,7 @@ protocolServer(IoServer *const tlsServer, IoSession *const socketSession)
IoSession *const tlsSession = ioServerAccept(tlsServer, socketSession);
result = protocolServerNew(
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoRead(tlsSession),
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoReadP(tlsSession),
ioSessionIoWrite(tlsSession));
// If session is authenticated
@ -681,7 +681,7 @@ protocolRemoteExec(
.keyFile = cfgOptionIdxStr(isRepo ? cfgOptRepoHostKeyFile : cfgOptPgHostKeyFile, hostIdx));
helper->ioSession = ioClientOpen(helper->ioClient);
read = ioSessionIoRead(helper->ioSession);
read = ioSessionIoReadP(helper->ioSession);
write = ioSessionIoWrite(helper->ioSession);
break;

View File

@ -320,7 +320,7 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
TRY_BEGIN()
{
ioRead(ioSessionIoRead(serverSession), buffer);
ioRead(ioSessionIoReadP(serverSession), buffer);
}
CATCH(FileReadError)
{

View File

@ -598,7 +598,9 @@ testRun(void)
TEST_TITLE("error with content");
hrnServerScriptExpectZ(http, "GET /?a=b HTTP/1.1\r\n" TEST_USER_AGENT "hdr1:1\r\nhdr2:2\r\n\r\n");
hrnServerScriptReplyZ(http, "HTTP/1.1 403 \r\ncontent-length:7\r\n\r\nCONTENT");
hrnServerScriptReplyZ(http, "HTTP/1.1 403 \r\nconnection:close\r\ncontent-type:application/json\r\n\r\nCONTENT");
hrnServerScriptClose(http);
StringList *headerRedact = strLstNew();
strLstAdd(headerRedact, STRDEF("hdr2"));
@ -616,7 +618,8 @@ testRun(void)
TEST_RESULT_UINT(httpResponseCode(response), 403, "check response code");
TEST_RESULT_STR_Z(httpResponseReason(response), "", "check empty response message");
TEST_RESULT_STR_Z(
httpHeaderToLog(httpResponseHeader(response)), "{content-length: '7'}", "check response headers");
httpHeaderToLog(httpResponseHeader(response)), "{connection: 'close', content-type: 'application/json'}",
"check response headers");
TEST_RESULT_STR_Z(strNewBuf(httpResponseContent(response)), "CONTENT", "check response content");
TEST_ERROR(
@ -628,22 +631,68 @@ testRun(void)
"hdr1: 1\n"
"hdr2: <redacted>\n"
"*** Response Headers ***:\n"
"content-length: 7\n"
"connection: close\n"
"content-type: application/json\n"
"*** Response Content ***:\n"
"CONTENT");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("end server process");
hrnServerScriptEnd(http);
}
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
HRN_FORK_BEGIN()
{
// Start HTTPS test server
HRN_FORK_CHILD_BEGIN(.prefix = "test server", .timeout = 5000)
{
// Set buffer size large enough for server to read expect messages
ioBufferSizeSet(65536);
TEST_RESULT_VOID(hrnServerRunP(HRN_FORK_CHILD_READ(), hrnServerProtocolTls), "http server run");
}
HRN_FORK_CHILD_END();
HRN_FORK_PARENT_BEGIN()
{
IoWrite *http = hrnServerScriptBegin(HRN_FORK_PARENT_WRITE(0));
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("create client");
ioBufferSizeSet(35);
TEST_ASSIGN(
client,
httpClientNew(
tlsClientNewP(
sckClientNew(hrnServerHost(), hrnServerPort(0), 5000, 5000), hrnServerHost(), 0, 0, TEST_IN_CONTAINER),
5000),
"new client");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("request with content using content-length");
hrnServerScriptAccept(http);
hrnServerScriptExpectZ(
http, "GET /path/file%201.txt HTTP/1.1\r\n" TEST_USER_AGENT "content-length:30\r\n\r\n"
"012345678901234567890123456789");
hrnServerScriptReplyZ(http, "HTTP/1.1 200 OK\r\nConnection:ClosE\r\n\r\n01234567890123456789012345678901");
hrnServerScriptReplyZ(
http,
"HTTP/1.1 200 OK\r\nConnection:ClosE\r\ncontent-type:application/xml\r\n\r\n01234567890123456789012345678901");
hrnServerScriptClose(http);
// Abort connect without proper TLS shutdown to show that all bytes are read correctly for content type xml
hrnServerScriptAbort(http);
ioBufferSizeSet(30);
HttpResponse *response = NULL;
TEST_ASSIGN(
response,
httpRequestResponse(
@ -652,8 +701,10 @@ testRun(void)
.header = httpHeaderAdd(httpHeaderNew(NULL), STRDEF("content-length"), STRDEF("30")),
.content = BUFSTRDEF("012345678901234567890123456789")), true),
"request");
TEST_RESULT_BOOL(httpResponseReadIgnoreUnexpectedEof(response), true, "check unexpected eof allowed");
TEST_RESULT_STR_Z(
httpHeaderToLog(httpResponseHeader(response)), "{connection: 'close'}", "check response headers");
httpHeaderToLog(httpResponseHeader(response)), "{connection: 'close', content-type: 'application/xml'}",
"check response headers");
TEST_RESULT_STR_Z(strNewBuf(httpResponseContent(response)), "01234567890123456789012345678901", "check response");
TEST_RESULT_UINT(httpResponseRead(response, bufNew(1), true), 0, "call internal read to check eof");

View File

@ -703,7 +703,7 @@ testRun(void)
"client open");
TEST_ERROR(
ioRead(ioSessionIoRead(clientSession), bufNew(1)), ServiceError,
ioRead(ioSessionIoReadP(clientSession), bufNew(1)), ServiceError,
"TLS error [1:336151576] tlsv1 alert unknown ca");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
@ -721,7 +721,7 @@ testRun(void)
"client open");
Buffer *buffer = bufNew(7);
TEST_RESULT_VOID(ioRead(ioSessionIoRead(clientSession), buffer), "client read");
TEST_RESULT_VOID(ioRead(ioSessionIoReadP(clientSession), buffer), "client read");
TEST_RESULT_STR_Z(strNewBuf(buffer), "message", "check read");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
@ -738,7 +738,7 @@ testRun(void)
"client open");
buffer = bufNew(8);
TEST_RESULT_VOID(ioRead(ioSessionIoRead(clientSession), buffer), "client read");
TEST_RESULT_VOID(ioRead(ioSessionIoReadP(clientSession), buffer), "client read");
TEST_RESULT_STR_Z(strNewBuf(buffer), "message2", "check read");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
@ -807,8 +807,8 @@ testRun(void)
TEST_RESULT_VOID(ioWrite(ioSessionIoWrite(session), input), "write input");
ioWriteFlush(ioSessionIoWrite(session));
TEST_RESULT_STR_Z(ioReadLine(ioSessionIoRead(session)), "something:0", "read line");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_STR_Z(ioReadLine(ioSessionIoReadP(session)), "something:0", "read line");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoReadP(session)), false, "check eof = false");
hrnServerScriptSleep(tls, 100);
hrnServerScriptReplyZ(tls, "some ");
@ -817,14 +817,14 @@ testRun(void)
hrnServerScriptReplyZ(tls, "contentAND MORE");
Buffer *output = bufNew(12);
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 12, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoReadP(session), output), 12, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "some content", "check output");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoReadP(session)), false, "check eof = false");
output = bufNew(8);
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 8, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoReadP(session), output), 8, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "AND MORE", "check output");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoReadP(session)), false, "check eof = false");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("read eof");
@ -834,7 +834,7 @@ testRun(void)
output = bufNew(12);
((IoFdRead *)((SocketSession *)tlsSession->ioSession->pub.driver)->read->pub.driver)->timeout = 100;
TEST_ERROR_FMT(
ioRead(ioSessionIoRead(session), output), FileReadError,
ioRead(ioSessionIoReadP(session), output), FileReadError,
"timeout after 100ms waiting for read from '%s:%u'", strZ(hrnServerHost()), hrnServerPort(0));
((IoFdRead *)((SocketSession *)tlsSession->ioSession->pub.driver)->read->pub.driver)->timeout = 5000;
@ -851,13 +851,13 @@ testRun(void)
ioWriteFlush(ioSessionIoWrite(session));
output = bufNew(12);
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 12, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoReadP(session), output), 12, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "0123456789AB", "check output");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoReadP(session)), false, "check eof = false");
output = bufNew(12);
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 0, "read no output after eof");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), true, "check eof = true");
TEST_RESULT_UINT(ioRead(ioSessionIoReadP(session), output), 0, "read no output after eof");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoReadP(session)), true, "check eof = true");
TEST_RESULT_VOID(ioSessionClose(session), "close again");
@ -873,7 +873,20 @@ testRun(void)
socketLocal.block = false;
output = bufNew(13);
TEST_ERROR(ioRead(ioSessionIoRead(session), output), KernelError, "TLS syscall error");
TEST_ERROR(ioRead(ioSessionIoReadP(session), output), KernelError, "TLS syscall error");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("aborted connection ignored and read complete (non-blocking socket)");
hrnServerScriptAccept(tls);
hrnServerScriptReplyZ(tls, "0123456789AC");
hrnServerScriptAbort(tls);
TEST_ASSIGN(session, ioClientOpen(client), "open client again (was closed by server)");
output = bufNew(13);
TEST_RESULT_VOID(ioRead(ioSessionIoReadP(session, .ignoreUnexpectedEof = true), output), "ignore syscall error");
TEST_RESULT_STR_Z(strNewBuf(output), "0123456789AC", "all bytes read");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("close connection");

View File

@ -728,7 +728,7 @@ testRun(void)
// Send ping
ProtocolClient *protocolClient = protocolClientNew(
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoRead(tlsSession),
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoReadP(tlsSession),
ioSessionIoWrite(tlsSession));
protocolClientNoExit(protocolClient);
protocolClientNoOp(protocolClient);