mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-03-21 21:17:22 +02:00
Non-blocking TLS implementation.
The prior blocking implementation seemed to be prone to locking up on some (especially recent) kernel versions. Since we were unable to reproduce the issue in a development environment we can only speculate as to the cause, but there is a good chance that blocking sockets were the issue or contributed to the issue. So move to a non-blocking implementation to hopefully clear up these issues. Testing in production environments that were prone to locking shows that the approach is promising and at the very least not a regression. The main differences from the blocking version are the non-blocking connect() implementation and handling of WANT_READ/WANT_WRITE retries for all SSL*() functions. Timeouts in the tests needed to be increased because socket connect() and TLS SSL_connect() were not included in the timeout before. The tests don't run any slower, though. In fact, all platforms but Ubuntu 12.04 worked fine with the shorter timeouts.
This commit is contained in:
parent
2260a7512a
commit
c88684e2bf
@ -27,6 +27,17 @@
|
|||||||
</release-bug-list>
|
</release-bug-list>
|
||||||
|
|
||||||
<release-improvement-list>
|
<release-improvement-list>
|
||||||
|
<release-item>
|
||||||
|
<release-item-contributor-list>
|
||||||
|
<release-item-reviewer id="cynthia.shang"/>
|
||||||
|
<release-item-reviewer id="stephen.frost"/>
|
||||||
|
<!-- Actually tester, but we don't have a tag for that yet -->
|
||||||
|
<release-item-reviewer id="slava.pagerduty"/>
|
||||||
|
</release-item-contributor-list>
|
||||||
|
|
||||||
|
<p>Non-blocking TLS implementation.</p>
|
||||||
|
</release-item>
|
||||||
|
|
||||||
<release-item>
|
<release-item>
|
||||||
<release-item-contributor-list>
|
<release-item-contributor-list>
|
||||||
<release-item-reviewer id="cynthia.shang"/>
|
<release-item-reviewer id="cynthia.shang"/>
|
||||||
@ -8375,6 +8386,11 @@
|
|||||||
<contributor-id type="github">slardiere</contributor-id>
|
<contributor-id type="github">slardiere</contributor-id>
|
||||||
</contributor>
|
</contributor>
|
||||||
|
|
||||||
|
<contributor id="slava.pagerduty">
|
||||||
|
<contributor-name-display>slava-pagerduty</contributor-name-display>
|
||||||
|
<contributor-id type="github">slava-pagerduty</contributor-id>
|
||||||
|
</contributor>
|
||||||
|
|
||||||
<contributor id="stefan.fercot">
|
<contributor id="stefan.fercot">
|
||||||
<contributor-name-display>Stefan Fercot</contributor-name-display>
|
<contributor-name-display>Stefan Fercot</contributor-name-display>
|
||||||
<contributor-id type="github">pgstef</contributor-id>
|
<contributor-id type="github">pgstef</contributor-id>
|
||||||
|
@ -3,10 +3,8 @@ Socket Client
|
|||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
#include "build.auto.h"
|
#include "build.auto.h"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/select.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -129,9 +127,7 @@ sckClientOpen(SocketClient *this)
|
|||||||
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
|
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
|
||||||
|
|
||||||
sckOptionSet(fd);
|
sckOptionSet(fd);
|
||||||
|
sckConnect(fd, this->host, this->port, hostAddress, waitRemaining(wait));
|
||||||
if (connect(fd, hostAddress->ai_addr, hostAddress->ai_addrlen) == -1)
|
|
||||||
THROW_SYS_ERROR_FMT(HostConnectError, "unable to connect to '%s:%u'", strPtr(this->host), this->port);
|
|
||||||
}
|
}
|
||||||
FINALLY()
|
FINALLY()
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,8 @@ static struct SocketLocal
|
|||||||
{
|
{
|
||||||
bool init; // sckInit() has been called
|
bool init; // sckInit() has been called
|
||||||
|
|
||||||
|
bool block; // Use blocking mode socket
|
||||||
|
|
||||||
bool keepAlive; // Are socket keep alives enabled?
|
bool keepAlive; // Are socket keep alives enabled?
|
||||||
int tcpKeepAliveCount; // TCP keep alive count (0 disables)
|
int tcpKeepAliveCount; // TCP keep alive count (0 disables)
|
||||||
int tcpKeepAliveIdle; // TCP keep alive idle (0 disables)
|
int tcpKeepAliveIdle; // TCP keep alive idle (0 disables)
|
||||||
@ -44,6 +46,7 @@ sckInit(bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle, int tcpKeep
|
|||||||
ASSERT(tcpKeepAliveInterval >= 0);
|
ASSERT(tcpKeepAliveInterval >= 0);
|
||||||
|
|
||||||
socketLocal.init = true;
|
socketLocal.init = true;
|
||||||
|
socketLocal.block = false;
|
||||||
socketLocal.keepAlive = keepAlive;
|
socketLocal.keepAlive = keepAlive;
|
||||||
socketLocal.tcpKeepAliveCount = tcpKeepAliveCount;
|
socketLocal.tcpKeepAliveCount = tcpKeepAliveCount;
|
||||||
socketLocal.tcpKeepAliveIdle = tcpKeepAliveIdle;
|
socketLocal.tcpKeepAliveIdle = tcpKeepAliveIdle;
|
||||||
@ -71,6 +74,15 @@ sckOptionSet(int fd)
|
|||||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &socketValue, sizeof(int)) == -1, ProtocolError, "unable set TCP_NODELAY");
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &socketValue, sizeof(int)) == -1, ProtocolError, "unable set TCP_NODELAY");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Put the socket in non-blocking mode
|
||||||
|
if (!socketLocal.block)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
// Automatically close the socket (in the child process) on a successful execve() call. Connections are never shared between
|
// Automatically close the socket (in the child process) on a successful execve() call. Connections are never shared between
|
||||||
// processes so there is no reason to leave them open.
|
// processes so there is no reason to leave them open.
|
||||||
#ifdef F_SETFD
|
#ifdef F_SETFD
|
||||||
@ -124,6 +136,55 @@ sckOptionSet(int fd)
|
|||||||
FUNCTION_TEST_RETURN_VOID();
|
FUNCTION_TEST_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********************************************************************************************************************************/
|
||||||
|
static bool
|
||||||
|
sckConnectInProgress(int errNo)
|
||||||
|
{
|
||||||
|
return errNo == EINPROGRESS || errNo == EINTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sckConnect(int fd, const String *host, unsigned int port, const struct addrinfo *hostAddress, TimeMSec timeout)
|
||||||
|
{
|
||||||
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||||
|
FUNCTION_LOG_PARAM(INT, fd);
|
||||||
|
FUNCTION_LOG_PARAM(STRING, host);
|
||||||
|
FUNCTION_LOG_PARAM(UINT, port);
|
||||||
|
FUNCTION_LOG_PARAM_P(VOID, hostAddress);
|
||||||
|
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||||
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
|
ASSERT(host != NULL);
|
||||||
|
ASSERT(hostAddress != NULL);
|
||||||
|
|
||||||
|
// Attempt connection
|
||||||
|
if (connect(fd, hostAddress->ai_addr, hostAddress->ai_addrlen) == -1)
|
||||||
|
{
|
||||||
|
// Save the error
|
||||||
|
int errNo = errno;
|
||||||
|
|
||||||
|
// The connection has started but since we are in non-blocking mode it has not completed yet
|
||||||
|
if (sckConnectInProgress(errNo))
|
||||||
|
{
|
||||||
|
// Wait for write-ready
|
||||||
|
if (!sckReadyWrite(fd, timeout))
|
||||||
|
THROW_FMT(HostConnectError, "timeout connecting to '%s:%u'", strPtr(host), port);
|
||||||
|
|
||||||
|
// Check for success or error. If the connection was successful this will set errNo to 0.
|
||||||
|
socklen_t errNoLen = sizeof(errNo);
|
||||||
|
|
||||||
|
THROW_ON_SYS_ERROR(
|
||||||
|
getsockopt(fd, SOL_SOCKET, SO_ERROR, &errNo, &errNoLen) == -1, HostConnectError, "unable to get socket error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw error if it is still set
|
||||||
|
if (errNo != 0)
|
||||||
|
THROW_SYS_ERROR_CODE_FMT(errNo, HostConnectError, "unable to connect to '%s:%u'", strPtr(host), port);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
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.
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
@ -4,7 +4,10 @@ Socket Common Functions
|
|||||||
#ifndef COMMON_IO_SOCKET_COMMON_H
|
#ifndef COMMON_IO_SOCKET_COMMON_H
|
||||||
#define COMMON_IO_SOCKET_COMMON_H
|
#define COMMON_IO_SOCKET_COMMON_H
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
#include "common/time.h"
|
#include "common/time.h"
|
||||||
|
#include "common/type/string.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Functions
|
Functions
|
||||||
@ -15,6 +18,9 @@ void sckInit(bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle, int tc
|
|||||||
// Set options on a socket
|
// Set options on a socket
|
||||||
void sckOptionSet(int fd);
|
void sckOptionSet(int fd);
|
||||||
|
|
||||||
|
// Connect socket to an IP address
|
||||||
|
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
|
// Wait until the socket is ready to read/write or timeout
|
||||||
bool sckReady(int fd, bool read, bool write, TimeMSec timeout);
|
bool sckReady(int fd, bool read, bool write, TimeMSec timeout);
|
||||||
bool sckReadyRead(int fd, TimeMSec timeout);
|
bool sckReadyRead(int fd, TimeMSec timeout);
|
||||||
|
@ -75,52 +75,92 @@ tlsSessionClose(TlsSession *this, bool shutdown)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Report TLS errors. Returns true if the command should continue and false if it should exit.
|
Process result from SSL_read(), SSL_write(), SSL_connect(), and SSL_accept().
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
0 if the function should be tried again with the same parameters
|
||||||
|
-1 if the connection was closed gracefully
|
||||||
|
> 0 with the read/write size if SSL_read()/SSL_write() was called
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
static bool
|
// Helper to process error conditions
|
||||||
tlsSessionError(TlsSession *this, int code)
|
static int
|
||||||
|
tlsSessionResultProcess(TlsSession *this, int errorTls, int errorSys, bool closeOk)
|
||||||
{
|
{
|
||||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||||
FUNCTION_LOG_PARAM(TLS_SESSION, this);
|
FUNCTION_LOG_PARAM(TLS_SESSION, this);
|
||||||
FUNCTION_LOG_PARAM(INT, code);
|
FUNCTION_LOG_PARAM(INT, errorTls);
|
||||||
|
FUNCTION_LOG_PARAM(INT, errorSys);
|
||||||
|
FUNCTION_LOG_PARAM(BOOL, closeOk);
|
||||||
FUNCTION_LOG_END();
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
bool result = false;
|
ASSERT(this != NULL);
|
||||||
|
ASSERT(this->session != NULL);
|
||||||
|
|
||||||
switch (code)
|
int result = -1;
|
||||||
|
|
||||||
|
switch (errorTls)
|
||||||
{
|
{
|
||||||
// The connection was closed
|
// The connection was closed
|
||||||
case SSL_ERROR_ZERO_RETURN:
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
{
|
{
|
||||||
|
if (!closeOk)
|
||||||
|
THROW(ProtocolError, "unexpected TLS eof");
|
||||||
|
|
||||||
tlsSessionClose(this, false);
|
tlsSessionClose(this, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try the read/write again
|
// Try again after waiting for read ready
|
||||||
case SSL_ERROR_WANT_READ:
|
case SSL_ERROR_WANT_READ:
|
||||||
|
{
|
||||||
|
sckSessionReadyRead(this->socketSession);
|
||||||
|
result = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try again after waiting for write ready
|
||||||
case SSL_ERROR_WANT_WRITE:
|
case SSL_ERROR_WANT_WRITE:
|
||||||
{
|
{
|
||||||
result = true;
|
sckSessionReadyWrite(this->socketSession);
|
||||||
|
result = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A syscall failed (usually indicates unexpected eof)
|
// A syscall failed (this usually indicates unexpected eof)
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
{
|
THROW_SYS_ERROR_CODE(errorSys, KernelError, "TLS syscall error");
|
||||||
// Get the error before closing so it is not cleared
|
|
||||||
int errNo = errno;
|
|
||||||
tlsSessionClose(this, false);
|
|
||||||
|
|
||||||
// Throw the sys error
|
// Any other error that we cannot handle
|
||||||
THROW_SYS_ERROR_CODE(errNo, KernelError, "tls failed syscall");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some other tls error that cannot be handled
|
|
||||||
default:
|
default:
|
||||||
THROW_FMT(ServiceError, "tls error [%d]", code);
|
THROW_FMT(ServiceError, "TLS error [%d]", errorTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN(BOOL, result);
|
FUNCTION_LOG_RETURN(INT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlsSessionResult(TlsSession *this, int result, bool closeOk)
|
||||||
|
{
|
||||||
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||||
|
FUNCTION_LOG_PARAM(TLS_SESSION, this);
|
||||||
|
FUNCTION_LOG_PARAM(INT, result);
|
||||||
|
FUNCTION_LOG_PARAM(BOOL, closeOk);
|
||||||
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
|
ASSERT(this != NULL);
|
||||||
|
ASSERT(this->session != NULL);
|
||||||
|
|
||||||
|
// Process errors
|
||||||
|
if (result <= 0)
|
||||||
|
{
|
||||||
|
// Get TLS error and store errno in case of syscall error
|
||||||
|
int errorTls = SSL_get_error(this->session, result);
|
||||||
|
int errorSys = errno;
|
||||||
|
|
||||||
|
result = tlsSessionResultProcess(this, errorTls, errorSys, closeOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION_LOG_RETURN(INT, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
@ -142,27 +182,26 @@ tlsSessionRead(THIS_VOID, Buffer *buffer, bool block)
|
|||||||
ASSERT(buffer != NULL);
|
ASSERT(buffer != NULL);
|
||||||
ASSERT(!bufFull(buffer));
|
ASSERT(!bufFull(buffer));
|
||||||
|
|
||||||
ssize_t result = 0;
|
int result = 0;
|
||||||
|
|
||||||
// If blocking read keep reading until buffer is full
|
// If blocking read keep reading until buffer is full
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// If no tls data pending then check the socket
|
// 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);
|
sckSessionReadyRead(this->socketSession);
|
||||||
|
|
||||||
// Read and handle errors
|
// Read and handle errors
|
||||||
result = SSL_read(this->session, bufRemainsPtr(buffer), (int)bufRemains(buffer));
|
result = tlsSessionResult(this, SSL_read(this->session, bufRemainsPtr(buffer), (int)bufRemains(buffer)), true);
|
||||||
|
|
||||||
if (result <= 0)
|
|
||||||
{
|
|
||||||
// Break if the error indicates that we should not continue trying
|
|
||||||
if (!tlsSessionError(this, SSL_get_error(this->session, (int)result)))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Update amount of buffer used
|
// Update amount of buffer used
|
||||||
else
|
if (result > 0)
|
||||||
|
{
|
||||||
bufUsedInc(buffer, (size_t)result);
|
bufUsedInc(buffer, (size_t)result);
|
||||||
|
}
|
||||||
|
// If the connection was closed then we are at eof. It is up to the layer above TLS to decide if this is an error.
|
||||||
|
else if (result == -1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
while (block && bufRemains(buffer) > 0);
|
while (block && bufRemains(buffer) > 0);
|
||||||
|
|
||||||
@ -170,51 +209,8 @@ tlsSessionRead(THIS_VOID, Buffer *buffer, bool block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Write to the tls session
|
Write to the TLS session
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
static bool
|
|
||||||
tlsSessionWriteContinue(TlsSession *this, int writeResult, int writeError, size_t writeSize)
|
|
||||||
{
|
|
||||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
|
||||||
FUNCTION_LOG_PARAM(TLS_SESSION, this);
|
|
||||||
FUNCTION_LOG_PARAM(INT, writeResult);
|
|
||||||
FUNCTION_LOG_PARAM(INT, writeError);
|
|
||||||
FUNCTION_LOG_PARAM(SIZE, writeSize);
|
|
||||||
FUNCTION_LOG_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
ASSERT(writeSize > 0);
|
|
||||||
|
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
// Handle errors
|
|
||||||
if (writeResult <= 0)
|
|
||||||
{
|
|
||||||
// If error = SSL_ERROR_NONE then this is the first write attempt so continue
|
|
||||||
if (writeError != SSL_ERROR_NONE)
|
|
||||||
{
|
|
||||||
// Error if the error indicates that we should not continue trying
|
|
||||||
if (!tlsSessionError(this, writeError))
|
|
||||||
THROW_FMT(FileWriteError, "unable to write to tls [%d]", writeError);
|
|
||||||
|
|
||||||
// Wait for the socket to be readable for tls renegotiation
|
|
||||||
sckSessionReadyRead(this->socketSession);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((size_t)writeResult != writeSize)
|
|
||||||
{
|
|
||||||
THROW_FMT(
|
|
||||||
FileWriteError, "unable to write to tls, write size %d does not match expected size %zu", writeResult, writeSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN(BOOL, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tlsSessionWrite(THIS_VOID, const Buffer *buffer)
|
tlsSessionWrite(THIS_VOID, const Buffer *buffer)
|
||||||
{
|
{
|
||||||
@ -230,12 +226,13 @@ tlsSessionWrite(THIS_VOID, const Buffer *buffer)
|
|||||||
ASSERT(buffer != NULL);
|
ASSERT(buffer != NULL);
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int error = SSL_ERROR_NONE;
|
|
||||||
|
|
||||||
while (tlsSessionWriteContinue(this, result, error, bufUsed(buffer)))
|
while (result == 0)
|
||||||
{
|
{
|
||||||
result = SSL_write(this->session, bufPtrConst(buffer), (int)bufUsed(buffer));
|
result = tlsSessionResult(this, SSL_write(this->session, bufPtrConst(buffer), (int)bufUsed(buffer)), false);
|
||||||
error = SSL_get_error(this->session, result);
|
|
||||||
|
// Either a retry or all data was written
|
||||||
|
CHECK(result == 0 || (size_t)result == bufUsed(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN_VOID();
|
FUNCTION_LOG_RETURN_VOID();
|
||||||
@ -288,14 +285,20 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
|
|||||||
// Ensure session is freed
|
// Ensure session is freed
|
||||||
memContextCallbackSet(this->memContext, tlsSessionFreeResource, this);
|
memContextCallbackSet(this->memContext, tlsSessionFreeResource, this);
|
||||||
|
|
||||||
// Negotiate TLS session
|
// Assign socket to TLS session
|
||||||
cryptoError(
|
cryptoError(
|
||||||
SSL_set_fd(this->session, sckSessionFd(this->socketSession)) != 1, "unable to add socket to TLS session");
|
SSL_set_fd(this->session, sckSessionFd(this->socketSession)) != 1, "unable to add socket to TLS session");
|
||||||
|
|
||||||
if (sckSessionType(this->socketSession) == sckSessionTypeClient)
|
// Negotiate TLS session
|
||||||
cryptoError(SSL_connect(this->session) != 1, "unable to negotiate client TLS session");
|
int result = 0;
|
||||||
else
|
|
||||||
cryptoError(SSL_accept(this->session) != 1, "unable to negotiate server TLS session");
|
while (result == 0)
|
||||||
|
{
|
||||||
|
if (sckSessionType(this->socketSession) == sckSessionTypeClient)
|
||||||
|
result = tlsSessionResult(this, SSL_connect(this->session), false);
|
||||||
|
else
|
||||||
|
result = tlsSessionResult(this, SSL_accept(this->session), false);
|
||||||
|
}
|
||||||
|
|
||||||
// Create read and write interfaces
|
// Create read and write interfaces
|
||||||
this->write = ioWriteNewP(this, .write = tlsSessionWrite);
|
this->write = ioWriteNewP(this, .write = tlsSessionWrite);
|
||||||
|
@ -239,7 +239,7 @@ unit:
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------------------------------------------
|
||||||
- name: io-tls
|
- name: io-tls
|
||||||
total: 4
|
total: 5
|
||||||
containerReq: true
|
containerReq: true
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
|
@ -464,13 +464,13 @@ testRun(void)
|
|||||||
{
|
{
|
||||||
// Test no output from server
|
// Test no output from server
|
||||||
TEST_ASSIGN(
|
TEST_ASSIGN(
|
||||||
client, httpClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 500, testContainer(), NULL, NULL),
|
client, httpClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 5000, testContainer(), NULL, NULL),
|
||||||
"new client");
|
"new client");
|
||||||
client->timeout = 0;
|
client->timeout = 0;
|
||||||
|
|
||||||
TEST_ERROR_FMT(
|
TEST_ERROR(
|
||||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), ProtocolError,
|
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FileReadError,
|
||||||
"timeout after 500ms waiting for read from '%s:%u'", strPtr(harnessTlsTestHost()), harnessTlsTestPort());
|
"unexpected eof while reading line");
|
||||||
|
|
||||||
// Test invalid http version
|
// Test invalid http version
|
||||||
TEST_ERROR(
|
TEST_ERROR(
|
||||||
@ -508,7 +508,7 @@ testRun(void)
|
|||||||
"[503] Slow Down");
|
"[503] Slow Down");
|
||||||
|
|
||||||
// Request with no content
|
// Request with no content
|
||||||
client->timeout = 2000;
|
client->timeout = 5000;
|
||||||
|
|
||||||
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
||||||
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
||||||
@ -642,7 +642,7 @@ testRun(void)
|
|||||||
HttpClient *client2 = NULL;
|
HttpClient *client2 = NULL;
|
||||||
|
|
||||||
TEST_ASSIGN(
|
TEST_ASSIGN(
|
||||||
cache, httpClientCacheNew(strNew("localhost"), harnessTlsTestPort(), 500, true, NULL, NULL), "new http client cache");
|
cache, httpClientCacheNew(strNew("localhost"), harnessTlsTestPort(), 5000, true, NULL, NULL), "new http client cache");
|
||||||
TEST_ASSIGN(client1, httpClientCacheGet(cache), "get http client");
|
TEST_ASSIGN(client1, httpClientCacheGet(cache), "get http client");
|
||||||
TEST_RESULT_PTR(client1, *(HttpClient **)lstGet(cache->clientList, 0), " check http client");
|
TEST_RESULT_PTR(client1, *(HttpClient **)lstGet(cache->clientList, 0), " check http client");
|
||||||
TEST_RESULT_PTR(httpClientCacheGet(cache), *(HttpClient **)lstGet(cache->clientList, 0), " get same http client");
|
TEST_RESULT_PTR(httpClientCacheGet(cache), *(HttpClient **)lstGet(cache->clientList, 0), " get same http client");
|
||||||
|
@ -87,11 +87,6 @@ testTlsServer(void)
|
|||||||
harnessTlsServerReply("0123456789AB");
|
harnessTlsServerReply("0123456789AB");
|
||||||
harnessTlsServerAbort();
|
harnessTlsServerAbort();
|
||||||
|
|
||||||
// Need data in read buffer to test tlsWriteContinue()
|
|
||||||
harnessTlsServerAccept();
|
|
||||||
harnessTlsServerReply("0123456789AB");
|
|
||||||
harnessTlsServerClose();
|
|
||||||
|
|
||||||
FUNCTION_HARNESS_RESULT_VOID();
|
FUNCTION_HARNESS_RESULT_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +114,15 @@ testRun(void)
|
|||||||
int result;
|
int result;
|
||||||
const char *port = "7777";
|
const char *port = "7777";
|
||||||
|
|
||||||
|
const char *hostLocal = "127.0.0.1";
|
||||||
|
struct addrinfo *hostLocalAddress;
|
||||||
|
|
||||||
|
if ((result = getaddrinfo(hostLocal, port, &hints, &hostLocalAddress)) != 0)
|
||||||
|
{
|
||||||
|
THROW_FMT( // {uncoverable - lookup on IP should never fail}
|
||||||
|
HostConnectError, "unable to get address for '%s': [%d] %s", hostLocal, result, gai_strerror(result));
|
||||||
|
}
|
||||||
|
|
||||||
const char *hostBad = "172.31.255.255";
|
const char *hostBad = "172.31.255.255";
|
||||||
struct addrinfo *hostBadAddress;
|
struct addrinfo *hostBadAddress;
|
||||||
|
|
||||||
@ -207,12 +211,6 @@ testRun(void)
|
|||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("connect to non-blocking socket to test write ready");
|
TEST_TITLE("connect to non-blocking socket to test write ready");
|
||||||
|
|
||||||
// Put the socket in non-blocking mode
|
|
||||||
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
|
// Attempt connection
|
||||||
CHECK(connect(fd, hostBadAddress->ai_addr, hostBadAddress->ai_addrlen) == -1);
|
CHECK(connect(fd, hostBadAddress->ai_addr, hostBadAddress->ai_addrlen) == -1);
|
||||||
|
|
||||||
@ -224,10 +222,25 @@ testRun(void)
|
|||||||
sckSessionReadyWrite(session), ProtocolError, "timeout after 100ms waiting for write to '172.31.255.255:7777'");
|
sckSessionReadyWrite(session), ProtocolError, "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");
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_TITLE("unable to connect to blocking socket");
|
||||||
|
|
||||||
|
socketLocal.block = true;
|
||||||
|
TEST_ERROR(
|
||||||
|
sckClientOpen(sckClientNew(STR(hostLocal), 7777, 0)), HostConnectError,
|
||||||
|
"unable to connect to '127.0.0.1:7777': [111] Connection refused");
|
||||||
|
socketLocal.block = false;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_TITLE("uncovered conditions for sckConnect()");
|
||||||
|
|
||||||
|
TEST_RESULT_BOOL(sckConnectInProgress(EINTR), true, "connection in progress (EINTR)");
|
||||||
}
|
}
|
||||||
FINALLY()
|
FINALLY()
|
||||||
{
|
{
|
||||||
// This needs to be freed or valgrind will complain
|
// These need to be freed or valgrind will complain
|
||||||
|
freeaddrinfo(hostLocalAddress);
|
||||||
freeaddrinfo(hostBadAddress);
|
freeaddrinfo(hostBadAddress);
|
||||||
}
|
}
|
||||||
TRY_END();
|
TRY_END();
|
||||||
@ -236,6 +249,21 @@ testRun(void)
|
|||||||
socketLocal = socketLocalSave;
|
socketLocal = socketLocalSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *****************************************************************************************************************************
|
||||||
|
if (testBegin("SocketClient"))
|
||||||
|
{
|
||||||
|
SocketClient *client = NULL;
|
||||||
|
|
||||||
|
TEST_ASSIGN(client, sckClientNew(strNew("localhost"), harnessTlsTestPort(), 100), "new client");
|
||||||
|
TEST_ERROR_FMT(
|
||||||
|
sckClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
|
||||||
|
harnessTlsTestPort());
|
||||||
|
|
||||||
|
// This address should not be in use in a test environment -- if it is the test will fail
|
||||||
|
TEST_ASSIGN(client, sckClientNew(strNew("172.31.255.255"), harnessTlsTestPort(), 100), "new client");
|
||||||
|
TEST_ERROR_FMT(sckClientOpen(client), HostConnectError, "timeout connecting to '172.31.255.255:%u'", harnessTlsTestPort());
|
||||||
|
}
|
||||||
|
|
||||||
// Additional coverage not provided by testing with actual certificates
|
// Additional coverage not provided by testing with actual certificates
|
||||||
// *****************************************************************************************************************************
|
// *****************************************************************************************************************************
|
||||||
if (testBegin("asn1ToStr(), tlsClientHostVerify(), and tlsClientHostVerifyName()"))
|
if (testBegin("asn1ToStr(), tlsClientHostVerify(), and tlsClientHostVerifyName()"))
|
||||||
@ -300,13 +328,13 @@ testRun(void)
|
|||||||
TEST_ERROR(
|
TEST_ERROR(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 500), 500, true, strNew("bogus.crt"),
|
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true, strNew("bogus.crt"),
|
||||||
strNew("/bogus"))),
|
strNew("/bogus"))),
|
||||||
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
|
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
|
||||||
TEST_ERROR_FMT(
|
TEST_ERROR_FMT(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 500), 500, true, NULL, strNew("/bogus"))),
|
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true, NULL, strNew("/bogus"))),
|
||||||
CryptoError,
|
CryptoError,
|
||||||
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
|
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
|
||||||
harnessTlsTestPort());
|
harnessTlsTestPort());
|
||||||
@ -316,19 +344,19 @@ testRun(void)
|
|||||||
TEST_RESULT_VOID(
|
TEST_RESULT_VOID(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("test.pgbackrest.org"), harnessTlsTestPort(), 500), 500, true,
|
sckClientNew(strNew("test.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||||
"success on valid ca file and match common name");
|
"success on valid ca file and match common name");
|
||||||
TEST_RESULT_VOID(
|
TEST_RESULT_VOID(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("host.test2.pgbackrest.org"), harnessTlsTestPort(), 500), 500, true,
|
sckClientNew(strNew("host.test2.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||||
"success on valid ca file and match alt name");
|
"success on valid ca file and match alt name");
|
||||||
TEST_ERROR(
|
TEST_ERROR(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("test3.pgbackrest.org"), harnessTlsTestPort(), 500), 500, true,
|
sckClientNew(strNew("test3.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||||
CryptoError,
|
CryptoError,
|
||||||
"unable to find hostname 'test3.pgbackrest.org' in certificate common name or subject alternative names");
|
"unable to find hostname 'test3.pgbackrest.org' in certificate common name or subject alternative names");
|
||||||
@ -337,7 +365,7 @@ testRun(void)
|
|||||||
TEST_ERROR_FMT(
|
TEST_ERROR_FMT(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(
|
tlsClientNew(
|
||||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 500), 500, true,
|
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true,
|
||||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
|
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
|
||||||
NULL)),
|
NULL)),
|
||||||
CryptoError,
|
CryptoError,
|
||||||
@ -346,7 +374,7 @@ testRun(void)
|
|||||||
|
|
||||||
TEST_RESULT_VOID(
|
TEST_RESULT_VOID(
|
||||||
tlsClientOpen(
|
tlsClientOpen(
|
||||||
tlsClientNew(sckClientNew(strNew("localhost"), harnessTlsTestPort(), 500), 500, false, NULL, NULL)),
|
tlsClientNew(sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, false, NULL, NULL)),
|
||||||
"success on no verify");
|
"success on no verify");
|
||||||
}
|
}
|
||||||
HARNESS_FORK_PARENT_END();
|
HARNESS_FORK_PARENT_END();
|
||||||
@ -380,7 +408,7 @@ testRun(void)
|
|||||||
|
|
||||||
TEST_ASSIGN(
|
TEST_ASSIGN(
|
||||||
client,
|
client,
|
||||||
tlsClientNew(sckClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 500), 500, testContainer(), NULL, NULL),
|
tlsClientNew(sckClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 5000), 0, testContainer(), NULL, NULL),
|
||||||
"new client");
|
"new client");
|
||||||
TEST_ASSIGN(session, tlsClientOpen(client), "open client");
|
TEST_ASSIGN(session, tlsClientOpen(client), "open client");
|
||||||
|
|
||||||
@ -403,6 +431,13 @@ testRun(void)
|
|||||||
TEST_RESULT_BOOL(sckReadyWrite(session->socketSession->fd, 100), true, "socket is write ready");
|
TEST_RESULT_BOOL(sckReadyWrite(session->socketSession->fd, 100), true, "socket is write ready");
|
||||||
TEST_RESULT_VOID(sckSessionReadyWrite(session->socketSession), "socket session is write ready");
|
TEST_RESULT_VOID(sckSessionReadyWrite(session->socketSession), "socket session is write ready");
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_TITLE("uncovered errors");
|
||||||
|
|
||||||
|
TEST_RESULT_INT(tlsSessionResultProcess(session, SSL_ERROR_WANT_WRITE, 0, false), 0, "write ready");
|
||||||
|
TEST_ERROR(tlsSessionResultProcess(session, SSL_ERROR_WANT_X509_LOOKUP, 0, false), ServiceError, "TLS error [4]");
|
||||||
|
TEST_ERROR(tlsSessionResultProcess(session, SSL_ERROR_ZERO_RETURN, 0, false), ProtocolError, "unexpected TLS eof");
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
const Buffer *input = BUFSTRDEF("some protocol info");
|
const Buffer *input = BUFSTRDEF("some protocol info");
|
||||||
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
|
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
|
||||||
@ -422,9 +457,11 @@ testRun(void)
|
|||||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
||||||
|
|
||||||
output = bufNew(12);
|
output = bufNew(12);
|
||||||
|
session->socketSession->timeout = 100;
|
||||||
TEST_ERROR_FMT(
|
TEST_ERROR_FMT(
|
||||||
ioRead(tlsSessionIoRead(session), output), ProtocolError,
|
ioRead(tlsSessionIoRead(session), output), ProtocolError,
|
||||||
"timeout after 500ms waiting for read from '%s:%u'", strPtr(harnessTlsTestHost()), harnessTlsTestPort());
|
"timeout after 100ms waiting for read from '%s:%u'", strPtr(harnessTlsTestHost()), harnessTlsTestPort());
|
||||||
|
session->socketSession->timeout = 5000;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
input = BUFSTRDEF("more protocol info");
|
input = BUFSTRDEF("more protocol info");
|
||||||
@ -441,26 +478,16 @@ testRun(void)
|
|||||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), true, " check eof = true");
|
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), true, " check eof = true");
|
||||||
|
|
||||||
TEST_RESULT_VOID(tlsSessionClose(session, false), "close again");
|
TEST_RESULT_VOID(tlsSessionClose(session, false), "close again");
|
||||||
TEST_ERROR(tlsSessionError(session, SSL_ERROR_WANT_X509_LOOKUP), ServiceError, "tls error [4]");
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("aborted connection before read complete");
|
TEST_TITLE("aborted connection before read complete (blocking socket)");
|
||||||
|
|
||||||
|
socketLocal.block = true;
|
||||||
TEST_ASSIGN(session, tlsClientOpen(client), "open client again (was closed by server)");
|
TEST_ASSIGN(session, tlsClientOpen(client), "open client again (was closed by server)");
|
||||||
|
socketLocal.block = false;
|
||||||
|
|
||||||
output = bufNew(13);
|
output = bufNew(13);
|
||||||
TEST_ERROR(ioRead(tlsSessionIoRead(session), output), KernelError, "tls failed syscall");
|
TEST_ERROR(ioRead(tlsSessionIoRead(session), output), KernelError, "TLS syscall error");
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
|
||||||
TEST_ASSIGN(session, tlsClientOpen(client), "open client again (was closed by server)");
|
|
||||||
|
|
||||||
TEST_RESULT_BOOL(tlsSessionWriteContinue(session, -1, SSL_ERROR_WANT_READ, 1), true, "continue on WANT_READ");
|
|
||||||
TEST_RESULT_BOOL(tlsSessionWriteContinue(session, 0, SSL_ERROR_NONE, 1), true, "continue on WANT_READ");
|
|
||||||
TEST_ERROR(
|
|
||||||
tlsSessionWriteContinue(session, 77, 0, 88), FileWriteError,
|
|
||||||
"unable to write to tls, write size 77 does not match expected size 88");
|
|
||||||
TEST_ERROR(
|
|
||||||
tlsSessionWriteContinue(session, 0, SSL_ERROR_ZERO_RETURN, 1), FileWriteError, "unable to write to tls [6]");
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
TEST_RESULT_BOOL(sckClientStatStr() != NULL, true, "check statistics exist");
|
TEST_RESULT_BOOL(sckClientStatStr() != NULL, true, "check statistics exist");
|
||||||
|
@ -765,7 +765,7 @@ testRun(void)
|
|||||||
{
|
{
|
||||||
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,
|
||||||
host, port, 1000, testContainer(), NULL, NULL);
|
host, port, 5000, testContainer(), NULL, NULL);
|
||||||
|
|
||||||
// Coverage for noop functions
|
// Coverage for noop functions
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -982,7 +982,7 @@ testRun(void)
|
|||||||
// Switch to path-style URIs
|
// Switch to path-style URIs
|
||||||
s3 = storageS3New(
|
s3 = storageS3New(
|
||||||
path, true, NULL, bucket, endPoint, storageS3UriStylePath, region, accessKey, secretAccessKey, NULL, 16, 2,
|
path, true, NULL, bucket, endPoint, storageS3UriStylePath, region, accessKey, secretAccessKey, NULL, 16, 2,
|
||||||
host, port, 1000, testContainer(), NULL, NULL);
|
host, port, 5000, testContainer(), NULL, NULL);
|
||||||
|
|
||||||
TEST_ERROR(
|
TEST_ERROR(
|
||||||
storagePathRemoveP(s3, strNew("/")), AssertError,
|
storagePathRemoveP(s3, strNew("/")), AssertError,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user