You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Allow redactions for HTTP queries.
The Azure storage driver exposes secrets in the query when using SAS authorization. These secrets can show up during logging or when an error occurs. Allow redaction of queries to prevent secrets from being exposed in logs and errors.
This commit is contained in:
@ -27,7 +27,7 @@ cmdRepoCreate(void)
|
||||
{
|
||||
storageAzureRequestP(
|
||||
(StorageAzure *)storageDriver(storageRepoWrite()), HTTP_VERB_PUT_STR,
|
||||
.query = httpQueryAdd(httpQueryNew(), AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_VALUE_CONTAINER_STR));
|
||||
.query = httpQueryAdd(httpQueryNewP(), AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_VALUE_CONTAINER_STR));
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
@ -17,6 +17,7 @@ struct HttpQuery
|
||||
{
|
||||
MemContext *memContext; // Mem context
|
||||
KeyValue *kv; // KeyValue store
|
||||
const StringList *redactList; // List of keys to redact values for
|
||||
};
|
||||
|
||||
OBJECT_DEFINE_MOVE(HTTP_QUERY);
|
||||
@ -24,9 +25,11 @@ OBJECT_DEFINE_FREE(HTTP_QUERY);
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
HttpQuery *
|
||||
httpQueryNew(void)
|
||||
httpQueryNew(HttpQueryNewParam param)
|
||||
{
|
||||
FUNCTION_TEST_VOID();
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STRING_LIST, param.redactList);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
HttpQuery *this = NULL;
|
||||
|
||||
@ -39,6 +42,7 @@ httpQueryNew(void)
|
||||
{
|
||||
.memContext = MEM_CONTEXT_NEW(),
|
||||
.kv = kvNew(),
|
||||
.redactList = strLstDup(param.redactList),
|
||||
};
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
@ -101,10 +105,11 @@ httpQueryNewStr(const String *query)
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
HttpQuery *
|
||||
httpQueryDup(const HttpQuery *query)
|
||||
httpQueryDup(const HttpQuery *query, HttpQueryDupParam param)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(HTTP_QUERY, query);
|
||||
FUNCTION_TEST_PARAM(STRING_LIST, param.redactList);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
HttpQuery *this = NULL;
|
||||
@ -120,6 +125,7 @@ httpQueryDup(const HttpQuery *query)
|
||||
{
|
||||
.memContext = MEM_CONTEXT_NEW(),
|
||||
.kv = kvDup(query->kv),
|
||||
.redactList = param.redactList != NULL ? strLstDup(param.redactList) : strLstDup(query->redactList),
|
||||
};
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
@ -231,38 +237,61 @@ httpQueryPut(HttpQuery *this, const String *key, const String *value)
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
String *
|
||||
httpQueryRender(const HttpQuery *this)
|
||||
bool
|
||||
httpQueryRedact(const HttpQuery *this, const String *key)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(HTTP_QUERY, this);
|
||||
FUNCTION_TEST_PARAM(STRING, key);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(key != NULL);
|
||||
|
||||
FUNCTION_TEST_RETURN(this->redactList != NULL && strLstExists(this->redactList, key));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
String *
|
||||
httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(HTTP_QUERY, this);
|
||||
FUNCTION_TEST_PARAM(BOOL, param.redact);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
String *result = NULL;
|
||||
|
||||
if (this != NULL)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
const StringList *keyList = httpQueryList(this);
|
||||
|
||||
if (strLstSize(keyList) > 0)
|
||||
{
|
||||
result = strNew("");
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = strNew("");
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
|
||||
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
||||
{
|
||||
const String *key = strLstGet(keyList, keyIdx);
|
||||
|
||||
if (strSize(result) != 0)
|
||||
strCatZ(result, "&");
|
||||
|
||||
strCatFmt(
|
||||
result, "%s=%s", strPtr(strLstGet(keyList, keyIdx)),
|
||||
strPtr(httpUriEncode(httpQueryGet(this, strLstGet(keyList, keyIdx)), false)));
|
||||
result, "%s=%s", strPtr(key),
|
||||
param.redact && httpQueryRedact(this, key) ?
|
||||
"<redacted>" : strPtr(httpUriEncode(httpQueryGet(this, key), false)));
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
@ -276,12 +305,17 @@ httpQueryToLog(const HttpQuery *this)
|
||||
|
||||
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
||||
{
|
||||
const String *key = strLstGet(keyList, keyIdx);
|
||||
|
||||
if (strSize(result) != 1)
|
||||
strCatZ(result, ", ");
|
||||
|
||||
strCatFmt(
|
||||
result, "%s: '%s'", strPtr(strLstGet(keyList, keyIdx)),
|
||||
strPtr(httpQueryGet(this, strLstGet(keyList, keyIdx))));
|
||||
strCatFmt(result, "%s: ", strPtr(key));
|
||||
|
||||
if (httpQueryRedact(this, key))
|
||||
strCatZ(result, "<redacted>");
|
||||
else
|
||||
strCatFmt(result, "'%s'", strPtr(httpQueryGet(this, key)));
|
||||
}
|
||||
|
||||
strCatZ(result, "}");
|
||||
|
@ -19,12 +19,31 @@ typedef struct HttpQuery HttpQuery;
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
HttpQuery *httpQueryNew(void);
|
||||
typedef struct HttpQueryNewParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
const StringList *redactList; // List of keys to redact values for
|
||||
} HttpQueryNewParam;
|
||||
|
||||
#define httpQueryNewP(...) \
|
||||
httpQueryNew((HttpQueryNewParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||
|
||||
HttpQuery *httpQueryNew(HttpQueryNewParam param);
|
||||
|
||||
// New from encoded query string
|
||||
HttpQuery *httpQueryNewStr(const String *query);
|
||||
|
||||
HttpQuery *httpQueryDup(const HttpQuery *query);
|
||||
// Duplicate
|
||||
typedef struct HttpQueryDupParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
const StringList *redactList; // List of keys to redact values for
|
||||
} HttpQueryDupParam;
|
||||
|
||||
#define httpQueryDupP(query, ...) \
|
||||
httpQueryDup(query, (HttpQueryDupParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||
|
||||
HttpQuery *httpQueryDup(const HttpQuery *query, HttpQueryDupParam param);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
@ -47,8 +66,20 @@ HttpQuery *httpQueryMove(HttpQuery *this, MemContext *parentNew);
|
||||
// Put a query item
|
||||
HttpQuery *httpQueryPut(HttpQuery *this, const String *header, const String *value);
|
||||
|
||||
// Should the query key be redacted when logging?
|
||||
bool httpQueryRedact(const HttpQuery *this, const String *key);
|
||||
|
||||
// Render the query for inclusion in an HTTP request
|
||||
String *httpQueryRender(const HttpQuery *this);
|
||||
typedef struct HttpQueryRenderParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
bool redact; // Redact user-visible query string
|
||||
} HttpQueryRenderParam;
|
||||
|
||||
#define httpQueryRenderP(this, ...) \
|
||||
httpQueryRender(this, (HttpQueryRenderParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||
|
||||
String *httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Destructor
|
||||
|
@ -104,7 +104,7 @@ httpRequestProcess(HttpRequest *this, bool requestOnly, bool contentCache)
|
||||
String *requestStr =
|
||||
strNewFmt(
|
||||
"%s %s%s%s " HTTP_VERSION CRLF_Z, strPtr(this->verb), strPtr(httpUriEncode(this->uri, true)),
|
||||
this->query == NULL ? "" : "?", this->query == NULL ? "" : strPtr(httpQueryRender(this->query)));
|
||||
this->query == NULL ? "" : "?", this->query == NULL ? "" : strPtr(httpQueryRenderP(this->query)));
|
||||
|
||||
// Add headers
|
||||
const StringList *headerList = httpHeaderList(this->header);
|
||||
@ -203,7 +203,7 @@ httpRequestNew(HttpClient *client, const String *verb, const String *uri, HttpRe
|
||||
.client = client,
|
||||
.verb = strDup(verb),
|
||||
.uri = strDup(uri),
|
||||
.query = httpQueryDup(param.query),
|
||||
.query = httpQueryDupP(param.query),
|
||||
.header = param.header == NULL ? httpHeaderNew(NULL) : httpHeaderDup(param.header, NULL),
|
||||
.content = param.content == NULL ? NULL : bufDup(param.content),
|
||||
};
|
||||
@ -256,7 +256,7 @@ httpRequestError(const HttpRequest *this, HttpResponse *response)
|
||||
strCatFmt(error, "\n%s", strPtr(httpUriEncode(this->uri, true)));
|
||||
|
||||
if (this->query != NULL)
|
||||
strCatFmt(error, "?%s", strPtr(httpQueryRender(this->query)));
|
||||
strCatFmt(error, "?%s", strPtr(httpQueryRenderP(this->query, .redact = true)));
|
||||
|
||||
// Output request headers
|
||||
const StringList *requestHeaderList = httpHeaderList(this->header);
|
||||
|
@ -45,6 +45,7 @@ STRING_EXTERN(AZURE_QUERY_COMP_STR, AZURE_QUERY_
|
||||
STRING_STATIC(AZURE_QUERY_DELIMITER_STR, "delimiter");
|
||||
STRING_STATIC(AZURE_QUERY_PREFIX_STR, "prefix");
|
||||
STRING_EXTERN(AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_RESTYPE);
|
||||
STRING_STATIC(AZURE_QUERY_SIG_STR, "sig");
|
||||
|
||||
STRING_STATIC(AZURE_QUERY_VALUE_LIST_STR, "list");
|
||||
STRING_EXTERN(AZURE_QUERY_VALUE_CONTAINER_STR, AZURE_QUERY_VALUE_CONTAINER);
|
||||
@ -70,6 +71,7 @@ struct StorageAzure
|
||||
MemContext *memContext;
|
||||
HttpClient *httpClient; // Http client to service requests
|
||||
StringList *headerRedactList; // List of headers to redact from logging
|
||||
StringList *queryRedactList; // List of query keys to redact from logging
|
||||
|
||||
const String *container; // Container to store data in
|
||||
const String *account; // Account
|
||||
@ -237,7 +239,10 @@ storageAzureRequestAsync(StorageAzure *this, const String *verb, StorageAzureReq
|
||||
}
|
||||
|
||||
// Make a copy of the query so it can be modified
|
||||
HttpQuery *query = this->sasKey != NULL && param.query == NULL ? httpQueryNew() : httpQueryDup(param.query);
|
||||
HttpQuery *query =
|
||||
this->sasKey != NULL && param.query == NULL ?
|
||||
httpQueryNewP(.redactList = this->queryRedactList) :
|
||||
httpQueryDupP(param.query, .redactList = this->queryRedactList);
|
||||
|
||||
// Generate authorization header
|
||||
storageAzureAuth(this, verb, httpUriEncode(param.uri, true), query, httpDateFromTime(time(NULL)), requestHeader);
|
||||
@ -363,7 +368,7 @@ storageAzureListInternal(
|
||||
// free memory at regular intervals
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
|
||||
// Add continuation token from the prior loop if any
|
||||
if (marker != NULL)
|
||||
@ -742,6 +747,10 @@ storageAzureNew(
|
||||
strLstAdd(driver->headerRedactList, HTTP_HEADER_AUTHORIZATION_STR);
|
||||
strLstAdd(driver->headerRedactList, HTTP_HEADER_DATE_STR);
|
||||
|
||||
// Create list of redacted query keys
|
||||
driver->queryRedactList = strLstNew();
|
||||
strLstAdd(driver->queryRedactList, AZURE_QUERY_SIG_STR);
|
||||
|
||||
// Generate starting file id
|
||||
cryptoRandomBytes((unsigned char *)&driver->fileId, sizeof(driver->fileId));
|
||||
|
||||
|
@ -141,7 +141,7 @@ storageWriteAzureBlockAsync(StorageWriteAzure *this)
|
||||
const String *blockId = strNewFmt("%016" PRIX64 "x%07u", this->fileId, strLstSize(this->blockIdList));
|
||||
|
||||
// Upload the block and add to block list
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
httpQueryAdd(query, AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_STR);
|
||||
httpQueryAdd(query, AZURE_QUERY_BLOCK_ID_STR, blockId);
|
||||
|
||||
@ -240,7 +240,7 @@ storageWriteAzureClose(THIS_VOID)
|
||||
// Finalize the multi-block upload
|
||||
storageAzureRequestP(
|
||||
this->storage, HTTP_VERB_PUT_STR, .uri = this->interface.name,
|
||||
.query = httpQueryAdd(httpQueryNew(), AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_LIST_STR),
|
||||
.query = httpQueryAdd(httpQueryNewP(), AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_LIST_STR),
|
||||
.content = xmlDocumentBuf(blockXml));
|
||||
}
|
||||
// Else upload all the data in a single block
|
||||
|
@ -173,7 +173,7 @@ storageS3Auth(
|
||||
String *signedHeaders = NULL;
|
||||
|
||||
String *canonicalRequest = strNewFmt(
|
||||
"%s\n%s\n%s\n", strPtr(verb), strPtr(uri), query == NULL ? "" : strPtr(httpQueryRender(query)));
|
||||
"%s\n%s\n%s\n", strPtr(verb), strPtr(uri), query == NULL ? "" : strPtr(httpQueryRenderP(query)));
|
||||
|
||||
for (unsigned int headerIdx = 0; headerIdx < strLstSize(headerList); headerIdx++)
|
||||
{
|
||||
@ -436,7 +436,7 @@ storageS3ListInternal(
|
||||
// free memory at regular intervals
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
|
||||
// Add continuation token from the prior loop if any
|
||||
if (continuationToken != NULL)
|
||||
@ -700,7 +700,7 @@ storageS3PathRemoveInternal(StorageS3 *this, XmlDocument *request)
|
||||
|
||||
const Buffer *response = httpResponseContent(
|
||||
storageS3RequestP(
|
||||
this, HTTP_VERB_POST_STR, FSLASH_STR, .query = httpQueryAdd(httpQueryNew(), S3_QUERY_DELETE_STR, EMPTY_STR),
|
||||
this, HTTP_VERB_POST_STR, FSLASH_STR, .query = httpQueryAdd(httpQueryNewP(), S3_QUERY_DELETE_STR, EMPTY_STR),
|
||||
.content = xmlDocumentBuf(request)));
|
||||
|
||||
// Nothing is returned when there are no errors
|
||||
|
@ -128,7 +128,7 @@ storageWriteS3PartAsync(StorageWriteS3 *this)
|
||||
httpResponseContent(
|
||||
storageS3RequestP(
|
||||
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
||||
.query = httpQueryAdd(httpQueryNew(), S3_QUERY_UPLOADS_STR, EMPTY_STR)))));
|
||||
.query = httpQueryAdd(httpQueryNewP(), S3_QUERY_UPLOADS_STR, EMPTY_STR)))));
|
||||
|
||||
// Store the upload id
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
@ -140,7 +140,7 @@ storageWriteS3PartAsync(StorageWriteS3 *this)
|
||||
}
|
||||
|
||||
// Upload the part async
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
httpQueryAdd(query, S3_QUERY_UPLOAD_ID_STR, this->uploadId);
|
||||
httpQueryAdd(query, S3_QUERY_PART_NUMBER_STR, strNewFmt("%u", strLstSize(this->uploadPartList) + 1));
|
||||
|
||||
@ -238,7 +238,7 @@ storageWriteS3Close(THIS_VOID)
|
||||
// Finalize the multi-part upload
|
||||
storageS3RequestP(
|
||||
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
||||
.query = httpQueryAdd(httpQueryNew(), S3_QUERY_UPLOAD_ID_STR, this->uploadId),
|
||||
.query = httpQueryAdd(httpQueryNewP(), S3_QUERY_UPLOAD_ID_STR, this->uploadId),
|
||||
.content = xmlDocumentBuf(partList));
|
||||
}
|
||||
// Else upload all the data in a single put
|
||||
|
@ -105,30 +105,44 @@ testRun(void)
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
TEST_ASSIGN(query, httpQueryNew(), "new query");
|
||||
StringList *redactList = strLstNew();
|
||||
strLstAdd(redactList, STRDEF("key2"));
|
||||
|
||||
TEST_ASSIGN(query, httpQueryNewP(.redactList = redactList), "new query");
|
||||
|
||||
TEST_RESULT_PTR(httpQueryMove(query, memContextPrior()), query, "move to new context");
|
||||
TEST_RESULT_PTR(httpQueryMove(NULL, memContextPrior()), NULL, "move null to new context");
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_RESULT_STR(httpQueryRender(NULL), NULL, "null query renders null");
|
||||
TEST_RESULT_STR(httpQueryRender(query), NULL, "empty query renders null");
|
||||
TEST_RESULT_STR(httpQueryRenderP(NULL), NULL, "null query renders null");
|
||||
TEST_RESULT_STR(httpQueryRenderP(query), NULL, "empty query renders null");
|
||||
|
||||
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key2"), strNew("value2")), query, "add query");
|
||||
TEST_ERROR(httpQueryAdd(query, strNew("key2"), strNew("value2")), AssertError, "key 'key2' already exists");
|
||||
TEST_RESULT_PTR(httpQueryPut(query, strNew("key2"), strNew("value2a")), query, "put query");
|
||||
TEST_RESULT_STR_Z(httpQueryRender(query), "key2=value2a", "render one query item");
|
||||
TEST_RESULT_STR_Z(httpQueryRenderP(query), "key2=value2a", "render one query item");
|
||||
|
||||
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key1"), strNew("value 1?")), query, "add query");
|
||||
TEST_RESULT_STR_Z(strLstJoin(httpQueryList(query), ", "), "key1, key2", "query list");
|
||||
TEST_RESULT_STR_Z(httpQueryRender(query), "key1=value%201%3F&key2=value2a", "render two query items");
|
||||
TEST_RESULT_STR_Z(httpQueryRenderP(query), "key1=value%201%3F&key2=value2a", "render two query items");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpQueryRenderP(query, .redact = true), "key1=value%201%3F&key2=<redacted>", "render two query items with redaction");
|
||||
|
||||
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key1")), "value 1?", "get value");
|
||||
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key2")), "value2a", "get value");
|
||||
TEST_RESULT_STR(httpQueryGet(query, strNew(BOGUS_STR)), NULL, "get missing value");
|
||||
|
||||
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: 'value 1?', key2: 'value2a'}", "log output");
|
||||
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: 'value 1?', key2: <redacted>}", "log output");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("dup query with redaction");
|
||||
|
||||
StringList *redactList = strLstNew();
|
||||
strLstAdd(redactList, STRDEF("key1"));
|
||||
|
||||
TEST_ASSIGN(query, httpQueryDupP(query, .redactList = redactList), "dup query");
|
||||
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: <redacted>, key2: 'value2a'}", "log output");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("new query from string");
|
||||
@ -137,13 +151,13 @@ testRun(void)
|
||||
|
||||
HttpQuery *query2 = NULL;
|
||||
TEST_ASSIGN(query2, httpQueryNewStr(STRDEF("?a=%2Bb&c=d%3D")), "query from string");
|
||||
TEST_RESULT_STR_Z(httpQueryRender(query2), "a=%2Bb&c=d%3D", "render query");
|
||||
TEST_RESULT_STR_Z(httpQueryRenderP(query2), "a=%2Bb&c=d%3D", "render query");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("merge queries");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
httpQueryRender(httpQueryMerge(query, query2)), "a=%2Bb&c=d%3D&key1=value%201%3F&key2=value2a", "render merge");
|
||||
httpQueryRenderP(httpQueryMerge(query, query2)), "a=%2Bb&c=d%3D&key1=value%201%3F&key2=value2a", "render merge");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("free query");
|
||||
@ -352,7 +366,7 @@ testRun(void)
|
||||
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
||||
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
||||
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
httpQueryAdd(query, strNew("name"), strNew("/path/A Z.txt"));
|
||||
httpQueryAdd(query, strNew("type"), strNew("test"));
|
||||
|
||||
@ -376,7 +390,7 @@ testRun(void)
|
||||
TEST_RESULT_STR_Z(httpRequestVerb(request), "GET", "check request verb");
|
||||
TEST_RESULT_STR_Z(httpRequestUri(request), "/", "check request uri");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpQueryRender(httpRequestQuery(request)), "name=%2Fpath%2FA%20Z.txt&type=test", "check request query");
|
||||
httpQueryRenderP(httpRequestQuery(request)), "name=%2Fpath%2FA%20Z.txt&type=test", "check request query");
|
||||
TEST_RESULT_PTR_NE(httpRequestHeader(request), NULL, "check request headers");
|
||||
|
||||
TEST_RESULT_UINT(httpResponseCode(response), 200, "check response code");
|
||||
@ -484,7 +498,7 @@ testRun(void)
|
||||
TEST_ASSIGN(
|
||||
request,
|
||||
httpRequestNewP(
|
||||
client, strNew("GET"), strNew("/"), .query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b")),
|
||||
client, strNew("GET"), strNew("/"), .query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b")),
|
||||
.header = headerRequest),
|
||||
"request");
|
||||
TEST_ASSIGN(response, httpRequest(request, false), "response");
|
||||
|
@ -44,7 +44,7 @@ testRequest(const char *verb, const char *uri, TestRequestParam param)
|
||||
// When SAS spit out the query and merge in the SAS key
|
||||
if (driver->sasKey != NULL)
|
||||
{
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
StringList *uriQuery = strLstNewSplitZ(STR(uri), "?");
|
||||
|
||||
if (strLstSize(uriQuery) == 2)
|
||||
@ -54,7 +54,7 @@ testRequest(const char *verb, const char *uri, TestRequestParam param)
|
||||
|
||||
strCat(request, strLstGet(uriQuery, 0));
|
||||
strCatZ(request, "?");
|
||||
strCat(request, httpQueryRender(query));
|
||||
strCat(request, httpQueryRenderP(query));
|
||||
}
|
||||
// Else just output URI as is
|
||||
else
|
||||
@ -235,7 +235,7 @@ testRun(void)
|
||||
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("44"));
|
||||
httpHeaderAdd(header, HTTP_HEADER_CONTENT_MD5_STR, STRDEF("b64f49553d5c441652e95697a2c5949e"));
|
||||
|
||||
HttpQuery *query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b"));
|
||||
HttpQuery *query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b"));
|
||||
|
||||
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
||||
TEST_RESULT_STR_Z(
|
||||
@ -256,13 +256,13 @@ testRun(void)
|
||||
16, NULL, 443, 1000, true, NULL, NULL)),
|
||||
"new azure storage - sas key");
|
||||
|
||||
query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b"));
|
||||
query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b"));
|
||||
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("66"));
|
||||
|
||||
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
||||
TEST_RESULT_STR_Z(
|
||||
httpHeaderToLog(header), "{content-length: '66', host: 'account.blob.core.windows.net'}", "check headers");
|
||||
TEST_RESULT_STR_Z(httpQueryRender(query), "a=b&sig=key", "check query");
|
||||
TEST_RESULT_STR_Z(httpQueryRenderP(query), "a=b&sig=key", "check query");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
@ -742,6 +742,22 @@ testRun(void)
|
||||
|
||||
TEST_RESULT_VOID(storageRemoveP(storage, strNew("/path/to/missing.txt")), "remove");
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("remove files error to check redacted sig");
|
||||
|
||||
testRequestP(HTTP_VERB_GET, "?comp=list&restype=container");
|
||||
testResponseP(.code = 403);
|
||||
|
||||
TEST_ERROR_FMT(
|
||||
storagePathRemoveP(storage, strNew("/"), .recurse = true), ProtocolError,
|
||||
"HTTP request failed with 403 (Forbidden):\n"
|
||||
"*** URI/Query ***:\n"
|
||||
"/account/container?comp=list&restype=container&sig=<redacted>\n"
|
||||
"*** Request Headers ***:\n"
|
||||
"content-length: 0\n"
|
||||
"host: %s",
|
||||
strPtr(hrnTlsServerHost()));
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("remove files from root");
|
||||
|
||||
|
@ -313,7 +313,7 @@ testRun(void)
|
||||
|
||||
HttpHeader *header = httpHeaderNew(NULL);
|
||||
|
||||
HttpQuery *query = httpQueryNew();
|
||||
HttpQuery *query = httpQueryNewP();
|
||||
httpQueryAdd(query, strNew("list-type"), strNew("2"));
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
|
Reference in New Issue
Block a user