| /* 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 */ |