blob: dd20ebc515a92b04bbbdd3bad8fa1a9c0ebf4460 [file] [log] [blame]
/* Copyright 2014 Lieven Govaerts
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MockHTTP_private_H
#define MockHTTP_private_H
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_poll.h>
#include <apr_time.h>
#include <apr_thread_proc.h>
#if !defined(HAVE_STDBOOL_H) && defined(_MSC_VER) && (_MSC_VER >= 1800)
/* VS 2015 errors out when redefining bool */
#define HAVE_STDBOOL_H 1
#endif
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
#include "MockHTTP.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define MH_VERBOSE 0
/* Simple macro to return from function when status != 0
expects 'apr_status_t status;' declaration. */
#define STATUSERR(x) if ((status = (x))) return status;
#define READ_ERROR(status) ((status) \
&& !APR_STATUS_IS_EOF(status) \
&& !APR_STATUS_IS_EAGAIN(status))
#define STATUSREADERR(x) if (((status = (x)) && READ_ERROR(status)))\
return status;
#define MH_STATUS_START (APR_OS_START_USERERR + 1500)
/* This code indicates that the server is waiting for a timed event */
#define MH_STATUS_WAITING (MH_STATUS_START + 1)
#define MH_STATUS_INCOMPLETE_REQUEST (MH_STATUS_START + 1)
#ifndef HAVE_STDBOOL_H
typedef short int bool;
#endif
static const bool YES = 1;
static const bool NO = 0;
typedef struct _mhClientCtx_t _mhClientCtx_t;
typedef enum expectation_t {
RequestsReceivedOnce = 0x00000001,
RequestsReceivedInOrder = 0x00000002,
} expectation_t;
struct MockHTTP {
apr_pool_t *pool;
mhServCtx_t *servCtx;
char *errmsg;
unsigned long expectations;
mhStats_t *verifyStats; /* Statistics gathered by the server */
mhResponse_t *defResponse; /* Default req matched response */
mhResponse_t *defErrorResponse; /* Default req not matched response */
mhServCtx_t *proxyCtx;
mhServCtx_t *ocspRespCtx;
apr_array_header_t *connMatchers; /* array of mhConnMatcherBldr_t *'s */
apr_array_header_t *reqMatchers; /* array of ReqMatcherRespPair_t *'s */
apr_array_header_t *ocspReqMatchers; /* array of ReqMatcherRespPair_t *'s */
apr_array_header_t *incompleteReqMatchers; /* .... same type */
};
typedef struct ReqMatcherRespPair_t {
mhRequestMatcher_t *rm;
mhResponse_t *resp;
mhAction_t action;
} ReqMatcherRespPair_t;
typedef enum servMode_t {
ModeServer,
ModeProxy,
ModeTunnel,
} servMode_t;
typedef enum loopRequestState_t {
NoReqsReceived = 0,
PartialReqReceived = 1,
FullReqReceived = 2,
} loopRequestState_t;
struct mhServCtx_t {
apr_pool_t *pool;
const MockHTTP *mh; /* keep const to avoid thread race problems */
const char *hostname;
const char *serverID; /* unique id for this server */
apr_port_t port;
apr_port_t default_port;
const char *alpn;
apr_pollset_t *pollset;
apr_socket_t *skt; /* Server listening socket */
mhServerType_t type;
mhThreading_t threading;
#if APR_HAS_THREADS
bool cancelThread; /* Variable used to signal that the thread should
be cancelled. */
apr_thread_t * threadid;
#endif
loopRequestState_t reqState; /* 1 if a request is in progress, 0 if
no req received yet or read completely. */
unsigned int maxRequests; /* Max. nr of reqs per connection. */
apr_array_header_t *clients; /* array of _mhClientCtx_t *'s */
/* HTTPS specific */
const char *certFilesPrefix;
const char *keyFile;
const char *passphrase;
apr_array_header_t *certFiles;
mhClientCertVerification_t clientCert;
int protocols; /* SSL protocol versions */
bool ocspEnabled;
apr_array_header_t *reqsReceived; /* array of mhRequest_t *'s */
apr_array_header_t *connMatchers; /* array of mhConnMatcherBldr_t *'s */
apr_array_header_t *reqMatchers; /* array of ReqMatcherRespPair_t *'s */
apr_array_header_t *incompleteReqMatchers; /* .... same type */
};
typedef enum reqReadState_t {
ReadStateStatusLine = 0,
ReadStateHeaders,
ReadStateBody,
ReadStateChunked,
ReadStateChunkedHeader,
ReadStateChunkedChunk,
ReadStateChunkedTrailer,
ReadStateDone
} reqReadState_t;
typedef enum method_t {
MethodOther = 0,
MethodACL,
MethodBASELINE_CONTROL,
MethodCHECKIN,
MethodCHECKOUT,
MethodCONNECT,
MethodCOPY,
MethodDELETE,
MethodGET,
MethodHEAD,
MethodLABEL,
MethodLOCK,
MethodMERGE,
MethodMKACTIVITY,
MethodMKCOL,
MethodMKWORKSPACE,
MethodMOVE,
MethodOPTIONS,
MethodORDERPATCH,
MethodPATCH,
MethodPOST,
MethodPROPFIND,
MethodPROPPATCH,
MethodPUT,
MethodREPORT,
MethodSEARCH,
MethodTRACE,
MethodUNCHECKOUT,
MethodUNLOCK,
MethodUPDATE,
MethodVERSION_CONTROL
} method_t;
typedef enum requestType_t {
RequestTypeHTTP,
RequestTypeOCSP,
} requestType_t;
struct mhRequest_t {
apr_pool_t *pool;
requestType_t type;
const char *method;
method_t methodCode;
const char *url;
apr_table_t *hdrs;
apr_array_header_t *hdrHashes;
int version;
apr_array_header_t *body; /* array of iovec strings that form the raw body */
apr_size_t bodyLen;
bool chunked;
/* array of iovec strings that form the dechunked body */
apr_array_header_t *chunks;
reqReadState_t readState;
bool incomplete_chunk; /* chunk reading in progress */
};
struct mhResponse_t {
apr_pool_t *pool;
const MockHTTP *mh;
bool built;
unsigned int code;
apr_table_t *hdrs;
apr_array_header_t *body; /* array of iovec strings that form the raw body */
apr_size_t bodyLen;
bool chunked;
/* array of iovec strings that form the dechunked body */
const apr_array_header_t *chunks;
const char *raw_data;
size_t raw_data_length;
apr_array_header_t *builders;
bool closeConn;
mhRequest_t *req; /* mhResponse_t instance is reply to req */
/* This is a OCSP response */
bool ocspResponse;
mhOCSPRespnseStatus_t ocsp_response_status;
};
/* Builder structures for server setup, request matching and response creation */
enum { MagicKey = 0x4D484244 }; /* MHBD */
typedef enum builderType_t {
BuilderTypeReqMatcher,
BuilderTypeConnMatcher,
BuilderTypeServerSetup,
BuilderTypeResponse,
BuilderTypeNone, /* A noop builder */
} builderType_t;
typedef struct builder_t {
unsigned int magic;
builderType_t type;
} builder_t;
typedef bool (*reqmatchfunc_t)(const mhReqMatcherBldr_t *mp,
const mhRequest_t *req);
struct mhRequestMatcher_t {
apr_pool_t *pool;
requestType_t type;
apr_array_header_t *matchers; /* array of mhReqMatcherBldr_t *'s. */
bool incomplete;
};
struct mhReqMatcherBldr_t {
builder_t builder;
const void *baton; /* use this for an expected string */
unsigned long ibaton;
const void *baton2;
reqmatchfunc_t matcher;
const char *describe_key;
const char *describe_value;
bool match_incomplete; /* Don't wait for full valid requests */
};
typedef bool (*connmatchfunc_t)(const mhConnMatcherBldr_t *cmb,
const _mhClientCtx_t *cctx);
struct mhConnMatcherBldr_t {
builder_t builder;
const void *baton;
connmatchfunc_t connmatcher;
const char *describe_key;
const char *describe_value;
};
typedef bool (*serversetupfunc_t)(const mhServerSetupBldr_t *ssb,
mhServCtx_t *ctx);
struct mhServerSetupBldr_t {
builder_t builder;
const void *baton;
unsigned int ibaton;
serversetupfunc_t serversetup;
};
typedef bool (* respbuilderfunc_t)(const mhResponseBldr_t *rb,
mhResponse_t *resp);
struct mhResponseBldr_t {
builder_t builder;
const void *baton;
unsigned int ibaton;
respbuilderfunc_t respbuilder;
};
method_t methodToCode(const char *code);
unsigned long calculateHeaderHash(const char *hdr, const char *val);
const char *getHeader(apr_table_t *hdrs, const char *hdr);
void setHeader(apr_table_t *hdrs, const char *hdr, const char *val);
/* Initialize a mhRequest_t object. */
mhRequest_t *_mhInitRequest(apr_pool_t *pool, requestType_t type);
bool _mhRequestMatcherMatch(const mhRequestMatcher_t *rm,
const mhRequest_t *req);
bool _mhClientcertcn_matcher(const mhConnMatcherBldr_t *mp,
const _mhClientCtx_t *cctx);
bool _mhClientcert_valid_matcher(const mhConnMatcherBldr_t *mp,
const _mhClientCtx_t *cctx);
/* Build a response */
void _mhBuildResponse(mhResponse_t *resp);
void _mhErrorUnexpectedBuilder(const MockHTTP *mh, void *actual,
builderType_t expected);
/* Test servers */
apr_status_t _mhRunServerLoop(mhServCtx_t *ctx);
void _mhLog(int verbose_flag, apr_socket_t *skt, const char *fmt, ...);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* MockHTTP_private_H */