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:
parent
f1bdf3e04b
commit
59a5373cf8
@ -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"/>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -320,7 +320,7 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
ioRead(ioSessionIoRead(serverSession), buffer);
|
||||
ioRead(ioSessionIoReadP(serverSession), buffer);
|
||||
}
|
||||
CATCH(FileReadError)
|
||||
{
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user