mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
Refactor TLS server test harness for ease of use.
The prior harness required a separate function to contain the server behavior but this made keeping the client/server code in sync very difficult and in general meant test writing took longer. Now, commands to define server behavior are inline with the client code, which should greatly simplify test writing.
This commit is contained in:
parent
d92d0513c0
commit
b27f9e886b
@ -1,5 +1,5 @@
|
||||
/***********************************************************************************************************************************
|
||||
Tls Test Harness
|
||||
TLS Test Harness
|
||||
***********************************************************************************************************************************/
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
@ -13,27 +13,208 @@ Tls Test Harness
|
||||
#include "common/error.h"
|
||||
#include "common/io/socket/session.h"
|
||||
#include "common/io/tls/session.intern.h"
|
||||
#include "common/log.h"
|
||||
#include "common/type/buffer.h"
|
||||
#include "common/type/json.h"
|
||||
#include "common/wait.h"
|
||||
|
||||
#include "common/harnessDebug.h"
|
||||
#include "common/harnessTest.h"
|
||||
#include "common/harnessTls.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test defaults
|
||||
Command enum
|
||||
***********************************************************************************************************************************/
|
||||
#define TLS_TEST_HOST "tls.test.pgbackrest.org"
|
||||
|
||||
static int testServerSocket = 0;
|
||||
static SSL_CTX *testServerContext = NULL;
|
||||
static TlsSession *testServerSession = NULL;
|
||||
typedef enum
|
||||
{
|
||||
hrnTlsCmdAbort,
|
||||
hrnTlsCmdAccept,
|
||||
hrnTlsCmdClose,
|
||||
hrnTlsCmdDone,
|
||||
hrnTlsCmdExpect,
|
||||
hrnTlsCmdReply,
|
||||
hrnTlsCmdSleep,
|
||||
} HrnTlsCmd;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Initialize TLS and listen on the specified port for TLS connections
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serverKey)
|
||||
#define TLS_TEST_HOST "tls.test.pgbackrest.org"
|
||||
#define TLS_CERT_TEST_KEY TLS_CERT_FAKE_PATH "/pgbackrest-test.key"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Local variables
|
||||
***********************************************************************************************************************************/
|
||||
static struct
|
||||
{
|
||||
IoWrite *clientWrite; // Write interface for client to send commands to the server
|
||||
} hrnTlsLocal;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Send commands to the server
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
hrnTlsServerCommand(HrnTlsCmd cmd, const Variant *data)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(ENUM, cmd);
|
||||
FUNCTION_HARNESS_PARAM(VARIANT, data);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(hrnTlsLocal.clientWrite != NULL);
|
||||
|
||||
ioWriteStrLine(hrnTlsLocal.clientWrite, jsonFromUInt(cmd));
|
||||
ioWriteStrLine(hrnTlsLocal.clientWrite, jsonFromVar(data));
|
||||
ioWriteFlush(hrnTlsLocal.clientWrite);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void hrnTlsClientBegin(IoWrite *write)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(IO_WRITE, write);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(hrnTlsLocal.clientWrite == NULL);
|
||||
ASSERT(write != NULL);
|
||||
|
||||
hrnTlsLocal.clientWrite = write;
|
||||
ioWriteOpen(write);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void hrnTlsClientEnd(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
ASSERT(hrnTlsLocal.clientWrite != NULL);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdDone, NULL);
|
||||
hrnTlsLocal.clientWrite = NULL;
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
hrnTlsServerAbort(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdAbort, NULL);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerAccept(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdAccept, NULL);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerClose()
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdClose, NULL);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerExpect(const String *data)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(STRING, data);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(data != NULL);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdExpect, VARSTR(data));
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerExpectZ(const char *data)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(STRINGZ, data);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(data != NULL);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdExpect, VARSTRZ(data));
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerReply(const String *data)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(STRING, data);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(data != NULL);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdReply, VARSTR(data));
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerReplyZ(const char *data)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(STRINGZ, data);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(data != NULL);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdReply, VARSTRZ(data));
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
void
|
||||
hrnTlsServerSleep(TimeMSec sleepMs)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(TIME_MSEC, sleepMs);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(sleepMs > 0);
|
||||
|
||||
hrnTlsServerCommand(hrnTlsCmdSleep, VARUINT64(sleepMs));
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String *key)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(IO_READ, read);
|
||||
FUNCTION_HARNESS_PARAM(STRING, certificate);
|
||||
FUNCTION_HARNESS_PARAM(STRING, key);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(read != NULL);
|
||||
ASSERT(certificate != NULL);
|
||||
ASSERT(key != NULL);
|
||||
|
||||
// Open read connection to client
|
||||
ioReadOpen(read);
|
||||
|
||||
// Add test hosts
|
||||
if (testContainer())
|
||||
{
|
||||
@ -47,28 +228,31 @@ harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serv
|
||||
const SSL_METHOD *method = SSLv23_method();
|
||||
cryptoError(method == NULL, "unable to load TLS method");
|
||||
|
||||
testServerContext = SSL_CTX_new(method);
|
||||
cryptoError(testServerContext == NULL, "unable to create TLS context");
|
||||
SSL_CTX *serverContext = SSL_CTX_new(method);
|
||||
cryptoError(serverContext == NULL, "unable to create TLS context");
|
||||
|
||||
// Configure the context by setting key and cert
|
||||
cryptoError(
|
||||
SSL_CTX_use_certificate_file(testServerContext, serverCert, SSL_FILETYPE_PEM) <= 0, "unable to load server certificate");
|
||||
SSL_CTX_use_certificate_file(serverContext, strPtr(certificate), SSL_FILETYPE_PEM) <= 0,
|
||||
"unable to load server certificate");
|
||||
cryptoError(
|
||||
SSL_CTX_use_PrivateKey_file(testServerContext, serverKey, SSL_FILETYPE_PEM) <= 0, "unable to load server private key");
|
||||
SSL_CTX_use_PrivateKey_file(serverContext, strPtr(key), SSL_FILETYPE_PEM) <= 0, "unable to load server private key");
|
||||
|
||||
// Create the socket
|
||||
int serverSocket;
|
||||
|
||||
struct sockaddr_in address;
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons((uint16_t)port);
|
||||
address.sin_port = htons((uint16_t)hrnTlsServerPort());
|
||||
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if ((testServerSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
THROW_SYS_ERROR(AssertError, "unable to create socket");
|
||||
|
||||
// Set the address as reusable so we can bind again in the same process for testing
|
||||
int reuseAddr = 1;
|
||||
setsockopt(testServerSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
|
||||
// Bind the address. It might take a bit to bind if another process was recently using it so retry a few times.
|
||||
Wait *wait = waitNew(2000);
|
||||
@ -76,7 +260,7 @@ harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serv
|
||||
|
||||
do
|
||||
{
|
||||
result = bind(testServerSocket, (struct sockaddr *)&address, sizeof(address));
|
||||
result = bind(serverSocket, (struct sockaddr *)&address, sizeof(address));
|
||||
}
|
||||
while (result < 0 && waitMore(wait));
|
||||
|
||||
@ -84,107 +268,136 @@ harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serv
|
||||
THROW_SYS_ERROR(AssertError, "unable to bind socket");
|
||||
|
||||
// Listen for client connections
|
||||
if (listen(testServerSocket, 1) < 0)
|
||||
if (listen(serverSocket, 1) < 0)
|
||||
THROW_SYS_ERROR(AssertError, "unable to listen on socket");
|
||||
|
||||
// Loop until no more commands
|
||||
TlsSession *serverSession = NULL;
|
||||
bool done = false;
|
||||
|
||||
do
|
||||
{
|
||||
HrnTlsCmd cmd = jsonToUInt(ioReadLine(read));
|
||||
const Variant *data = jsonToVar(ioReadLine(read));
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case hrnTlsCmdAbort:
|
||||
{
|
||||
tlsSessionClose(serverSession, false);
|
||||
tlsSessionFree(serverSession);
|
||||
serverSession = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdAccept:
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
unsigned int len = sizeof(addr);
|
||||
|
||||
int testClientSocket = accept(serverSocket, (struct sockaddr *)&addr, &len);
|
||||
|
||||
if (testClientSocket < 0)
|
||||
THROW_SYS_ERROR(AssertError, "unable to accept socket");
|
||||
|
||||
SSL *testClientSSL = SSL_new(serverContext);
|
||||
|
||||
serverSession = tlsSessionNew(
|
||||
testClientSSL, sckSessionNew(sckSessionTypeServer, testClientSocket, STRDEF("client"), 0, 5000), 5000);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdClose:
|
||||
{
|
||||
tlsSessionClose(serverSession, true);
|
||||
tlsSessionFree(serverSession);
|
||||
serverSession = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdDone:
|
||||
{
|
||||
done = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdExpect:
|
||||
{
|
||||
const String *expected = varStr(data);
|
||||
Buffer *buffer = bufNew(strSize(expected));
|
||||
|
||||
ioRead(tlsSessionIoRead(serverSession), buffer);
|
||||
|
||||
// Treat any ? characters as wildcards so variable elements (e.g. auth hashes) can be ignored
|
||||
String *actual = strNewBuf(buffer);
|
||||
|
||||
for (unsigned int actualIdx = 0; actualIdx < strSize(actual); actualIdx++)
|
||||
{
|
||||
if (strPtr(expected)[actualIdx] == '?')
|
||||
((char *)strPtr(actual))[actualIdx] = '?';
|
||||
}
|
||||
|
||||
// Error if actual does not match expected
|
||||
if (!strEq(actual, expected))
|
||||
THROW_FMT(AssertError, "server expected '%s' but got '%s'", strPtr(expected), strPtr(actual));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdReply:
|
||||
{
|
||||
ioWrite(tlsSessionIoWrite(serverSession), BUFSTR(varStr(data)));
|
||||
ioWriteFlush(tlsSessionIoWrite(serverSession));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case hrnTlsCmdSleep:
|
||||
{
|
||||
sleepMSec(varUInt64Force(data));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!done);
|
||||
|
||||
// Free TLS context
|
||||
SSL_CTX_free(serverContext);
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerInitDefault(void)
|
||||
void hrnTlsServerRun(IoRead *read)
|
||||
{
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(IO_READ, read);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
if (testContainer())
|
||||
harnessTlsServerInit(harnessTlsTestPort(), TLS_CERT_TEST_CERT, TLS_CERT_TEST_KEY);
|
||||
hrnTlsServerRunParam(read, STRDEF(TLS_CERT_TEST_CERT), STRDEF(TLS_CERT_TEST_KEY));
|
||||
else
|
||||
{
|
||||
harnessTlsServerInit(
|
||||
harnessTlsTestPort(),
|
||||
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath())),
|
||||
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".key", testRepoPath())));
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Expect an exact string from the client
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerExpect(const char *expected)
|
||||
{
|
||||
Buffer *buffer = bufNew(strlen(expected));
|
||||
|
||||
ioRead(tlsSessionIoRead(testServerSession), buffer);
|
||||
|
||||
// Treat any ? characters as wildcards so variable elements (e.g. auth hashes) can be ignored
|
||||
String *actual = strNewBuf(buffer);
|
||||
|
||||
for (unsigned int actualIdx = 0; actualIdx < strSize(actual); actualIdx++)
|
||||
{
|
||||
if (expected[actualIdx] == '?')
|
||||
((char *)strPtr(actual))[actualIdx] = '?';
|
||||
hrnTlsServerRunParam(
|
||||
read, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".key", testRepoPath()));
|
||||
}
|
||||
|
||||
// Error if actual does not match expected
|
||||
if (!strEqZ(actual, expected))
|
||||
THROW_FMT(AssertError, "server expected '%s' but got '%s'", expected, strPtr(actual));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Send a reply to the client
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerReply(const char *reply)
|
||||
{
|
||||
ioWrite(tlsSessionIoWrite(testServerSession), BUF((unsigned char *)reply, strlen(reply)));
|
||||
ioWriteFlush(tlsSessionIoWrite(testServerSession));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Accept a TLS connection from the client
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerAccept(void)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
unsigned int len = sizeof(addr);
|
||||
|
||||
int testClientSocket = accept(testServerSocket, (struct sockaddr *)&addr, &len);
|
||||
|
||||
if (testClientSocket < 0)
|
||||
THROW_SYS_ERROR(AssertError, "unable to accept socket");
|
||||
|
||||
SSL *testClientSSL = SSL_new(testServerContext);
|
||||
|
||||
testServerSession = tlsSessionNew(
|
||||
testClientSSL, sckSessionNew(sckSessionTypeServer, testClientSocket, STRDEF("client"), 0, 5000), 5000);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Close the connection
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerClose(void)
|
||||
{
|
||||
tlsSessionClose(testServerSession, true);
|
||||
tlsSessionFree(testServerSession);
|
||||
testServerSession = NULL;
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
harnessTlsServerAbort(void)
|
||||
{
|
||||
tlsSessionClose(testServerSession, false);
|
||||
tlsSessionFree(testServerSession);
|
||||
testServerSession = NULL;
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
const String *harnessTlsTestHost(void)
|
||||
const String *hrnTlsServerHost(void)
|
||||
{
|
||||
return strNew(testContainer() ? TLS_TEST_HOST : "127.0.0.1");
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
unsigned int harnessTlsTestPort(void)
|
||||
unsigned int hrnTlsServerPort(void)
|
||||
{
|
||||
return 44443 + testIdx();
|
||||
}
|
||||
|
@ -1,46 +1,62 @@
|
||||
/***********************************************************************************************************************************
|
||||
Tls Test Harness
|
||||
TLS Test Harness
|
||||
|
||||
Simple TLS server for testing TLS client functionality.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef TEST_COMMON_HARNESS_TLS_H
|
||||
#define TEST_COMMON_HARNESS_TLS_H
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/io/tls/session.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Path and prefix for test certificates
|
||||
***********************************************************************************************************************************/
|
||||
#define TEST_CERTIFICATE_PREFIX "test/certificate/pgbackrest-test"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Tls test defaults
|
||||
TLS test defaults
|
||||
***********************************************************************************************************************************/
|
||||
#define TLS_CERT_FAKE_PATH "/etc/fake-cert"
|
||||
#define TLS_CERT_TEST_CERT TLS_CERT_FAKE_PATH "/pgbackrest-test.crt"
|
||||
#define TLS_CERT_TEST_KEY TLS_CERT_FAKE_PATH "/pgbackrest-test.key"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serverKey);
|
||||
// Begin/end client
|
||||
void hrnTlsClientBegin(IoWrite *write);
|
||||
void hrnTlsClientEnd(void);
|
||||
|
||||
// Initialize TLS with default parameters
|
||||
void harnessTlsServerInitDefault(void);
|
||||
|
||||
void harnessTlsServerAccept(void);
|
||||
void harnessTlsServerExpect(const char *expected);
|
||||
void harnessTlsServerReply(const char *reply);
|
||||
void harnessTlsServerClose(void);
|
||||
// Run server
|
||||
void hrnTlsServerRun(IoRead *read);
|
||||
|
||||
// Abort the server session (i.e. don't perform proper TLS shutdown)
|
||||
void harnessTlsServerAbort(void);
|
||||
void hrnTlsServerAbort(void);
|
||||
|
||||
// Accept new TLS connection
|
||||
void hrnTlsServerAccept(void);
|
||||
|
||||
// Close the TLS connection
|
||||
void hrnTlsServerClose(void);
|
||||
|
||||
// Expect the specfified string
|
||||
void hrnTlsServerExpect(const String *data);
|
||||
void hrnTlsServerExpectZ(const char *data);
|
||||
|
||||
// Reply with the specfified string
|
||||
void hrnTlsServerReply(const String *data);
|
||||
void hrnTlsServerReplyZ(const char *data);
|
||||
|
||||
// Sleep specfified milliseconds
|
||||
void hrnTlsServerSleep(TimeMSec sleepMs);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Getters/Setters
|
||||
***********************************************************************************************************************************/
|
||||
// Hostname to use for testing -- this will vary based on whether the test is running in a container
|
||||
const String *harnessTlsTestHost(void);
|
||||
const String *hrnTlsServerHost(void);
|
||||
|
||||
// Port to use for testing. This will be unique for each test running in parallel to avoid conflicts
|
||||
unsigned int harnessTlsTestPort(void);
|
||||
// Port to use for testing. This will be unique for each test running in parallel to avoid conflicts
|
||||
unsigned int hrnTlsServerPort(void);
|
||||
|
||||
#endif
|
||||
|
@ -3,320 +3,12 @@ Test Http
|
||||
***********************************************************************************************************************************/
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/time.h"
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
|
||||
#include "common/harnessFork.h"
|
||||
#include "common/harnessTls.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test server
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
testHttpServer(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
harnessTlsServerInitDefault();
|
||||
|
||||
// Test no output from server
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
sleepMSec(600);
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test invalid http version
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.0 200 OK\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test no space in status
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200OK\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test unexpected end of headers
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test missing colon in header
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"header-value\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test invalid transfer encoding
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"transfer-encoding:bogus\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test content length and transfer encoding both set
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"transfer-encoding:chunked\r\n"
|
||||
"content-length:777\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test 5xx error with no retry
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 503 Slow Down\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Request with no content (with an internal error)
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /?name=%2Fpath%2FA%20Z.txt&type=test HTTP/1.1\r\n"
|
||||
"host:myhost.com\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 500 Internal Error\r\n"
|
||||
"Connection:close\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /?name=%2Fpath%2FA%20Z.txt&type=test HTTP/1.1\r\n"
|
||||
"host:myhost.com\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"key1:0\r\n"
|
||||
" key2 : value2\r\n"
|
||||
"Connection:ack\r\n"
|
||||
"\r\n");
|
||||
|
||||
// Head request with content-length but no content
|
||||
harnessTlsServerExpect(
|
||||
"HEAD / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"content-length:380\r\n"
|
||||
"\r\n");
|
||||
|
||||
// Head request with transfer encoding but no content
|
||||
harnessTlsServerExpect(
|
||||
"HEAD / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n");
|
||||
|
||||
// Head request with connection close but no content
|
||||
harnessTlsServerExpect(
|
||||
"HEAD / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection:close\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
harnessTlsServerAccept();
|
||||
|
||||
// Error with content (with a few slow down errors)
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 503 Slow Down\r\n"
|
||||
"content-length:3\r\n"
|
||||
"Connection:close\r\n"
|
||||
"\r\n"
|
||||
"123");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 503 Slow Down\r\n"
|
||||
"Transfer-Encoding:chunked\r\n"
|
||||
"Connection:close\r\n"
|
||||
"\r\n"
|
||||
"0\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 404 Not Found\r\n"
|
||||
"content-length:0\r\n"
|
||||
"\r\n");
|
||||
|
||||
// Error with content
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 403 \r\n"
|
||||
"content-length:7\r\n"
|
||||
"\r\n"
|
||||
"CONTENT");
|
||||
|
||||
// Request with content
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /path/file%201.txt HTTP/1.1\r\n"
|
||||
"content-length:30\r\n"
|
||||
"\r\n"
|
||||
"012345678901234567890123456789");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection:close\r\n"
|
||||
"\r\n"
|
||||
"01234567890123456789012345678901");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Request with eof before content complete with retry
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /path/file%201.txt HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"content-length:32\r\n"
|
||||
"\r\n"
|
||||
"0123456789012345678901234567890");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /path/file%201.txt HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"content-length:32\r\n"
|
||||
"\r\n"
|
||||
"01234567890123456789012345678901");
|
||||
|
||||
// Request with eof before content complete
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET /path/file%201.txt HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"content-length:32\r\n"
|
||||
"\r\n"
|
||||
"0123456789012345678901234567890");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Request with chunked content
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerReply(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Transfer-Encoding:chunked\r\n"
|
||||
"\r\n"
|
||||
"20\r\n"
|
||||
"01234567890123456789012345678901\r\n"
|
||||
"10\r\n"
|
||||
"0123456789012345\r\n"
|
||||
"0\r\n"
|
||||
"\r\n");
|
||||
|
||||
harnessTlsServerClose();
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
@ -436,7 +128,6 @@ testRun(void)
|
||||
if (testBegin("HttpClient"))
|
||||
{
|
||||
HttpClient *client = NULL;
|
||||
ioBufferSizeSet(35);
|
||||
|
||||
// Reset statistics
|
||||
httpClientStatLocal = (HttpClientStat){0};
|
||||
@ -444,71 +135,164 @@ testRun(void)
|
||||
TEST_RESULT_PTR(httpClientStatStr(), NULL, "no stats yet");
|
||||
|
||||
TEST_ASSIGN(
|
||||
client, httpClientNew(strNew("localhost"), harnessTlsTestPort(), 500, testContainer(), NULL, NULL),
|
||||
client, httpClientNew(strNew("localhost"), hrnTlsServerPort(), 500, testContainer(), NULL, NULL),
|
||||
"new client");
|
||||
|
||||
TEST_ERROR_FMT(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), HostConnectError,
|
||||
"unable to connect to 'localhost:%u': [111] Connection refused", harnessTlsTestPort());
|
||||
"unable to connect to 'localhost:%u': [111] Connection refused", hrnTlsServerPort());
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, false)
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
// Start http test server
|
||||
TEST_RESULT_VOID(testHttpServer(), "http server begin");
|
||||
TEST_RESULT_VOID(
|
||||
hrnTlsServerRun(ioHandleReadNew(strNew("test server read"), HARNESS_FORK_CHILD_READ(), 5000)),
|
||||
"http server begin");
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
// Test no output from server
|
||||
hrnTlsClientBegin(ioHandleWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("create client");
|
||||
|
||||
ioBufferSizeSet(35);
|
||||
|
||||
TEST_ASSIGN(
|
||||
client, httpClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 5000, testContainer(), NULL, NULL),
|
||||
client, httpClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000, testContainer(), NULL, NULL),
|
||||
"new client");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("no output from server");
|
||||
|
||||
client->timeout = 0;
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerSleep(600);
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FileReadError,
|
||||
"unexpected eof while reading line");
|
||||
|
||||
// Test invalid http version
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("invalid http version");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.0 200 OK\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FormatError,
|
||||
"http version of response 'HTTP/1.0 200 OK' must be HTTP/1.1");
|
||||
|
||||
// Test no space in status
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("no space in status");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200OK\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FormatError,
|
||||
"response status '200OK' must have a space");
|
||||
|
||||
// Test unexpected end of headers
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("unexpected end of headers");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FileReadError,
|
||||
"unexpected eof while reading line");
|
||||
|
||||
// Test missing colon in header
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("missing colon in header");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\nheader-value\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FormatError,
|
||||
"header 'header-value' missing colon");
|
||||
|
||||
// Test invalid transfer encoding
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("invalid transfer encoding");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ntransfer-encoding:bogus\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FormatError,
|
||||
"only 'chunked' is supported for 'transfer-encoding' header");
|
||||
|
||||
// Test content length and transfer encoding both set
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("content length and transfer encoding both set");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ntransfer-encoding:chunked\r\ncontent-length:777\r\n\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), FormatError,
|
||||
"'transfer-encoding' and 'content-length' headers are both set");
|
||||
|
||||
// Test 5xx error with no retry
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("5xx error with no retry");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 503 Slow Down\r\n\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), ServiceError,
|
||||
"[503] Slow Down");
|
||||
|
||||
// Request with no content
|
||||
client->timeout = 5000;
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("request with no content (with an internal error)");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET /?name=%2Fpath%2FA%20Z.txt&type=test HTTP/1.1\r\nhost:myhost.com\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 500 Internal Error\r\nConnection:close\r\n\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET /?name=%2Fpath%2FA%20Z.txt&type=test HTTP/1.1\r\nhost:myhost.com\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\nkey1:0\r\n key2 : value2\r\nConnection:ack\r\n\r\n");
|
||||
|
||||
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
||||
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
||||
@ -517,71 +301,113 @@ testRun(void)
|
||||
httpQueryAdd(query, strNew("name"), strNew("/path/A Z.txt"));
|
||||
httpQueryAdd(query, strNew("type"), strNew("test"));
|
||||
|
||||
client->timeout = 5000;
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), query, headerRequest, NULL, false),
|
||||
"request with no content");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", " check response message");
|
||||
TEST_RESULT_UINT(httpClientEof(client), true, " io is eof");
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), query, headerRequest, NULL, false), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", "check response message");
|
||||
TEST_RESULT_UINT(httpClientEof(client), true, "io is eof");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{connection: 'ack', key1: '0', key2: 'value2'}",
|
||||
" check response headers");
|
||||
"check response headers");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("head request with content-length but no content");
|
||||
|
||||
hrnTlsServerExpectZ("HEAD / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ncontent-length:380\r\n\r\n");
|
||||
|
||||
// Head request with content-length but no content
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true),
|
||||
"head request with content-length");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", " check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, " io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, " client is not busy");
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", "check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, "io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, "client is not busy");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '380'}", " check response headers");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '380'}", "check response headers");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("head request with transfer encoding but no content");
|
||||
|
||||
hrnTlsServerExpectZ("HEAD / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
|
||||
// Head request with transfer encoding but no content
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true),
|
||||
"head request with transfer encoding");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", " check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, " io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, " client is not busy");
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", "check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, "io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, "client is not busy");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{transfer-encoding: 'chunked'}",
|
||||
" check response headers");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{transfer-encoding: 'chunked'}", "check response headers");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("head request with connection close but no content");
|
||||
|
||||
hrnTlsServerExpectZ("HEAD / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\nConnection:close\r\n\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
// Head request with connection close but no content
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true),
|
||||
"head request with connection close");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", " check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, " io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, " client is not busy");
|
||||
httpClientRequest(client, strNew("HEAD"), strNew("/"), NULL, httpHeaderNew(NULL), NULL, true), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 200, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "OK", "check response message");
|
||||
TEST_RESULT_BOOL(httpClientEof(client), true, "io is eof");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), false, "client is not busy");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{connection: 'close'}", " check response headers");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{connection: 'close'}", "check response headers");
|
||||
|
||||
// Error with content length 0
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), "error with content length 0");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 404, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "Not Found", " check response message");
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error with content (with a few slow down errors)");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 503 Slow Down\r\ncontent-length:3\r\nConnection:close\r\n\r\n123");
|
||||
|
||||
hrnTlsServerClose();
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 503 Slow Down\r\nTransfer-Encoding:chunked\r\nConnection:close\r\n\r\n0\r\n\r\n");
|
||||
|
||||
hrnTlsServerClose();
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 404 Not Found\r\ncontent-length:0\r\n\r\n");
|
||||
|
||||
TEST_RESULT_VOID(httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 404, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "Not Found", "check response message");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '0'}", " check response headers");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '0'}", "check response headers");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error with content");
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 403 \r\ncontent-length:7\r\n\r\nCONTENT");
|
||||
|
||||
// Error with content
|
||||
Buffer *buffer = NULL;
|
||||
|
||||
TEST_ASSIGN(
|
||||
buffer, httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false),
|
||||
"error with content length");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 403, " check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "", " check empty response message");
|
||||
TEST_ASSIGN(buffer, httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), "request");
|
||||
TEST_RESULT_UINT(httpClientResponseCode(client), 403, "check response code");
|
||||
TEST_RESULT_STR_Z(httpClientResponseMessage(client), "", "check empty response message");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '7'}", " check response headers");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "CONTENT", " check response");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{content-length: '7'}", "check response headers");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "CONTENT", "check response");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("request with content using content-length");
|
||||
|
||||
hrnTlsServerExpectZ("GET /path/file%201.txt HTTP/1.1\r\ncontent-length:30\r\n\r\n012345678901234567890123456789");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\nConnection:close\r\n\r\n01234567890123456789012345678901");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
// Request with content using content-length
|
||||
ioBufferSizeSet(30);
|
||||
|
||||
TEST_ASSIGN(
|
||||
@ -590,48 +416,86 @@ testRun(void)
|
||||
client, strNew("GET"), strNew("/path/file 1.txt"), NULL,
|
||||
httpHeaderAdd(httpHeaderNew(NULL), strNew("content-length"), strNew("30")),
|
||||
BUFSTRDEF("012345678901234567890123456789"), true),
|
||||
"request with content length");
|
||||
"request");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{connection: 'close'}",
|
||||
" check response headers");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901", " check response");
|
||||
TEST_RESULT_UINT(httpClientRead(client, bufNew(1), true), 0, " call internal read to check eof");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{connection: 'close'}", "check response headers");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901", "check response");
|
||||
TEST_RESULT_UINT(httpClientRead(client, bufNew(1), true), 0, "call internal read to check eof");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("request with eof before content complete with retry");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET /path/file%201.txt HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ncontent-length:32\r\n\r\n0123456789012345678901234567890");
|
||||
|
||||
hrnTlsServerClose();
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET /path/file%201.txt HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ncontent-length:32\r\n\r\n01234567890123456789012345678901");
|
||||
|
||||
// Request with eof before content complete with retry
|
||||
TEST_ASSIGN(
|
||||
buffer, httpClientRequest(client, strNew("GET"), strNew("/path/file 1.txt"), NULL, NULL, NULL, true),
|
||||
"request with content length retry");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901", " check response");
|
||||
TEST_RESULT_UINT(httpClientRead(client, bufNew(1), true), 0, " call internal read to check eof");
|
||||
"request");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901", "check response");
|
||||
TEST_RESULT_UINT(httpClientRead(client, bufNew(1), true), 0, "call internal read to check eof");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("request with eof before content complete");
|
||||
|
||||
hrnTlsServerExpectZ("GET /path/file%201.txt HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ("HTTP/1.1 200 OK\r\ncontent-length:32\r\n\r\n0123456789012345678901234567890");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
// Request with eof before content and error
|
||||
buffer = bufNew(32);
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/path/file 1.txt"), NULL, NULL, NULL, false),
|
||||
"request with content length error");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), true, " client is busy");
|
||||
TEST_ERROR(
|
||||
ioRead(httpClientIoRead(client), buffer), FileReadError, "unexpected EOF reading HTTP content");
|
||||
|
||||
// Request with content using chunked encoding
|
||||
TEST_RESULT_VOID(
|
||||
httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false),
|
||||
"request with chunked encoding");
|
||||
httpClientRequest(client, strNew("GET"), strNew("/path/file 1.txt"), NULL, NULL, NULL, false), "request");
|
||||
TEST_RESULT_BOOL(httpClientBusy(client), true, "client is busy");
|
||||
TEST_ERROR(ioRead(httpClientIoRead(client), buffer), FileReadError, "unexpected EOF reading HTTP content");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("request with chunked content");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
hrnTlsServerExpectZ("GET / HTTP/1.1\r\n\r\n");
|
||||
hrnTlsServerReplyZ(
|
||||
"HTTP/1.1 200 OK\r\nTransfer-Encoding:chunked\r\n\r\n"
|
||||
"20\r\n01234567890123456789012345678901\r\n"
|
||||
"10\r\n0123456789012345\r\n"
|
||||
"0\r\n\r\n");
|
||||
|
||||
TEST_RESULT_VOID(httpClientRequest(client, strNew("GET"), strNew("/"), NULL, NULL, NULL, false), "request");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{transfer-encoding: 'chunked'}",
|
||||
" check response headers");
|
||||
httpHeaderToLog(httpClientResponseHeader(client)), "{transfer-encoding: 'chunked'}", "check response headers");
|
||||
|
||||
buffer = bufNew(35);
|
||||
TEST_RESULT_VOID(ioRead(httpClientIoRead(client), buffer), " read response");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901012", " check response");
|
||||
|
||||
TEST_RESULT_BOOL(httpClientStatStr() != NULL, true, "check statistics exist");
|
||||
TEST_RESULT_VOID(ioRead(httpClientIoRead(client), buffer), "read response");
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "01234567890123456789012345678901012", "check response");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("close connection");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_RESULT_VOID(httpClientFree(client), "free client");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
hrnTlsClientEnd();
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("statistics exist");
|
||||
|
||||
TEST_RESULT_BOOL(httpClientStatStr() != NULL, true, "check");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
@ -642,7 +506,7 @@ testRun(void)
|
||||
HttpClient *client2 = NULL;
|
||||
|
||||
TEST_ASSIGN(
|
||||
cache, httpClientCacheNew(strNew("localhost"), harnessTlsTestPort(), 5000, true, NULL, NULL), "new http client cache");
|
||||
cache, httpClientCacheNew(strNew("localhost"), hrnTlsServerPort(), 5000, true, NULL, NULL), "new http client cache");
|
||||
TEST_ASSIGN(client1, httpClientCacheGet(cache), "get 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");
|
||||
|
@ -4,92 +4,16 @@ Test Tls Client
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/time.h"
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
|
||||
#include "common/harnessFork.h"
|
||||
#include "common/harnessTls.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test server with subject alternate names
|
||||
Version that allows custom certs
|
||||
***********************************************************************************************************************************/
|
||||
#ifdef TEST_CONTAINER_REQUIRED
|
||||
|
||||
static void
|
||||
testTlsServerAltName(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
harnessTlsServerInit(
|
||||
harnessTlsTestPort(),
|
||||
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-alt-name.crt", testRepoPath())),
|
||||
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".key", testRepoPath())));
|
||||
|
||||
// Certificate error on invalid ca path
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Success on valid ca file and match common name
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Success on valid ca file and match alt name
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Unable to find matching hostname in certificate
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Certificate error
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Certificate ignored
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerClose();
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
||||
#endif // TEST_CONTAINER_REQUIRED
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test server
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
testTlsServer(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
harnessTlsServerInitDefault();
|
||||
|
||||
// First protocol exchange
|
||||
harnessTlsServerAccept();
|
||||
|
||||
harnessTlsServerExpect("some protocol info");
|
||||
harnessTlsServerReply("something:0\n");
|
||||
|
||||
sleepMSec(100);
|
||||
harnessTlsServerReply("some ");
|
||||
|
||||
sleepMSec(100);
|
||||
harnessTlsServerReply("contentAND MORE");
|
||||
|
||||
// This will cause the client to disconnect
|
||||
sleepMSec(500);
|
||||
|
||||
// Second protocol exchange
|
||||
harnessTlsServerExpect("more protocol info");
|
||||
harnessTlsServerReply("0123456789AB");
|
||||
harnessTlsServerClose();
|
||||
|
||||
// Test aborted connection before read complete
|
||||
harnessTlsServerAccept();
|
||||
harnessTlsServerReply("0123456789AB");
|
||||
harnessTlsServerAbort();
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String *key);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
@ -257,14 +181,14 @@ testRun(void)
|
||||
{
|
||||
SocketClient *client = NULL;
|
||||
|
||||
TEST_ASSIGN(client, sckClientNew(strNew("localhost"), harnessTlsTestPort(), 100), "new client");
|
||||
TEST_ASSIGN(client, sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), "new client");
|
||||
TEST_ERROR_FMT(
|
||||
sckClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
|
||||
harnessTlsTestPort());
|
||||
hrnTlsServerPort());
|
||||
|
||||
// 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());
|
||||
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());
|
||||
}
|
||||
|
||||
// Additional coverage not provided by testing with actual certificates
|
||||
@ -292,17 +216,26 @@ testRun(void)
|
||||
// Connection errors
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_ASSIGN(
|
||||
client, tlsClientNew(sckClientNew(strNew("99.99.99.99.99"), harnessTlsTestPort(), 0), 0, true, NULL, NULL),
|
||||
client, tlsClientNew(sckClientNew(strNew("99.99.99.99.99"), hrnTlsServerPort(), 0), 0, true, NULL, NULL),
|
||||
"new client");
|
||||
TEST_ERROR(
|
||||
tlsClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known");
|
||||
|
||||
TEST_ASSIGN(
|
||||
client, tlsClientNew(sckClientNew(strNew("localhost"), harnessTlsTestPort(), 100), 100, true, NULL, NULL),
|
||||
client, tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), 100, true, NULL, NULL),
|
||||
"new client");
|
||||
TEST_ERROR_FMT(
|
||||
tlsClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
|
||||
harnessTlsTestPort());
|
||||
hrnTlsServerPort());
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("bogus client cert/path");
|
||||
|
||||
TEST_ERROR(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, strNew("bogus.crt"), strNew("/bogus"))),
|
||||
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
|
||||
|
||||
// Certificate location and validation errors
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -317,64 +250,105 @@ testRun(void)
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, false)
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
// Start server to test various certificate errors
|
||||
TEST_RESULT_VOID(testTlsServerAltName(), "tls alt name server begin");
|
||||
TEST_RESULT_VOID(
|
||||
hrnTlsServerRunParam(
|
||||
ioHandleReadNew(strNew("test server read"), HARNESS_FORK_CHILD_READ(), 5000),
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-alt-name.crt", testRepoPath()),
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".key", testRepoPath())),
|
||||
"tls alt name server begin");
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
TEST_ERROR(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true, strNew("bogus.crt"),
|
||||
strNew("/bogus"))),
|
||||
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
|
||||
hrnTlsClientBegin(ioHandleWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("certificate error on invalid ca path");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR_FMT(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true, NULL, strNew("/bogus"))),
|
||||
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, NULL, strNew("/bogus"))),
|
||||
CryptoError,
|
||||
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
|
||||
harnessTlsTestPort());
|
||||
hrnTlsServerPort());
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("valid ca file and match common name");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("test.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||
sckClientNew(strNew("test.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||
"success on valid ca file and match common name");
|
||||
"open connection");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("valid ca file and match alt name");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("host.test2.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||
sckClientNew(strNew("host.test2.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||
"success on valid ca file and match alt name");
|
||||
"open connection");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("unable to find matching hostname in certificate");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("test3.pgbackrest.org"), harnessTlsTestPort(), 5000), 0, true,
|
||||
sckClientNew(strNew("test3.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
|
||||
CryptoError,
|
||||
"unable to find hostname 'test3.pgbackrest.org' in certificate common name or subject alternative names");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("certificate error");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_ERROR_FMT(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(
|
||||
sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, true,
|
||||
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true,
|
||||
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
|
||||
NULL)),
|
||||
CryptoError,
|
||||
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
|
||||
harnessTlsTestPort());
|
||||
hrnTlsServerPort());
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("no certificate verify");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerClose();
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
tlsClientOpen(
|
||||
tlsClientNew(sckClientNew(strNew("localhost"), harnessTlsTestPort(), 5000), 0, false, NULL, NULL)),
|
||||
"success on no verify");
|
||||
tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, false, NULL, NULL)),
|
||||
"open connection");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
hrnTlsClientEnd();
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
@ -396,20 +370,26 @@ testRun(void)
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, false)
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
TEST_RESULT_VOID(testTlsServer(), "tls server begin");
|
||||
TEST_RESULT_VOID(
|
||||
hrnTlsServerRun(ioHandleReadNew(strNew("test server read"), HARNESS_FORK_CHILD_READ(), 5000)),
|
||||
"tls server begin");
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
hrnTlsClientBegin(ioHandleWriteNew(strNew("test client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
|
||||
ioBufferSizeSet(12);
|
||||
|
||||
TEST_ASSIGN(
|
||||
client,
|
||||
tlsClientNew(sckClientNew(harnessTlsTestHost(), harnessTlsTestPort(), 5000), 0, testContainer(), NULL, NULL),
|
||||
tlsClientNew(sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), 0, testContainer(), NULL, NULL),
|
||||
"new client");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
|
||||
TEST_ASSIGN(session, tlsClientOpen(client), "open client");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
@ -439,49 +419,76 @@ testRun(void)
|
||||
TEST_ERROR(tlsSessionResultProcess(session, SSL_ERROR_ZERO_RETURN, 0, false), ProtocolError, "unexpected TLS eof");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("first protocol exchange");
|
||||
|
||||
hrnTlsServerExpectZ("some protocol info");
|
||||
hrnTlsServerReplyZ("something:0\n");
|
||||
|
||||
const Buffer *input = BUFSTRDEF("some protocol info");
|
||||
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
|
||||
ioWriteFlush(tlsSessionIoWrite(session));
|
||||
|
||||
TEST_RESULT_STR_Z(ioReadLine(tlsSessionIoRead(session)), "something:0", "read line");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
|
||||
|
||||
hrnTlsServerSleep(100);
|
||||
hrnTlsServerReplyZ("some ");
|
||||
|
||||
hrnTlsServerSleep(100);
|
||||
hrnTlsServerReplyZ("contentAND MORE");
|
||||
|
||||
Buffer *output = bufNew(12);
|
||||
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 12, "read output");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "some content", " check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "some content", "check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
|
||||
|
||||
output = bufNew(8);
|
||||
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 8, "read output");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "AND MORE", " check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "AND MORE", "check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("read eof");
|
||||
|
||||
hrnTlsServerSleep(500);
|
||||
|
||||
output = bufNew(12);
|
||||
session->socketSession->timeout = 100;
|
||||
TEST_ERROR_FMT(
|
||||
ioRead(tlsSessionIoRead(session), output), ProtocolError,
|
||||
"timeout after 100ms waiting for read from '%s:%u'", strPtr(harnessTlsTestHost()), harnessTlsTestPort());
|
||||
"timeout after 100ms waiting for read from '%s:%u'", strPtr(hrnTlsServerHost()), hrnTlsServerPort());
|
||||
session->socketSession->timeout = 5000;
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("second protocol exchange");
|
||||
|
||||
hrnTlsServerExpectZ("more protocol info");
|
||||
hrnTlsServerReplyZ("0123456789AB");
|
||||
|
||||
hrnTlsServerClose();
|
||||
|
||||
input = BUFSTRDEF("more protocol info");
|
||||
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
|
||||
ioWriteFlush(tlsSessionIoWrite(session));
|
||||
|
||||
output = bufNew(12);
|
||||
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 12, "read output");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "0123456789AB", " check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, " check eof = false");
|
||||
TEST_RESULT_STR_Z(strNewBuf(output), "0123456789AB", "check output");
|
||||
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
|
||||
|
||||
output = bufNew(12);
|
||||
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 0, "read no output after eof");
|
||||
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_TITLE("aborted connection before read complete (blocking socket)");
|
||||
|
||||
hrnTlsServerAccept();
|
||||
hrnTlsServerReplyZ("0123456789AB");
|
||||
hrnTlsServerAbort();
|
||||
|
||||
socketLocal.block = true;
|
||||
TEST_ASSIGN(session, tlsClientOpen(client), "open client again (was closed by server)");
|
||||
socketLocal.block = false;
|
||||
@ -490,14 +497,22 @@ testRun(void)
|
||||
TEST_ERROR(ioRead(tlsSessionIoRead(session), output), KernelError, "TLS syscall error");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_BOOL(sckClientStatStr() != NULL, true, "check statistics exist");
|
||||
TEST_RESULT_BOOL(tlsClientStatStr() != NULL, true, "check statistics exist");
|
||||
TEST_TITLE("close connection");
|
||||
|
||||
TEST_RESULT_VOID(tlsClientFree(client), "free client");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
hrnTlsClientEnd();
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("stastistics exist");
|
||||
|
||||
TEST_RESULT_BOOL(sckClientStatStr() != NULL, true, "check socket");
|
||||
TEST_RESULT_BOOL(tlsClientStatStr() != NULL, true, "check tls");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user