1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-07 00:35:37 +02:00

Implement IoClient/IoSession interfaces for SocketClient/SocketSession.

Following up on 111d33c, implement the new interfaces for socket client/session. Now HTTP objects can be used over TLS or plain sockets.

This required adding ioSessionFd() and ioSessionRole() to provide the functionality of sckSessionFd() and sckSessionType(). sckClientHost() and sckClientPort don't make sense in a generic interface so they were replaced with ioSessionName().
This commit is contained in:
David Steele
2020-08-10 16:03:38 -04:00
committed by GitHub
parent 54c3c39645
commit 7fdbd94e39
23 changed files with 443 additions and 259 deletions

View File

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

View File

@ -8,6 +8,7 @@ Common Command Routines
#include "common/debug.h" #include "common/debug.h"
#include "common/io/http/client.h" #include "common/io/http/client.h"
#include "common/io/socket/client.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"

View File

@ -19,6 +19,7 @@ struct IoClient
const IoClientInterface *interface; // Driver interface const IoClientInterface *interface; // Driver interface
}; };
OBJECT_DEFINE_MOVE(IO_CLIENT);
OBJECT_DEFINE_FREE(IO_CLIENT); OBJECT_DEFINE_FREE(IO_CLIENT);
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -33,6 +34,7 @@ ioClientNew(void *driver, const IoClientInterface *interface)
ASSERT(driver != NULL); ASSERT(driver != NULL);
ASSERT(interface != NULL); ASSERT(interface != NULL);
ASSERT(interface->type != NULL); ASSERT(interface->type != NULL);
ASSERT(interface->name != NULL);
ASSERT(interface->open != NULL); ASSERT(interface->open != NULL);
ASSERT(interface->toLog != NULL); ASSERT(interface->toLog != NULL);
@ -61,6 +63,19 @@ ioClientOpen(IoClient *this)
FUNCTION_LOG_RETURN(IO_SESSION, this->interface->open(this->driver)); FUNCTION_LOG_RETURN(IO_SESSION, this->interface->open(this->driver));
} }
/**********************************************************************************************************************************/
const String *
ioClientName(IoClient *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
FUNCTION_LOG_RETURN_CONST(STRING, this->interface->name(this->driver));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
String * String *
ioClientToLog(const IoClient *this) ioClientToLog(const IoClient *this)

View File

@ -20,9 +20,18 @@ typedef struct IoClient IoClient;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Move to a new parent mem context
IoClient *ioClientMove(IoClient *this, MemContext *parentNew);
// Open session // Open session
IoSession *ioClientOpen(IoClient *this); IoSession *ioClientOpen(IoClient *this);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Name that identifies the client
const String *ioClientName(IoClient *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -15,6 +15,9 @@ typedef struct IoClientInterface
// constant (e.g. created with STRING_EXTERN()) without needing to be copied. // constant (e.g. created with STRING_EXTERN()) without needing to be copied.
const String *const *type; const String *const *type;
// Client name, usually host:port or some other unique indentifier
const String *(*name)(void *driver);
// Open a session // Open a session
IoSession *(*open)(void *driver); IoSession *(*open)(void *driver);

View File

@ -37,6 +37,7 @@ ioSessionNew(void *driver, const IoSessionInterface *interface)
ASSERT(interface->close != NULL); ASSERT(interface->close != NULL);
ASSERT(interface->ioRead != NULL); ASSERT(interface->ioRead != NULL);
ASSERT(interface->ioWrite != NULL); ASSERT(interface->ioWrite != NULL);
ASSERT(interface->role != NULL);
ASSERT(interface->toLog != NULL); ASSERT(interface->toLog != NULL);
IoSession *this = memNew(sizeof(IoSession)); IoSession *this = memNew(sizeof(IoSession));
@ -66,6 +67,19 @@ ioSessionClose(IoSession *this)
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
} }
/**********************************************************************************************************************************/
int
ioSessionFd(IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->fd == NULL ? -1 : this->interface->fd(this->driver));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
IoRead * IoRead *
ioSessionIoRead(IoSession *this) ioSessionIoRead(IoSession *this)
@ -92,9 +106,24 @@ ioSessionIoWrite(IoSession *this)
FUNCTION_TEST_RETURN(this->interface->ioWrite(this->driver)); FUNCTION_TEST_RETURN(this->interface->ioWrite(this->driver));
} }
/**********************************************************************************************************************************/
IoSessionRole
ioSessionRole(const IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->role(this->driver));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
String * String *
ioSessionToLog(const IoSession *this) ioSessionToLog(const IoSession *this)
{ {
return strNewFmt("{type: %s, driver: %s}", strZ(*this->interface->type), strZ(this->interface->toLog(this->driver))); return strNewFmt(
"{type: %s, role: %s, driver: %s}", strZ(*this->interface->type),
ioSessionRole(this) == ioSessionRoleClient ? "client" : "server", strZ(this->interface->toLog(this->driver)));
} }

View File

@ -18,6 +18,15 @@ typedef struct IoSession IoSession;
#include "common/io/read.h" #include "common/io/read.h"
#include "common/io/write.h" #include "common/io/write.h"
/***********************************************************************************************************************************
Session roles
***********************************************************************************************************************************/
typedef enum
{
ioSessionRoleClient, // Client session
ioSessionRoleServer, // Server session
} IoSessionRole;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -30,12 +39,18 @@ IoSession *ioSessionMove(IoSession *this, MemContext *parentNew);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Getters/Setters Getters/Setters
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Session file descriptor, -1 if none
int ioSessionFd(IoSession *this);
// Read interface // Read interface
IoRead *ioSessionIoRead(IoSession *this); IoRead *ioSessionIoRead(IoSession *this);
// Write interface // Write interface
IoWrite *ioSessionIoWrite(IoSession *this); IoWrite *ioSessionIoWrite(IoSession *this);
// Session role
IoSessionRole ioSessionRole(const IoSession *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -18,12 +18,18 @@ typedef struct IoSessionInterface
// Close the session // Close the session
void (*close)(void *driver); void (*close)(void *driver);
// Session file descriptor, if any
int (*fd)(void *driver);
// IoRead interface for the session // IoRead interface for the session
IoRead *(*ioRead)(void *driver); IoRead *(*ioRead)(void *driver);
// IoWrite interface for the session // IoWrite interface for the session
IoWrite *(*ioWrite)(void *driver); IoWrite *(*ioWrite)(void *driver);
// Session role
IoSessionRole (*role)(const void *driver);
// Driver log function // Driver log function
String *(*toLog)(const void *driver); String *(*toLog)(const void *driver);
} IoSessionInterface; } IoSessionInterface;

View File

@ -10,6 +10,7 @@ Socket Client
#include "common/debug.h" #include "common/debug.h"
#include "common/log.h" #include "common/log.h"
#include "common/io/client.intern.h"
#include "common/io/socket/client.h" #include "common/io/socket/client.h"
#include "common/io/socket/common.h" #include "common/io/socket/common.h"
#include "common/io/socket/session.h" #include "common/io/socket/session.h"
@ -17,6 +18,11 @@ Socket Client
#include "common/type/object.h" #include "common/type/object.h"
#include "common/wait.h" #include "common/wait.h"
/***********************************************************************************************************************************
Io client type
***********************************************************************************************************************************/
STRING_EXTERN(IO_CLIENT_SOCKET_TYPE_STR, IO_CLIENT_SOCKET_TYPE);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Statistics Statistics
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -25,63 +31,47 @@ static SocketClientStat sckClientStatLocal;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Object type Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct SocketClient #define SOCKET_CLIENT_TYPE SocketClient
#define SOCKET_CLIENT_PREFIX sckClient
typedef struct SocketClient
{ {
MemContext *memContext; // Mem context MemContext *memContext; // Mem context
String *host; // Hostname or IP address String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on unsigned int port; // Port to connect to host on
String *name; // Socket name (host:port)
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.) TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
}; } SocketClient;
OBJECT_DEFINE_MOVE(SOCKET_CLIENT); /***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
sckClientToLog(const THIS_VOID)
{
THIS(const SocketClient);
OBJECT_DEFINE_GET(Host, const, SOCKET_CLIENT, const String *, host); return strNewFmt("{host: %s, port: %u, timeout: %" PRIu64 "}", strZ(this->host), this->port, this->timeout);
OBJECT_DEFINE_GET(Port, const, SOCKET_CLIENT, unsigned int, port); }
/**********************************************************************************************************************************/ #define FUNCTION_LOG_SOCKET_CLIENT_TYPE \
SocketClient * SocketClient *
sckClientNew(const String *host, unsigned int port, TimeMSec timeout) #define FUNCTION_LOG_SOCKET_CLIENT_FORMAT(value, buffer, bufferSize) \
{ FUNCTION_LOG_STRING_OBJECT_FORMAT(value, sckClientToLog, buffer, bufferSize)
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(host != NULL);
SocketClient *this = NULL;
MEM_CONTEXT_NEW_BEGIN("SocketClient")
{
this = memNew(sizeof(SocketClient));
*this = (SocketClient)
{
.memContext = MEM_CONTEXT_NEW(),
.host = strDup(host),
.port = port,
.timeout = timeout,
};
sckClientStatLocal.object++;
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(SOCKET_CLIENT, this);
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
SocketSession * static IoSession *
sckClientOpen(SocketClient *this) sckClientOpen(THIS_VOID)
{ {
FUNCTION_LOG_BEGIN(logLevelTrace) THIS(SocketClient);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_CLIENT, this); FUNCTION_LOG_PARAM(SOCKET_CLIENT, this);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(this != NULL); ASSERT(this != NULL);
SocketSession *result = NULL; IoSession *result = NULL;
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
@ -137,7 +127,7 @@ sckClientOpen(SocketClient *this)
// Create the session // Create the session
MEM_CONTEXT_PRIOR_BEGIN() MEM_CONTEXT_PRIOR_BEGIN()
{ {
result = sckSessionNew(sckSessionTypeClient, fd, this->host, this->port, this->timeout); result = sckSessionNew(ioSessionRoleClient, fd, this->host, this->port, this->timeout);
} }
MEM_CONTEXT_PRIOR_END(); MEM_CONTEXT_PRIOR_END();
} }
@ -165,7 +155,66 @@ sckClientOpen(SocketClient *this)
} }
MEM_CONTEXT_TEMP_END(); MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(SOCKET_SESSION, result); FUNCTION_LOG_RETURN(IO_SESSION, result);
}
/**********************************************************************************************************************************/
static const String *
sckClientName(THIS_VOID)
{
THIS(SocketClient);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_CLIENT, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->name);
}
/**********************************************************************************************************************************/
static const IoClientInterface sckClientInterface =
{
.type = &IO_CLIENT_SOCKET_TYPE_STR,
.name = sckClientName,
.open = sckClientOpen,
.toLog = sckClientToLog,
};
IoClient *
sckClientNew(const String *host, unsigned int port, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(host != NULL);
IoClient *this = NULL;
MEM_CONTEXT_NEW_BEGIN("SocketClient")
{
SocketClient *driver = memNew(sizeof(SocketClient));
*driver = (SocketClient)
{
.memContext = MEM_CONTEXT_NEW(),
.host = strDup(host),
.port = port,
.name = strNewFmt("%s:%u", strZ(host), port),
.timeout = timeout,
};
sckClientStatLocal.object++;
this = ioClientNew(driver, &sckClientInterface);
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(IO_CLIENT, this);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -185,10 +234,3 @@ sckClientStatStr(void)
FUNCTION_TEST_RETURN(result); FUNCTION_TEST_RETURN(result);
} }
/**********************************************************************************************************************************/
String *
sckClientToLog(const SocketClient *this)
{
return strNewFmt("{host: %s, port: %u, timeout: %" PRIu64 "}", strZ(this->host), this->port, this->timeout);
}

View File

@ -2,25 +2,18 @@
Socket Client Socket Client
A simple socket client intended to allow access to services that are exposed via a socket. A simple socket client intended to allow access to services that are exposed via a socket.
Currently this is not a full-featured client and is only intended to isolate socket functionality from the tls code.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#ifndef COMMON_IO_SOCKET_CLIENT_H #ifndef COMMON_IO_SOCKET_CLIENT_H
#define COMMON_IO_SOCKET_CLIENT_H #define COMMON_IO_SOCKET_CLIENT_H
/*********************************************************************************************************************************** #include "common/io/client.h"
Object type
***********************************************************************************************************************************/
#define SOCKET_CLIENT_TYPE SocketClient
#define SOCKET_CLIENT_PREFIX sckClient
typedef struct SocketClient SocketClient;
#include "common/io/read.h"
#include "common/io/socket/session.h"
#include "common/io/write.h"
#include "common/time.h" #include "common/time.h"
#include "common/type/string.h"
/***********************************************************************************************************************************
Io client type
***********************************************************************************************************************************/
#define IO_CLIENT_SOCKET_TYPE "socket"
STRING_DECLARE(IO_CLIENT_SOCKET_TYPE_STR);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Statistics Statistics
@ -35,37 +28,12 @@ typedef struct SocketClientStat
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
SocketClient *sckClientNew(const String *host, unsigned int port, TimeMSec timeout); IoClient *sckClientNew(const String *host, unsigned int port, TimeMSec timeout);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Open the connection
SocketSession *sckClientOpen(SocketClient *this);
// Move to a new parent mem context
SocketClient *sckClientMove(SocketClient *this, MemContext *parentNew);
// Statistics as a formatted string // Statistics as a formatted string
String *sckClientStatStr(void); String *sckClientStatStr(void);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Socket host
const String *sckClientHost(const SocketClient *this);
// Socket port
unsigned int sckClientPort(const SocketClient *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
String *sckClientToLog(const SocketClient *this);
#define FUNCTION_LOG_SOCKET_CLIENT_TYPE \
SocketClient *
#define FUNCTION_LOG_SOCKET_CLIENT_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, sckClientToLog, buffer, bufferSize)
#endif #endif

View File

@ -10,18 +10,20 @@ Socket Session
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/socket/client.h" #include "common/io/socket/client.h"
#include "common/io/socket/common.h" #include "common/io/session.intern.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"
#include "common/wait.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Object type Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct SocketSession #define SOCKET_SESSION_TYPE SocketSession
#define SOCKET_SESSION_PREFIX sckSession
typedef struct SocketSession
{ {
MemContext *memContext; // Mem context MemContext *memContext; // Mem context
SocketSessionType type; // Type (server or client) IoSessionRole role; // Role (server or client)
int fd; // File descriptor int fd; // File descriptor
String *host; // Hostname or IP address String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on unsigned int port; // Port to connect to host on
@ -29,16 +31,23 @@ struct SocketSession
IoRead *read; // IoRead interface to the file descriptor IoRead *read; // IoRead interface to the file descriptor
IoWrite *write; // IoWrite interface to the file descriptor IoWrite *write; // IoWrite interface to the file descriptor
}; } SocketSession;
OBJECT_DEFINE_MOVE(SOCKET_SESSION); /***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
sckSessionToLog(const THIS_VOID)
{
THIS(const SocketSession);
OBJECT_DEFINE_GET(Fd, , SOCKET_SESSION, int, fd); return strNewFmt("{fd %d, host: %s, port: %u, timeout: %" PRIu64 "}", this->fd, strZ(this->host), this->port, this->timeout);
OBJECT_DEFINE_GET(IoRead, , SOCKET_SESSION, IoRead *, read); }
OBJECT_DEFINE_GET(IoWrite, , SOCKET_SESSION, IoWrite *, write);
OBJECT_DEFINE_GET(Type, const, SOCKET_SESSION, SocketSessionType, type);
OBJECT_DEFINE_FREE(SOCKET_SESSION); #define FUNCTION_LOG_SOCKET_SESSION_TYPE \
SocketSession *
#define FUNCTION_LOG_SOCKET_SESSION_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, sckSessionToLog, buffer, bufferSize)
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Free connection Free connection
@ -50,11 +59,108 @@ OBJECT_DEFINE_FREE_RESOURCE_BEGIN(SOCKET_SESSION, LOG, logLevelTrace)
OBJECT_DEFINE_FREE_RESOURCE_END(LOG); OBJECT_DEFINE_FREE_RESOURCE_END(LOG);
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
SocketSession * static void
sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int port, TimeMSec timeout) sckSessionClose(THIS_VOID)
{
THIS(SocketSession);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
// If not already closed
if (this->fd != -1)
{
// Clear the callback to close the socket
memContextCallbackClear(this->memContext);
// Close the socket
close(this->fd);
this->fd = -1;
}
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
static int
sckSessionFd(THIS_VOID)
{
THIS(SocketSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->fd);
}
/**********************************************************************************************************************************/
static IoRead *
sckSessionIoRead(THIS_VOID)
{
THIS(SocketSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->read);
}
/**********************************************************************************************************************************/
static IoWrite *
sckSessionIoWrite(THIS_VOID)
{
THIS(SocketSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->write);
}
/**********************************************************************************************************************************/
static IoSessionRole
sckSessionRole(const THIS_VOID)
{
THIS(const SocketSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SOCKET_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->role);
}
/**********************************************************************************************************************************/
static const IoSessionInterface sckSessionInterface =
{
.type = &IO_CLIENT_SOCKET_TYPE_STR,
.close = sckSessionClose,
.fd = sckSessionFd,
.ioRead = sckSessionIoRead,
.ioWrite = sckSessionIoWrite,
.role = sckSessionRole,
.toLog = sckSessionToLog,
};
IoSession *
sckSessionNew(IoSessionRole role, int fd, const String *host, unsigned int port, TimeMSec timeout)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug) FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(ENUM, type); FUNCTION_LOG_PARAM(ENUM, role);
FUNCTION_LOG_PARAM(INT, fd); FUNCTION_LOG_PARAM(INT, fd);
FUNCTION_LOG_PARAM(STRING, host); FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port); FUNCTION_LOG_PARAM(UINT, port);
@ -64,18 +170,18 @@ sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int p
ASSERT(fd != -1); ASSERT(fd != -1);
ASSERT(host != NULL); ASSERT(host != NULL);
SocketSession *this = NULL; IoSession *this = NULL;
MEM_CONTEXT_NEW_BEGIN("SocketSession") MEM_CONTEXT_NEW_BEGIN("SocketSession")
{ {
this = memNew(sizeof(SocketSession)); SocketSession *driver = memNew(sizeof(SocketSession));
String *name = strNewFmt("%s:%u", strZ(host), port); String *name = strNewFmt("%s:%u", strZ(host), port);
*this = (SocketSession) *driver = (SocketSession)
{ {
.memContext = MEM_CONTEXT_NEW(), .memContext = MEM_CONTEXT_NEW(),
.type = type, .role = role,
.fd = fd, .fd = fd,
.host = strDup(host), .host = strDup(host),
.port = port, .port = port,
@ -84,19 +190,18 @@ sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int p
.write = ioFdWriteNew(name, fd, timeout), .write = ioFdWriteNew(name, fd, timeout),
}; };
memContextCallbackSet(this->memContext, sckSessionFreeResource, this);
strFree(name); strFree(name);
// Open read/write io
ioReadOpen(driver->read);
ioWriteOpen(driver->write);
// Ensure file descriptor is closed
memContextCallbackSet(driver->memContext, sckSessionFreeResource, driver);
this = ioSessionNew(driver, &sckSessionInterface);
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(SOCKET_SESSION, this); FUNCTION_LOG_RETURN(IO_SESSION, this);
}
/**********************************************************************************************************************************/
String *
sckSessionToLog(const SocketSession *this)
{
return strNewFmt(
"{type: %s, fd %d, host: %s, port: %u, timeout: %" PRIu64 "}", this->type == sckSessionTypeClient ? "client" : "server",
this->fd, strZ(this->host), this->port, this->timeout);
} }

View File

@ -2,76 +2,17 @@
Socket Session Socket Session
A simple socket session intended to allow access to services that are exposed via a socket. A simple socket session intended to allow access to services that are exposed via a socket.
Currently this is not a full-featured session and is only intended to isolate socket functionality from the tls code.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#ifndef COMMON_IO_SOCKET_SESSION_H #ifndef COMMON_IO_SOCKET_SESSION_H
#define COMMON_IO_SOCKET_SESSION_H #define COMMON_IO_SOCKET_SESSION_H
/*********************************************************************************************************************************** #include "common/io/session.h"
Test result operations
***********************************************************************************************************************************/
typedef enum
{
sckSessionTypeClient,
sckSessionTypeServer,
} SocketSessionType;
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define SOCKET_SESSION_TYPE SocketSession
#define SOCKET_SESSION_PREFIX sckSession
typedef struct SocketSession SocketSession;
#include "common/io/read.h"
#include "common/io/write.h"
#include "common/time.h" #include "common/time.h"
#include "common/type/string.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
SocketSession *sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int port, TimeMSec timeout); IoSession *sckSessionNew(IoSessionRole role, int fd, const String *host, unsigned int port, TimeMSec timeout);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Read interface
IoRead *sckSessionIoRead(SocketSession *this);
// Write interface
IoWrite *sckSessionIoWrite(SocketSession *this);
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Move to a new parent mem context
SocketSession *sckSessionMove(SocketSession *this, MemContext *parentNew);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Socket file descriptor
int sckSessionFd(SocketSession *this);
// Socket type
SocketSessionType sckSessionType(const SocketSession *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void sckSessionFree(SocketSession *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
String *sckSessionToLog(const SocketSession *this);
#define FUNCTION_LOG_SOCKET_SESSION_TYPE \
SocketSession *
#define FUNCTION_LOG_SOCKET_SESSION_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, sckSessionToLog, buffer, bufferSize)
#endif #endif

View File

@ -38,9 +38,10 @@ Object type
typedef struct TlsClient typedef struct TlsClient
{ {
MemContext *memContext; // Mem context MemContext *memContext; // Mem context
const String *host; // Host to use for peer verification
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.) TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
bool verifyPeer; // Should the peer (server) certificate be verified? bool verifyPeer; // Should the peer (server) certificate be verified?
SocketClient *socketClient; // Socket client IoClient *ioClient; // Underlying client (usually a SocketClient)
SSL_CTX *context; // TLS context SSL_CTX *context; // TLS context
} TlsClient; } TlsClient;
@ -54,8 +55,8 @@ tlsClientToLog(const THIS_VOID)
THIS(const TlsClient); THIS(const TlsClient);
return strNewFmt( return strNewFmt(
"{socketClient: %s, timeout: %" PRIu64", verifyPeer: %s}", "{ioClient: %s, timeout: %" PRIu64", verifyPeer: %s}",
memContextFreeing(this->memContext) ? NULL_Z : strZ(sckClientToLog(this->socketClient)), this->timeout, memContextFreeing(this->memContext) ? NULL_Z : strZ(ioClientToLog(this->ioClient)), this->timeout,
cvtBoolToConstZ(this->verifyPeer)); cvtBoolToConstZ(this->verifyPeer));
} }
@ -239,20 +240,18 @@ tlsClientOpen(THIS_VOID)
TRY_BEGIN() TRY_BEGIN()
{ {
// Open the socket session first since this is mostly likely to fail // Open the underlying session first since this is mostly likely to fail
SocketSession *socketSession = sckClientOpen(this->socketClient); IoSession *ioSession = ioClientOpen(this->ioClient);
// Create internal TLS session. If there is a failure before the TlsSession object is created there may be a leak // Create internal TLS session. If there is a failure before the TlsSession object is created there may be a leak
// of the TLS session but this is likely to result in program termination so it doesn't seem worth coding for. // of the TLS session but this is likely to result in program termination so it doesn't seem worth coding for.
cryptoError((session = SSL_new(this->context)) == NULL, "unable to create TLS session"); cryptoError((session = SSL_new(this->context)) == NULL, "unable to create TLS session");
// Set server host name used for validation // Set server host name used for validation
cryptoError( cryptoError(SSL_set_tlsext_host_name(session, strZ(this->host)) != 1, "unable to set TLS host name");
SSL_set_tlsext_host_name(session, strZ(sckClientHost(this->socketClient))) != 1,
"unable to set TLS host name");
// Create the TLS session // Create the TLS session
result = tlsSessionNew(session, socketSession, this->timeout); result = tlsSessionNew(session, ioSession, this->timeout);
} }
CATCH_ANY() CATCH_ANY()
{ {
@ -288,14 +287,13 @@ tlsClientOpen(THIS_VOID)
if (verifyResult != X509_V_OK) // {vm_covered} if (verifyResult != X509_V_OK) // {vm_covered}
{ {
THROW_FMT( // {vm_covered} THROW_FMT( // {vm_covered}
CryptoError, "unable to verify certificate presented by '%s:%u': [%ld] %s", // {vm_covered} CryptoError, "unable to verify certificate presented by '%s': [%ld] %s", // {vm_covered}
strZ(sckClientHost(this->socketClient)), sckClientPort(this->socketClient), verifyResult, // {vm_covered} strZ(ioClientName(this->ioClient)), verifyResult, X509_verify_cert_error_string(verifyResult)); // {vm_covered}
X509_verify_cert_error_string(verifyResult)); // {vm_covered}
} }
// Verify that the hostname appears in the certificate // Verify that the hostname appears in the certificate
X509 *certificate = SSL_get_peer_certificate(session); // {vm_covered} X509 *certificate = SSL_get_peer_certificate(session); // {vm_covered}
bool nameResult = tlsClientHostVerify(sckClientHost(this->socketClient), certificate); // {vm_covered} bool nameResult = tlsClientHostVerify(this->host, certificate); // {vm_covered}
X509_free(certificate); // {vm_covered} X509_free(certificate); // {vm_covered}
if (!nameResult) // {vm_covered} if (!nameResult) // {vm_covered}
@ -303,33 +301,50 @@ tlsClientOpen(THIS_VOID)
THROW_FMT( // {vm_covered} THROW_FMT( // {vm_covered}
CryptoError, // {vm_covered} CryptoError, // {vm_covered}
"unable to find hostname '%s' in certificate common name or subject alternative names", // {vm_covered} "unable to find hostname '%s' in certificate common name or subject alternative names", // {vm_covered}
strZ(sckClientHost(this->socketClient))); // {vm_covered} strZ(this->host)); // {vm_covered}
} }
} }
FUNCTION_LOG_RETURN(IO_SESSION, result); FUNCTION_LOG_RETURN(IO_SESSION, result);
} }
/**********************************************************************************************************************************/
static const String *
tlsClientName(THIS_VOID)
{
THIS(TlsClient);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TLS_CLIENT, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(ioClientName(this->ioClient));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
static const IoClientInterface tlsClientInterface = static const IoClientInterface tlsClientInterface =
{ {
.type = &IO_CLIENT_TLS_TYPE_STR, .type = &IO_CLIENT_TLS_TYPE_STR,
.name = tlsClientName,
.open = tlsClientOpen, .open = tlsClientOpen,
.toLog = tlsClientToLog, .toLog = tlsClientToLog,
}; };
IoClient * IoClient *
tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath) tlsClientNew(IoClient *ioClient, const String *host, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug) FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(SOCKET_CLIENT, socket); FUNCTION_LOG_PARAM(IO_CLIENT, ioClient);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout); FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(BOOL, verifyPeer); FUNCTION_LOG_PARAM(BOOL, verifyPeer);
FUNCTION_LOG_PARAM(STRING, caFile); FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, caPath); FUNCTION_LOG_PARAM(STRING, caPath);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(socket != NULL); ASSERT(ioClient != NULL);
IoClient *this = NULL; IoClient *this = NULL;
@ -340,7 +355,8 @@ tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const Stri
*driver = (TlsClient) *driver = (TlsClient)
{ {
.memContext = MEM_CONTEXT_NEW(), .memContext = MEM_CONTEXT_NEW(),
.socketClient = sckClientMove(socket, MEM_CONTEXT_NEW()), .ioClient = ioClientMove(ioClient, MEM_CONTEXT_NEW()),
.host = strDup(host),
.timeout = timeout, .timeout = timeout,
.verifyPeer = verifyPeer, .verifyPeer = verifyPeer,
}; };

View File

@ -10,7 +10,6 @@ This object is intended to be used for multiple TLS sessions so ioClientOpen() c
#define COMMON_IO_TLS_CLIENT_H #define COMMON_IO_TLS_CLIENT_H
#include "common/io/client.h" #include "common/io/client.h"
#include "common/io/socket/client.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Io client type Io client type
@ -31,7 +30,8 @@ typedef struct TlsClientStat
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
IoClient *tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath); IoClient *tlsClientNew(
IoClient *ioClient, const String *host, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions

View File

@ -10,7 +10,6 @@ TLS Session
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/read.intern.h" #include "common/io/read.intern.h"
#include "common/io/session.intern.h" #include "common/io/session.intern.h"
#include "common/io/socket/session.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/io/tls/session.h" #include "common/io/tls/session.h"
#include "common/io/write.intern.h" #include "common/io/write.intern.h"
@ -27,10 +26,10 @@ Object type
typedef struct TlsSession typedef struct TlsSession
{ {
MemContext *memContext; // Mem context MemContext *memContext; // Mem context
SocketSession *socketSession; // Socket session IoSession *ioSession; // Io session
SSL *session; // TLS session on the socket SSL *session; // TLS session on the file descriptor
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.) TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
bool shutdownOnClose; // Shutdown the TLS connection when closing the socket bool shutdownOnClose; // Shutdown the TLS connection when closing the session
IoRead *read; // Read interface IoRead *read; // Read interface
IoWrite *write; // Write interface IoWrite *write; // Write interface
@ -45,9 +44,9 @@ tlsSessionToLog(const THIS_VOID)
THIS(const TlsSession); THIS(const TlsSession);
return strNewFmt( return strNewFmt(
"{socketSession: %s, timeout: %" PRIu64", shutdownOnClose: %s}", "{ioSession: %s, timeout: %" PRIu64", shutdownOnClose: %s}",
this->socketSession == NULL || memContextFreeing(this->memContext) ? NULL_Z : strZ(sckSessionToLog(this->socketSession)), memContextFreeing(this->memContext) ? NULL_Z : strZ(ioSessionToLog(this->ioSession)), this->timeout,
this->timeout, cvtBoolToConstZ(this->shutdownOnClose)); cvtBoolToConstZ(this->shutdownOnClose));
} }
#define FUNCTION_LOG_TLS_SESSION_TYPE \ #define FUNCTION_LOG_TLS_SESSION_TYPE \
@ -83,9 +82,8 @@ tlsSessionClose(THIS_VOID)
if (this->shutdownOnClose) if (this->shutdownOnClose)
SSL_shutdown(this->session); SSL_shutdown(this->session);
// Free the socket session // Close the io session
sckSessionFree(this->socketSession); ioSessionClose(this->ioSession);
this->socketSession = NULL;
// Free the TLS session // Free the TLS session
memContextCallbackClear(this->memContext); memContextCallbackClear(this->memContext);
@ -137,7 +135,7 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
// Try again after waiting for read ready // Try again after waiting for read ready
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
{ {
ioReadReadyP(sckSessionIoRead(this->socketSession), .error = true); ioReadReadyP(ioSessionIoRead(this->ioSession), .error = true);
result = 0; result = 0;
break; break;
} }
@ -145,7 +143,7 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
// Try again after waiting for write ready // Try again after waiting for write ready
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
{ {
ioWriteReadyP(sckSessionIoWrite(this->socketSession), .error = true); ioWriteReadyP(ioSessionIoWrite(this->ioSession), .error = true);
result = 0; result = 0;
break; break;
} }
@ -219,9 +217,9 @@ tlsSessionRead(THIS_VOID, Buffer *buffer, bool block)
// 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 to reduce blocking // If no TLS data pending then check the io to reduce blocking
if (!SSL_pending(this->session)) if (!SSL_pending(this->session))
ioReadReadyP(sckSessionIoRead(this->socketSession), .error = true); ioReadReadyP(ioSessionIoRead(this->ioSession), .error = true);
// Read and handle errors. The error queue must be cleared before this operation. // Read and handle errors. The error queue must be cleared before this operation.
ERR_clear_error(); ERR_clear_error();
@ -322,6 +320,21 @@ tlsSessionIoWrite(THIS_VOID)
FUNCTION_TEST_RETURN(this->write); FUNCTION_TEST_RETURN(this->write);
} }
/**********************************************************************************************************************************/
static IoSessionRole
tlsSessionRole(const THIS_VOID)
{
THIS(const TlsSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TLS_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(ioSessionRole(this->ioSession));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
static const IoSessionInterface tlsSessionInterface = static const IoSessionInterface tlsSessionInterface =
{ {
@ -329,20 +342,21 @@ static const IoSessionInterface tlsSessionInterface =
.close = tlsSessionClose, .close = tlsSessionClose,
.ioRead = tlsSessionIoRead, .ioRead = tlsSessionIoRead,
.ioWrite = tlsSessionIoWrite, .ioWrite = tlsSessionIoWrite,
.role = tlsSessionRole,
.toLog = tlsSessionToLog, .toLog = tlsSessionToLog,
}; };
IoSession * IoSession *
tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout) tlsSessionNew(SSL *session, IoSession *ioSession, TimeMSec timeout)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug) FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM_P(VOID, session); FUNCTION_LOG_PARAM_P(VOID, session);
FUNCTION_LOG_PARAM(SOCKET_SESSION, socketSession); FUNCTION_LOG_PARAM(IO_SESSION, ioSession);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout); FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(session != NULL); ASSERT(session != NULL);
ASSERT(socketSession != NULL); ASSERT(ioSession != NULL);
IoSession *this = NULL; IoSession *this = NULL;
@ -354,7 +368,7 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
{ {
.memContext = MEM_CONTEXT_NEW(), .memContext = MEM_CONTEXT_NEW(),
.session = session, .session = session,
.socketSession = sckSessionMove(socketSession, MEM_CONTEXT_NEW()), .ioSession = ioSessionMove(ioSession, MEM_CONTEXT_NEW()),
.timeout = timeout, .timeout = timeout,
.shutdownOnClose = true, .shutdownOnClose = true,
}; };
@ -362,9 +376,8 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
// Ensure session is freed // Ensure session is freed
memContextCallbackSet(driver->memContext, tlsSessionFreeResource, driver); memContextCallbackSet(driver->memContext, tlsSessionFreeResource, driver);
// Assign socket to TLS session // Assign file descriptor to TLS session
cryptoError( cryptoError(SSL_set_fd(driver->session, ioSessionFd(driver->ioSession)) != 1, "unable to add fd to TLS session");
SSL_set_fd(driver->session, sckSessionFd(driver->socketSession)) != 1, "unable to add socket to TLS session");
// Negotiate TLS session. The error queue must be cleared before this operation. // Negotiate TLS session. The error queue must be cleared before this operation.
int result = 0; int result = 0;
@ -373,7 +386,7 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
{ {
ERR_clear_error(); ERR_clear_error();
if (sckSessionType(driver->socketSession) == sckSessionTypeClient) if (ioSessionRole(driver->ioSession) == ioSessionRoleClient)
result = tlsSessionResult(driver, SSL_connect(driver->session), false); result = tlsSessionResult(driver, SSL_connect(driver->session), false);
else else
result = tlsSessionResult(driver, SSL_accept(driver->session), false); result = tlsSessionResult(driver, SSL_accept(driver->session), false);

View File

@ -16,13 +16,12 @@ Object type
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include "common/io/session.h" #include "common/io/session.h"
#include "common/io/socket/session.h"
#include "common/time.h" #include "common/time.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Only called by TLS client/server code // Only called by TLS client/server code
IoSession *tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout); IoSession *tlsSessionNew(SSL *session, IoSession *ioSession, TimeMSec timeout);
#endif #endif

View File

@ -11,6 +11,7 @@ Azure Storage
#include "common/debug.h" #include "common/debug.h"
#include "common/io/http/client.h" #include "common/io/http/client.h"
#include "common/io/http/common.h" #include "common/io/http/common.h"
#include "common/io/socket/client.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -741,7 +742,7 @@ storageAzureNew(
// Create the http client used to service requests // Create the http client used to service requests
driver->httpClient = httpClientNew( driver->httpClient = httpClientNew(
tlsClientNew(sckClientNew(driver->host, port, timeout), timeout, verifyPeer, caFile, caPath), timeout); tlsClientNew(sckClientNew(driver->host, port, timeout), driver->host, timeout, verifyPeer, caFile, caPath), timeout);
// Create list of redacted headers // Create list of redacted headers
driver->headerRedactList = strLstNew(); driver->headerRedactList = strLstNew();

View File

@ -10,6 +10,7 @@ S3 Storage
#include "common/debug.h" #include "common/debug.h"
#include "common/io/http/client.h" #include "common/io/http/client.h"
#include "common/io/http/common.h" #include "common/io/http/common.h"
#include "common/io/socket/client.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -857,10 +858,11 @@ storageS3New(
}; };
// Create the HTTP client used to service requests // Create the HTTP client used to service requests
if (host == NULL)
host = driver->bucketEndpoint;
driver->httpClient = httpClientNew( driver->httpClient = httpClientNew(
tlsClientNew( tlsClientNew(sckClientNew(host, port, timeout), host, timeout, verifyPeer, caFile, caPath), timeout);
sckClientNew(host == NULL ? driver->bucketEndpoint : host, port, timeout), timeout, verifyPeer, caFile, caPath),
timeout);
// Create list of redacted headers // Create list of redacted headers
driver->headerRedactList = strLstNew(); driver->headerRedactList = strLstNew();

View File

@ -302,7 +302,7 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
SSL *testClientSSL = SSL_new(serverContext); SSL *testClientSSL = SSL_new(serverContext);
serverSession = tlsSessionNew( serverSession = tlsSessionNew(
testClientSSL, sckSessionNew(sckSessionTypeServer, testClientSocket, STRDEF("client"), 0, 5000), 5000); testClientSSL, sckSessionNew(ioSessionRoleServer, testClientSocket, STRDEF("client"), 0, 5000), 5000);
break; break;
} }

View File

@ -121,7 +121,7 @@ testRun(void)
cfgOptionSet(cfgOptLogTimestamp, cfgSourceParam, varNewBool(true)); cfgOptionSet(cfgOptLogTimestamp, cfgSourceParam, varNewBool(true));
httpClientNew(tlsClientNew(sckClientNew(STRDEF("BOGUS"), 443, 1000), 1000, true, NULL, NULL), 1000); httpClientNew(tlsClientNew(sckClientNew(STRDEF("BOGUS"), 443, 1000), STRDEF("BOGUS"), 1000, true, NULL, NULL), 1000);
harnessLogLevelSet(logLevelDetail); harnessLogLevelSet(logLevelDetail);

View File

@ -6,6 +6,7 @@ Test HTTP
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/io/socket/client.h"
#include "common/harnessFork.h" #include "common/harnessFork.h"
#include "common/harnessTls.h" #include "common/harnessTls.h"
@ -179,7 +180,9 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
client, client,
httpClientNew( httpClientNew(
tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 500), 500, testContainer(), NULL, NULL), 500), tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 500), strNew("X"), 500, testContainer(), NULL, NULL),
500),
"new client"); "new client");
TEST_ERROR_FMT( TEST_ERROR_FMT(
@ -209,7 +212,9 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
client, client,
httpClientNew( httpClientNew(
tlsClientNew(sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), 5000, testContainer(), NULL, NULL), tlsClientNew(
sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), hrnTlsServerHost(), 5000, testContainer(),
NULL, NULL),
5000), 5000),
"new client"); "new client");

View File

@ -140,24 +140,26 @@ testRun(void)
CHECK(connect(fd, hostBadAddress->ai_addr, hostBadAddress->ai_addrlen) == -1); CHECK(connect(fd, hostBadAddress->ai_addr, hostBadAddress->ai_addrlen) == -1);
// Create socket session and wait for timeout // Create socket session and wait for timeout
SocketSession *session = NULL; IoSession *session = NULL;
TEST_ASSIGN(session, sckSessionNew(sckSessionTypeClient, fd, strNew(hostBad), 7777, 100), "new socket"); TEST_ASSIGN(session, sckSessionNew(ioSessionRoleClient, fd, strNew(hostBad), 7777, 100), "new socket");
TEST_ERROR( TEST_ERROR(
ioWriteReadyP(sckSessionIoWrite(session), .error = true), FileWriteError, ioWriteReadyP(ioSessionIoWrite(session), .error = true), FileWriteError,
"timeout after 100ms waiting for write to '172.31.255.255:7777'"); "timeout after 100ms waiting for write to '172.31.255.255:7777'");
TEST_RESULT_VOID(sckSessionFree(session), "free socket session"); TEST_RESULT_VOID(ioSessionClose(session), "close socket session");
TEST_RESULT_VOID(ioSessionClose(session), "close socket session again");
TEST_RESULT_VOID(ioSessionFree(session), "free socket session");
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
TEST_TITLE("unable to connect to blocking socket"); TEST_TITLE("unable to connect to blocking socket");
SocketClient *socketClient = sckClientNew(STR(hostLocal), 7777, 0); IoClient *socketClient = sckClientNew(STR(hostLocal), 7777, 0);
TEST_RESULT_UINT(sckClientPort(socketClient), 7777, " check port"); TEST_RESULT_STR_Z(ioClientName(socketClient), "127.0.0.1:7777", " check name");
socketLocal.block = true; socketLocal.block = true;
TEST_ERROR( TEST_ERROR(
sckClientOpen(socketClient), HostConnectError, "unable to connect to '127.0.0.1:7777': [111] Connection refused"); ioClientOpen(socketClient), HostConnectError, "unable to connect to '127.0.0.1:7777': [111] Connection refused");
socketLocal.block = false; socketLocal.block = false;
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
@ -180,16 +182,16 @@ testRun(void)
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("SocketClient")) if (testBegin("SocketClient"))
{ {
SocketClient *client = NULL; IoClient *client = NULL;
TEST_ASSIGN(client, sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), "new client"); TEST_ASSIGN(client, sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), "new client");
TEST_ERROR_FMT( TEST_ERROR_FMT(
sckClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused", ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
hrnTlsServerPort()); hrnTlsServerPort());
// This address should not be in use in a test environment -- if it is the test will fail // 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"), hrnTlsServerPort(), 100), "new client"); TEST_ASSIGN(client, sckClientNew(strNew("172.31.255.255"), hrnTlsServerPort(), 100), "new client");
TEST_ERROR_FMT(sckClientOpen(client), HostConnectError, "timeout connecting to '172.31.255.255:%u'", hrnTlsServerPort()); TEST_ERROR_FMT(ioClientOpen(client), HostConnectError, "timeout connecting to '172.31.255.255:%u'", hrnTlsServerPort());
} }
// Additional coverage not provided by testing with actual certificates // Additional coverage not provided by testing with actual certificates
@ -217,13 +219,14 @@ testRun(void)
// Connection errors // Connection errors
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_ASSIGN( TEST_ASSIGN(
client, tlsClientNew(sckClientNew(strNew("99.99.99.99.99"), hrnTlsServerPort(), 0), 0, true, NULL, NULL), client, tlsClientNew(sckClientNew(strNew("99.99.99.99.99"), 7777, 0), strNew("X"), 0, true, NULL, NULL),
"new client"); "new client");
TEST_RESULT_STR_Z(ioClientName(client), "99.99.99.99.99:7777", " check name");
TEST_ERROR( TEST_ERROR(
ioClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known"); ioClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known");
TEST_ASSIGN( TEST_ASSIGN(
client, tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), 100, true, NULL, NULL), client, tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), strNew("X"), 100, true, NULL, NULL),
"new client"); "new client");
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused", ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
@ -235,7 +238,9 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, strNew("bogus.crt"), strNew("/bogus"))), sckClientNew(
strNew("localhost"), hrnTlsServerPort(), 5000), strNew("X"), 0, true, strNew("bogus.crt"),
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");
// Certificate location and validation errors // Certificate location and validation errors
@ -276,7 +281,8 @@ testRun(void)
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, NULL, strNew("/bogus"))), sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), strNew("X"), 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",
hrnTlsServerPort()); hrnTlsServerPort());
@ -290,8 +296,8 @@ testRun(void)
TEST_RESULT_VOID( TEST_RESULT_VOID(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("test.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true, sckClientNew(strNew("test.pgbackrest.org"), hrnTlsServerPort(), 5000), strNew("test.pgbackrest.org"),
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)), 0, true, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
"open connection"); "open connection");
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
@ -303,7 +309,8 @@ testRun(void)
TEST_RESULT_VOID( TEST_RESULT_VOID(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("host.test2.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true, sckClientNew(strNew("host.test2.pgbackrest.org"), hrnTlsServerPort(), 5000),
strNew("host.test2.pgbackrest.org"), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)), strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
"open connection"); "open connection");
@ -316,7 +323,8 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("test3.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true, sckClientNew(strNew("test3.pgbackrest.org"), hrnTlsServerPort(), 5000),
strNew("test3.pgbackrest.org"), 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");
@ -330,7 +338,7 @@ testRun(void)
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioClientOpen( ioClientOpen(
tlsClientNew( tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), strNew("X"), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()), strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
NULL)), NULL)),
CryptoError, CryptoError,
@ -345,7 +353,8 @@ testRun(void)
TEST_RESULT_VOID( TEST_RESULT_VOID(
ioClientOpen( ioClientOpen(
tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, false, NULL, NULL)), tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), strNew("X"), 0, false, NULL, NULL)),
"open connection"); "open connection");
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
@ -385,7 +394,9 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
client, client,
tlsClientNew(sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), 0, testContainer(), NULL, NULL), tlsClientNew(
sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), hrnTlsServerHost(), 0, testContainer(), NULL,
NULL),
"new client"); "new client");
hrnTlsServerAccept(); hrnTlsServerAccept();
@ -393,6 +404,8 @@ testRun(void)
TEST_ASSIGN(session, ioClientOpen(client), "open client"); TEST_ASSIGN(session, ioClientOpen(client), "open client");
TlsSession *tlsSession = (TlsSession *)session->driver; TlsSession *tlsSession = (TlsSession *)session->driver;
TEST_RESULT_INT(ioSessionFd(session), -1, "no fd for tls session");
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("uncovered errors"); TEST_TITLE("uncovered errors");
@ -441,11 +454,11 @@ testRun(void)
hrnTlsServerSleep(500); hrnTlsServerSleep(500);
output = bufNew(12); output = bufNew(12);
((IoFdRead *)tlsSession->socketSession->read->driver)->timeout = 100; ((IoFdRead *)((SocketSession *)tlsSession->ioSession->driver)->read->driver)->timeout = 100;
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioRead(ioSessionIoRead(session), output), FileReadError, ioRead(ioSessionIoRead(session), output), FileReadError,
"timeout after 100ms waiting for read from '%s:%u'", strZ(hrnTlsServerHost()), hrnTlsServerPort()); "timeout after 100ms waiting for read from '%s:%u'", strZ(hrnTlsServerHost()), hrnTlsServerPort());
((IoFdRead *)tlsSession->socketSession->read->driver)->timeout = 5000; ((IoFdRead *)((SocketSession *)tlsSession->ioSession->driver)->read->driver)->timeout = 5000;
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("second protocol exchange"); TEST_TITLE("second protocol exchange");

View File

@ -213,8 +213,8 @@ testRun(void)
TEST_RESULT_STR( TEST_RESULT_STR(
httpClientToLog(driver->httpClient), httpClientToLog(driver->httpClient),
strNewFmt( strNewFmt(
"{ioClient: {type: tls, driver: {socketClient: {host: bucket.s3.amazonaws.com, port: 443, timeout: 60000}" "{ioClient: {type: tls, driver: {ioClient: {type: socket, driver: {host: bucket.s3.amazonaws.com, port: 443"
", timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}", ", timeout: 60000}}, timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}",
cvtBoolToConstZ(testContainer())), cvtBoolToConstZ(testContainer())),
"check http client"); "check http client");
@ -281,8 +281,8 @@ testRun(void)
TEST_RESULT_STR( TEST_RESULT_STR(
httpClientToLog(driver->httpClient), httpClientToLog(driver->httpClient),
strNewFmt( strNewFmt(
"{ioClient: {type: tls, driver: {socketClient: {host: bucket.custom.endpoint, port: 333, timeout: 60000}" "{ioClient: {type: tls, driver: {ioClient: {type: socket, driver: {host: bucket.custom.endpoint, port: 333"
", timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}", ", timeout: 60000}}, timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}",
cvtBoolToConstZ(testContainer())), cvtBoolToConstZ(testContainer())),
"check http client"); "check http client");