1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Replace OBJECT_DEFINE_GET() with *Pub struct pattern.

This macro was originally intended to simplify the creation of simple getters but it has been superseded by the pattern introduced in 79a2d02c.

Remove instances of OBJECT_DEFINE_GET() to avoid confusion with the new pattern.
This commit is contained in:
David Steele 2021-04-07 14:27:57 -04:00
parent b715c70b46
commit cc85c4f03d
10 changed files with 173 additions and 140 deletions

View File

@ -24,15 +24,13 @@ Object type
***********************************************************************************************************************************/
struct HttpClient
{
HttpClientPub pub; // Publicly accessible variables
MemContext *memContext; // Mem context
TimeMSec timeout; // Request timeout
IoClient *ioClient; // Io client (e.g. TLS or socket client)
List *sessionReuseList; // List of HTTP sessions that can be reused
};
OBJECT_DEFINE_GET(Timeout, const, HTTP_CLIENT, TimeMSec, timeout);
/**********************************************************************************************************************************/
HttpClient *
httpClientNew(IoClient *ioClient, TimeMSec timeout)
@ -52,8 +50,11 @@ httpClientNew(IoClient *ioClient, TimeMSec timeout)
*this = (HttpClient)
{
.pub =
{
.timeout = timeout,
},
.memContext = MEM_CONTEXT_NEW(),
.timeout = timeout,
.ioClient = ioClient,
.sessionReuseList = lstNewP(sizeof(HttpSession *)),
};
@ -121,5 +122,5 @@ httpClientToLog(const HttpClient *this)
{
return strNewFmt(
"{ioClient: %s, reusable: %u, timeout: %" PRIu64"}", strZ(ioClientToLog(this->ioClient)), lstSize(this->sessionReuseList),
this->timeout);
httpClientTimeout(this));
}

View File

@ -19,9 +19,6 @@ completes and tries to call httpClientReuse() on an HttpClient that has been fre
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define HTTP_CLIENT_TYPE HttpClient
#define HTTP_CLIENT_PREFIX httpClient
typedef struct HttpClient HttpClient;
#include "common/io/client.h"
@ -47,6 +44,21 @@ Constructors
***********************************************************************************************************************************/
HttpClient *httpClientNew(IoClient *ioClient, TimeMSec timeout);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct HttpClientPub
{
TimeMSec timeout; // Request timeout
} HttpClientPub;
__attribute__((always_inline)) static inline TimeMSec
httpClientTimeout(const HttpClient *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpClientPub *)this)->timeout;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
@ -56,11 +68,6 @@ HttpSession *httpClientOpen(HttpClient *this);
// Request/response finished cleanly so session can be reused
void httpClientReuse(HttpClient *this, HttpSession *session);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
TimeMSec httpClientTimeout(const HttpClient *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/

View File

@ -44,12 +44,9 @@ Object type
***********************************************************************************************************************************/
struct HttpRequest
{
HttpRequestPub pub; // Publicly accessible variables
MemContext *memContext; // Mem context
HttpClient *client; // HTTP client
const String *verb; // HTTP verb (GET, POST, etc.)
const String *path; // HTTP path
const HttpQuery *query; // HTTP query
const HttpHeader *header; // HTTP headers
const Buffer *content; // HTTP content
HttpSession *session; // Session for async requests
@ -58,11 +55,6 @@ struct HttpRequest
OBJECT_DEFINE_MOVE(HTTP_REQUEST);
OBJECT_DEFINE_FREE(HTTP_REQUEST);
OBJECT_DEFINE_GET(Verb, const, HTTP_REQUEST, const String *, verb);
OBJECT_DEFINE_GET(Path, const, HTTP_REQUEST, const String *, path);
OBJECT_DEFINE_GET(Query, const, HTTP_REQUEST, const HttpQuery *, query);
OBJECT_DEFINE_GET(Header, const, HTTP_REQUEST, const HttpHeader *, header);
/***********************************************************************************************************************************
Process the request
***********************************************************************************************************************************/
@ -111,17 +103,19 @@ httpRequestProcess(HttpRequest *this, bool waitForResponse, bool contentCache)
String *requestStr =
strNewFmt(
"%s %s%s%s " HTTP_VERSION CRLF_Z HTTP_HEADER_USER_AGENT ":" PROJECT_NAME "/" PROJECT_VERSION CRLF_Z,
strZ(this->verb), strZ(this->path), this->query == NULL ? "" : "?",
this->query == NULL ? "" : strZ(httpQueryRenderP(this->query)));
strZ(httpRequestVerb(this)), strZ(httpRequestPath(this)), httpRequestQuery(this) == NULL ? "" : "?",
httpRequestQuery(this) == NULL ? "" : strZ(httpQueryRenderP(httpRequestQuery(this))));
// Add headers
const StringList *headerList = httpHeaderList(this->header);
const StringList *headerList = httpHeaderList(httpRequestHeader(this));
for (unsigned int headerIdx = 0; headerIdx < strLstSize(headerList); headerIdx++)
{
const String *headerKey = strLstGet(headerList, headerIdx);
strCatFmt(requestStr, "%s:%s" CRLF_Z, strZ(headerKey), strZ(httpHeaderGet(this->header, headerKey)));
strCatFmt(
requestStr, "%s:%s" CRLF_Z, strZ(headerKey),
strZ(httpHeaderGet(httpRequestHeader(this), headerKey)));
}
// Add blank line to end of headers and write the request as a buffer so secrets do not show up in logs
@ -143,7 +137,7 @@ httpRequestProcess(HttpRequest *this, bool waitForResponse, bool contentCache)
// Wait for response
if (waitForResponse)
{
result = httpResponseNew(session, this->verb, contentCache);
result = httpResponseNew(session, httpRequestVerb(this), contentCache);
// Retry when response code is 5xx. These errors generally represent a server error for a request that
// looks valid. There are a few errors that might be permanently fatal but they are rare and it seems best
@ -206,12 +200,15 @@ httpRequestNew(HttpClient *client, const String *verb, const String *path, HttpR
*this = (HttpRequest)
{
.pub =
{
.verb = strDup(verb),
.path = strDup(path),
.query = httpQueryDupP(param.query),
.header = param.header == NULL ? httpHeaderNew(NULL) : httpHeaderDup(param.header, NULL),
},
.memContext = MEM_CONTEXT_NEW(),
.client = client,
.verb = strDup(verb),
.path = strDup(path),
.query = httpQueryDupP(param.query),
.header = param.header == NULL ? httpHeaderNew(NULL) : httpHeaderDup(param.header, NULL),
.content = param.content == NULL ? NULL : bufDup(param.content),
};
@ -260,13 +257,13 @@ httpRequestError(const HttpRequest *this, HttpResponse *response)
// Output path/query
strCatZ(error, ":\n*** Path/Query ***:");
strCatFmt(error, "\n%s", strZ(this->path));
strCatFmt(error, "\n%s", strZ(httpRequestPath(this)));
if (this->query != NULL)
strCatFmt(error, "?%s", strZ(httpQueryRenderP(this->query, .redact = true)));
if (httpRequestQuery(this) != NULL)
strCatFmt(error, "?%s", strZ(httpQueryRenderP(httpRequestQuery(this), .redact = true)));
// Output request headers
const StringList *requestHeaderList = httpHeaderList(this->header);
const StringList *requestHeaderList = httpHeaderList(httpRequestHeader(this));
if (!strLstEmpty(requestHeaderList))
{
@ -278,7 +275,7 @@ httpRequestError(const HttpRequest *this, HttpResponse *response)
strCatFmt(
error, "\n%s: %s", strZ(key),
httpHeaderRedact(this->header, key) ? "<redacted>" : strZ(httpHeaderGet(this->header, key)));
httpHeaderRedact(httpRequestHeader(this), key) ? "<redacted>" : strZ(httpHeaderGet(httpRequestHeader(this), key)));
}
}
@ -312,7 +309,7 @@ String *
httpRequestToLog(const HttpRequest *this)
{
return strNewFmt(
"{verb: %s, path: %s, query: %s, header: %s, contentSize: %zu}", strZ(this->verb), strZ(this->path),
this->query == NULL ? "null" : strZ(httpQueryToLog(this->query)), strZ(httpHeaderToLog(this->header)),
this->content == NULL ? 0 : bufUsed(this->content));
"{verb: %s, path: %s, query: %s, header: %s, contentSize: %zu}", strZ(httpRequestVerb(this)), strZ(httpRequestPath(this)),
httpRequestQuery(this) == NULL ? "null" : strZ(httpQueryToLog(httpRequestQuery(this))),
strZ(httpHeaderToLog(httpRequestHeader(this))), this->content == NULL ? 0 : bufUsed(this->content));
}

View File

@ -77,6 +77,49 @@ typedef struct HttpRequestNewParam
HttpRequest *httpRequestNew(HttpClient *client, const String *verb, const String *path, HttpRequestNewParam param);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct HttpRequestPub
{
const String *verb; // HTTP verb (GET, POST, etc.)
const String *path; // HTTP path
const HttpQuery *query; // HTTP query
const HttpHeader *header; // HTTP headers
} HttpRequestPub;
// Request path
__attribute__((always_inline)) static inline const String *
httpRequestPath(const HttpRequest *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpRequestPub *)this)->path;
}
// Request query
__attribute__((always_inline)) static inline const HttpQuery *
httpRequestQuery(const HttpRequest *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpRequestPub *)this)->query;
}
// Request headers
__attribute__((always_inline)) static inline const HttpHeader *
httpRequestHeader(const HttpRequest *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpRequestPub *)this)->header;
}
// Request verb
__attribute__((always_inline)) static inline const String *
httpRequestVerb(const HttpRequest *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpRequestPub *)this)->verb;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
@ -89,21 +132,6 @@ void httpRequestError(const HttpRequest *this, HttpResponse *response) __attribu
// Move to a new parent mem context
HttpRequest *httpRequestMove(HttpRequest *this, MemContext *parentNew);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Request verb
const String *httpRequestVerb(const HttpRequest *this);
// Request path
const String *httpRequestPath(const HttpRequest *this);
// Request query
const HttpQuery *httpRequestQuery(const HttpRequest *this);
// Request headers
const HttpHeader *httpRequestHeader(const HttpRequest *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/

View File

@ -33,14 +33,10 @@ Object type
***********************************************************************************************************************************/
struct HttpResponse
{
HttpResponsePub pub; // Publicly accessible variables
MemContext *memContext; // Mem context
HttpSession *session; // HTTP session
IoRead *contentRead; // Read interface for response content
unsigned int code; // Response code (e.g. 200, 404)
String *reason; // Response reason e.g. (OK, Not Found)
HttpHeader *header; // Response headers
bool contentChunked; // Is the response content chunked?
uint64_t contentSize; // Content size (ignored for chunked)
@ -54,11 +50,6 @@ struct HttpResponse
OBJECT_DEFINE_MOVE(HTTP_RESPONSE);
OBJECT_DEFINE_FREE(HTTP_RESPONSE);
OBJECT_DEFINE_GET(IoRead, , HTTP_RESPONSE, IoRead *, contentRead);
OBJECT_DEFINE_GET(Code, const, HTTP_RESPONSE, unsigned int, code);
OBJECT_DEFINE_GET(Header, const, HTTP_RESPONSE, const HttpHeader *, header);
OBJECT_DEFINE_GET(Reason, const, HTTP_RESPONSE, const String *, reason);
/***********************************************************************************************************************************
When response is done close/reuse the connection
***********************************************************************************************************************************/
@ -224,9 +215,12 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
*this = (HttpResponse)
{
.pub =
{
.header = httpHeaderNew(NULL),
},
.memContext = MEM_CONTEXT_NEW(),
.session = httpSessionMove(session, memContextCurrent()),
.header = httpHeaderNew(NULL),
};
MEM_CONTEXT_TEMP_BEGIN()
@ -261,12 +255,12 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
if (spacePos != 3)
THROW_FMT(FormatError, "response status '%s' must have a space after the status code", strZ(status));
this->code = cvtZToUInt(strZ(strSubN(status, 0, (size_t)spacePos)));
this->pub.code = cvtZToUInt(strZ(strSubN(status, 0, (size_t)spacePos)));
// Read reason phrase. A missing reason phrase will be represented as an empty string.
MEM_CONTEXT_BEGIN(this->memContext)
{
this->reason = strSub(status, (size_t)spacePos + 1);
this->pub.reason = strSub(status, (size_t)spacePos + 1);
}
MEM_CONTEXT_END();
@ -289,7 +283,7 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
String *headerKey = strLower(strTrim(strSubN(header, 0, (size_t)colonPos)));
String *headerValue = strTrim(strSub(header, (size_t)colonPos + 1));
httpHeaderAdd(this->header, headerKey, headerValue);
httpHeaderAdd(this->pub.header, headerKey, headerValue);
// Read transfer encoding (only chunked is supported)
if (strEq(headerKey, HTTP_HEADER_TRANSFER_ENCODING_STR))
@ -336,8 +330,8 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
// rather than also checking if the io object exists.
MEM_CONTEXT_BEGIN(this->memContext)
{
this->contentRead = ioReadNewP(this, .eof = httpResponseEof, .read = httpResponseRead);
ioReadOpen(this->contentRead);
this->pub.contentRead = ioReadNewP(this, .eof = httpResponseEof, .read = httpResponseRead);
ioReadOpen(httpResponseIoRead(this));
}
MEM_CONTEXT_END();
@ -393,19 +387,6 @@ httpResponseContent(HttpResponse *this)
FUNCTION_TEST_RETURN(this->content);
}
/**********************************************************************************************************************************/
bool
httpResponseCodeOk(const HttpResponse *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(HTTP_RESPONSE, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->code / 100 == 2);
}
/**********************************************************************************************************************************/
String *
httpResponseToLog(const HttpResponse *this)
@ -413,7 +394,7 @@ httpResponseToLog(const HttpResponse *this)
return strNewFmt(
"{code: %u, reason: %s, header: %s, contentChunked: %s, contentSize: %" PRIu64 ", contentRemaining: %" PRIu64
", closeOnContentEof: %s, contentExists: %s, contentEof: %s, contentCached: %s}",
this->code, strZ(this->reason), strZ(httpHeaderToLog(this->header)), cvtBoolToConstZ(this->contentChunked),
this->contentSize, this->contentRemaining, cvtBoolToConstZ(this->closeOnContentEof), cvtBoolToConstZ(this->contentExists),
cvtBoolToConstZ(this->contentEof), cvtBoolToConstZ(this->content != NULL));
httpResponseCode(this), strZ(httpResponseReason(this)), strZ(httpHeaderToLog(httpResponseHeader(this))),
cvtBoolToConstZ(this->contentChunked), this->contentSize, this->contentRemaining, cvtBoolToConstZ(this->closeOnContentEof),
cvtBoolToConstZ(this->contentExists), cvtBoolToConstZ(this->contentEof), cvtBoolToConstZ(this->content != NULL));
}

View File

@ -31,11 +31,59 @@ Constructors
***********************************************************************************************************************************/
HttpResponse *httpResponseNew(HttpSession *session, const String *verb, bool contentCache);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct HttpResponsePub
{
IoRead *contentRead; // Read interface for response content
unsigned int code; // Response code (e.g. 200, 404)
HttpHeader *header; // Response headers
String *reason; // Response reason e.g. (OK, Not Found)
} HttpResponsePub;
// Read interface used to get the response content. This is intended for reading content that may be very large and will not be held
// in memory all at once. If the content must be loaded completely for processing (e.g. XML) then httpResponseContent() is simpler.
__attribute__((always_inline)) static inline IoRead *
httpResponseIoRead(HttpResponse *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpResponsePub *)this)->contentRead;
}
// Response code
__attribute__((always_inline)) static inline unsigned int
httpResponseCode(const HttpResponse *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpResponsePub *)this)->code;
}
// Response headers
__attribute__((always_inline)) static inline const HttpHeader *
httpResponseHeader(const HttpResponse *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpResponsePub *)this)->header;
}
// Response reason
__attribute__((always_inline)) static inline const String *
httpResponseReason(const HttpResponse *this)
{
ASSERT_INLINE(this != NULL);
return ((const HttpResponsePub *)this)->reason;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Is this response code OK, i.e. 2XX?
bool httpResponseCodeOk(const HttpResponse *this);
__attribute__((always_inline)) static inline bool
httpResponseCodeOk(const HttpResponse *this)
{
return httpResponseCode(this) / 100 == 2;
}
// Fetch all response content. Content will be cached so it can be retrieved again without additional cost.
const Buffer *httpResponseContent(HttpResponse *this);
@ -43,25 +91,6 @@ const Buffer *httpResponseContent(HttpResponse *this);
// Move to a new parent mem context
HttpResponse *httpResponseMove(HttpResponse *this, MemContext *parentNew);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Is the response still being read?
bool httpResponseBusy(const HttpResponse *this);
// Read interface used to get the response content. This is intended for reading content that may be very large and will not be held
// in memory all at once. If the content must be loaded completely for processing (e.g. XML) then httpResponseContent() is simpler.
IoRead *httpResponseIoRead(HttpResponse *this);
// Response code
unsigned int httpResponseCode(const HttpResponse *this);
// Response headers
const HttpHeader *httpResponseHeader(const HttpResponse *this);
// Response reason
const String *httpResponseReason(const HttpResponse *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/

View File

@ -34,27 +34,6 @@ Create a local "this" variable of the correct type from a THIS_VOID parameter
***********************************************************************************************************************************/
#define THIS(type) type *this = thisVoid
/***********************************************************************************************************************************
Define a function used to get an object member variable.
If the object type/prefix is "Object"/"object" then the macro:
OBJECT_DEFINE_GET(Size, const, OBJECT, size_t, size);
will define the function as:
size_t objectSize(const Object *this)
No function logging in required because no functions can be called which means no errors can be thrown.
***********************************************************************************************************************************/
#define OBJECT_DEFINE_GET(name, objectQualifier, objectMacro, returnType, objectMember) \
returnType \
GLUE(objectMacro##_PREFIX, name)(objectQualifier objectMacro##_TYPE *this) \
{ \
ASSERT(this != NULL); \
return this->objectMember; \
}
/***********************************************************************************************************************************
Define a function used by the caller to move an object from one context to another

View File

@ -14,16 +14,14 @@ Object type
***********************************************************************************************************************************/
struct Wait
{
WaitPub pub; // Publicly accessible variables
MemContext *memContext; // Context that contains the wait handler
TimeMSec waitTime; // Total time to wait (in usec)
TimeMSec remainTime; // Wait time remaining (in usec)
TimeMSec sleepTime; // Next sleep time (in usec)
TimeMSec sleepPrevTime; // Previous time slept (in usec)
TimeMSec beginTime; // Time the wait began (in epoch usec)
};
OBJECT_DEFINE_GET(Remaining, const, WAIT, TimeMSec, remainTime);
OBJECT_DEFINE_FREE(WAIT);
/**********************************************************************************************************************************/
@ -46,9 +44,12 @@ waitNew(TimeMSec waitTime)
*this = (Wait)
{
.pub =
{
.remainTime = waitTime,
},
.memContext = MEM_CONTEXT_NEW(),
.waitTime = waitTime,
.remainTime = waitTime,
};
// Calculate first sleep time -- start with 1/10th of a second for anything >= 1 second
@ -100,7 +101,7 @@ waitMore(Wait *this)
// Store new sleep times
this->sleepPrevTime = this->sleepTime;
this->sleepTime = sleepNextTime;
this->remainTime = this->waitTime - elapsedTime;
this->pub.remainTime = this->waitTime - elapsedTime;
}
// Else set sleep to zero so next call will return false
else

View File

@ -19,18 +19,28 @@ Constructors
***********************************************************************************************************************************/
Wait *waitNew(TimeMSec waitTime);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct WaitPub
{
TimeMSec remainTime; // Wait time remaining (in usec)
} WaitPub;
// How much time is remaining? Recalculated each time waitMore() is called.
__attribute__((always_inline)) static inline TimeMSec
waitRemaining(const Wait *this)
{
ASSERT_INLINE(this != NULL);
return ((const WaitPub *)this)->remainTime;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Wait and return whether the caller has more time left
bool waitMore(Wait *this);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// How much time is remaining? Recalculated each time waitMore() is called.
TimeMSec waitRemaining(const Wait *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/

View File

@ -306,7 +306,7 @@ testRun(void)
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("no output from server");
client->timeout = 0;
client->pub.timeout = 0;
hrnServerScriptAccept(http);
@ -469,7 +469,7 @@ testRun(void)
httpQueryAdd(query, strNew("name"), strNew("/path/A Z.txt"));
httpQueryAdd(query, strNew("type"), strNew("test"));
client->timeout = 5000;
client->pub.timeout = 5000;
HttpRequest *request = NULL;
HttpResponse *response = NULL;