[gateway,http] improve header handling

In http context store header values in a hash table. This simplifies
handling of currently known and custom http headers.
This commit is contained in:
akallabeth
2025-11-07 09:41:07 +01:00
parent 2dbc042ea7
commit 32aa6d6e8f
2 changed files with 105 additions and 141 deletions

View File

@@ -41,6 +41,7 @@
#include "http.h"
#include "../tcp.h"
#include "../utils.h"
#define TAG FREERDP_TAG("core.gateway.http")
@@ -52,19 +53,12 @@ struct s_http_context
{
char* Method;
char* URI;
char* UserAgent;
char* X_MS_UserAgent;
char* Host;
char* Accept;
char* CacheControl;
char* Connection;
char* Pragma;
char* RdgConnectionId;
char* RdgCorrelationId;
char* RdgAuthScheme;
BOOL websocketUpgrade;
char* SecWebsocketKey;
wListDictionary* cookies;
wHashTable* headers;
};
struct s_http_request
@@ -75,8 +69,8 @@ struct s_http_request
char* AuthParam;
char* Authorization;
size_t ContentLength;
char* ContentType;
TRANSFER_ENCODING TransferEncoding;
wHashTable* headers;
};
struct s_http_response
@@ -101,6 +95,8 @@ struct s_http_response
wStream* data;
};
static wHashTable* HashTable_New_String(void);
static char* string_strnstr(char* str1, const char* str2, size_t slen)
{
char c = 0;
@@ -143,6 +139,10 @@ HttpContext* http_context_new(void)
if (!context)
return NULL;
context->headers = HashTable_New_String();
if (!context->headers)
goto fail;
context->cookies = ListDictionary_New(FALSE);
if (!context->cookies)
goto fail;
@@ -186,13 +186,7 @@ BOOL http_request_set_content_type(HttpRequest* request, const char* ContentType
if (!request || !ContentType)
return FALSE;
free(request->ContentType);
request->ContentType = _strdup(ContentType);
if (!request->ContentType)
return FALSE;
return TRUE;
return http_request_set_header(request, "Content-Type", "%s", ContentType);
}
const char* http_context_get_uri(HttpContext* context)
@@ -222,13 +216,7 @@ BOOL http_context_set_user_agent(HttpContext* context, const char* UserAgent)
if (!context || !UserAgent)
return FALSE;
free(context->UserAgent);
context->UserAgent = _strdup(UserAgent);
if (!context->UserAgent)
return FALSE;
return TRUE;
return http_context_set_header(context, "User-Agent", "%s", UserAgent);
}
BOOL http_context_set_x_ms_user_agent(HttpContext* context, const char* X_MS_UserAgent)
@@ -236,13 +224,7 @@ BOOL http_context_set_x_ms_user_agent(HttpContext* context, const char* X_MS_Use
if (!context || !X_MS_UserAgent)
return FALSE;
free(context->X_MS_UserAgent);
context->X_MS_UserAgent = _strdup(X_MS_UserAgent);
if (!context->X_MS_UserAgent)
return FALSE;
return TRUE;
return http_context_set_header(context, "X-MS-User-Agent", "%s", X_MS_UserAgent);
}
BOOL http_context_set_host(HttpContext* context, const char* Host)
@@ -250,13 +232,7 @@ BOOL http_context_set_host(HttpContext* context, const char* Host)
if (!context || !Host)
return FALSE;
free(context->Host);
context->Host = _strdup(Host);
if (!context->Host)
return FALSE;
return TRUE;
return http_context_set_header(context, "Host", "%s", Host);
}
BOOL http_context_set_accept(HttpContext* context, const char* Accept)
@@ -264,13 +240,7 @@ BOOL http_context_set_accept(HttpContext* context, const char* Accept)
if (!context || !Accept)
return FALSE;
free(context->Accept);
context->Accept = _strdup(Accept);
if (!context->Accept)
return FALSE;
return TRUE;
return http_context_set_header(context, "Accept", "%s", Accept);
}
BOOL http_context_set_cache_control(HttpContext* context, const char* CacheControl)
@@ -278,13 +248,7 @@ BOOL http_context_set_cache_control(HttpContext* context, const char* CacheContr
if (!context || !CacheControl)
return FALSE;
free(context->CacheControl);
context->CacheControl = _strdup(CacheControl);
if (!context->CacheControl)
return FALSE;
return TRUE;
return http_context_set_header(context, "Cache-Control", "%s", CacheControl);
}
BOOL http_context_set_connection(HttpContext* context, const char* Connection)
@@ -362,21 +326,20 @@ BOOL http_context_append_pragma(HttpContext* context, const char* Pragma, ...)
return list_append(context, Pragma, ap);
}
static char* guid2str(const GUID* guid)
static char* guid2str(const GUID* guid, char* buffer, size_t len)
{
if (!guid)
return NULL;
char* strguid = NULL;
char bracedGuid[64] = { 0 };
RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
if (rpcStatus != RPC_S_OK)
return NULL;
(void)sprintf_s(bracedGuid, sizeof(bracedGuid), "{%s}", strguid);
(void)sprintf_s(buffer, len, "{%s}", strguid);
RpcStringFreeA(&strguid);
return _strdup(bracedGuid);
return buffer;
}
BOOL http_context_set_rdg_connection_id(HttpContext* context, const GUID* RdgConnectionId)
@@ -384,13 +347,9 @@ BOOL http_context_set_rdg_connection_id(HttpContext* context, const GUID* RdgCon
if (!context || !RdgConnectionId)
return FALSE;
free(context->RdgConnectionId);
context->RdgConnectionId = guid2str(RdgConnectionId);
if (!context->RdgConnectionId)
return FALSE;
return TRUE;
char buffer[64] = { 0 };
return http_context_set_header(context, "RDG-Connection-Id", "%s",
guid2str(RdgConnectionId, buffer, sizeof(buffer)));
}
BOOL http_context_set_rdg_correlation_id(HttpContext* context, const GUID* RdgCorrelationId)
@@ -398,13 +357,9 @@ BOOL http_context_set_rdg_correlation_id(HttpContext* context, const GUID* RdgCo
if (!context || !RdgCorrelationId)
return FALSE;
free(context->RdgCorrelationId);
context->RdgCorrelationId = guid2str(RdgCorrelationId);
if (!context->RdgCorrelationId)
return FALSE;
return TRUE;
char buffer[64] = { 0 };
return http_context_set_header(context, "RDG-Correlation-Id", "%s",
guid2str(RdgCorrelationId, buffer, sizeof(buffer)));
}
BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
@@ -438,9 +393,7 @@ BOOL http_context_set_rdg_auth_scheme(HttpContext* context, const char* RdgAuthS
if (!context || !RdgAuthScheme)
return FALSE;
free(context->RdgAuthScheme);
context->RdgAuthScheme = _strdup(RdgAuthScheme);
return context->RdgAuthScheme != NULL;
return http_context_set_header(context, "RDG-Auth-Scheme", "%s", RdgAuthScheme);
}
BOOL http_context_set_cookie(HttpContext* context, const char* CookieName, const char* CookieValue)
@@ -465,18 +418,11 @@ void http_context_free(HttpContext* context)
if (context)
{
free(context->SecWebsocketKey);
free(context->UserAgent);
free(context->X_MS_UserAgent);
free(context->Host);
free(context->URI);
free(context->Accept);
free(context->Method);
free(context->CacheControl);
free(context->Connection);
free(context->Pragma);
free(context->RdgConnectionId);
free(context->RdgCorrelationId);
free(context->RdgAuthScheme);
HashTable_Free(context->headers);
ListDictionary_Free(context->cookies);
free(context);
}
@@ -651,6 +597,19 @@ unlock:
return status;
}
static BOOL write_headers(const void* pkey, void* pvalue, void* arg)
{
const char* key = pkey;
const char* value = pvalue;
wStream* s = arg;
WINPR_ASSERT(key);
WINPR_ASSERT(value);
WINPR_ASSERT(s);
return http_encode_body_line(s, key, value);
}
wStream* http_request_write(HttpContext* context, HttpRequest* request)
{
wStream* s = NULL;
@@ -664,11 +623,8 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request)
return NULL;
if (!http_encode_header_line(s, request->Method, request->URI) ||
!http_encode_body_line(s, "Cache-Control", context->CacheControl) ||
!http_encode_body_line(s, "Pragma", context->Pragma) ||
!http_encode_body_line(s, "Accept", context->Accept) ||
!http_encode_body_line(s, "User-Agent", context->UserAgent) ||
!http_encode_body_line(s, "Host", context->Host))
!http_encode_body_line(s, "Pragma", context->Pragma))
goto fail;
if (!context->websocketUpgrade)
@@ -685,24 +641,6 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request)
goto fail;
}
if (context->RdgConnectionId)
{
if (!http_encode_body_line(s, "RDG-Connection-Id", context->RdgConnectionId))
goto fail;
}
if (context->RdgCorrelationId)
{
if (!http_encode_body_line(s, "RDG-Correlation-Id", context->RdgCorrelationId))
goto fail;
}
if (context->RdgAuthScheme)
{
if (!http_encode_body_line(s, "RDG-Auth-Scheme", context->RdgAuthScheme))
goto fail;
}
if (request->TransferEncoding != TransferEncodingIdentity)
{
if (request->TransferEncoding == TransferEncodingChunked)
@@ -719,34 +657,25 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request)
goto fail;
}
if (request->Authorization)
if (!utils_str_is_empty(request->Authorization))
{
if (!http_encode_body_line(s, "Authorization", request->Authorization))
goto fail;
}
else if (request->AuthScheme && request->AuthParam)
else if (!utils_str_is_empty(request->AuthScheme) && !utils_str_is_empty(request->AuthParam))
{
if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
goto fail;
}
if (context->cookies)
{
if (!http_encode_cookie_line(s, context->cookies))
goto fail;
}
if (!HashTable_Foreach(context->headers, write_headers, s))
goto fail;
if (request->ContentType)
{
if (!http_encode_body_line(s, "Content-Type", request->ContentType))
goto fail;
}
if (!HashTable_Foreach(request->headers, write_headers, s))
goto fail;
if (context->X_MS_UserAgent)
{
if (!http_encode_body_line(s, "X-MS-User-Agent", context->X_MS_UserAgent))
goto fail;
}
if (!http_encode_cookie_line(s, context->cookies))
goto fail;
if (!http_encode_print(s, "\r\n"))
goto fail;
@@ -764,8 +693,14 @@ HttpRequest* http_request_new(void)
if (!request)
return NULL;
request->headers = HashTable_New_String();
if (!request->headers)
goto fail;
request->TransferEncoding = TransferEncodingIdentity;
return request;
fail:
http_request_free(request);
return NULL;
}
void http_request_free(HttpRequest* request)
@@ -776,9 +711,9 @@ void http_request_free(HttpRequest* request)
free(request->AuthParam);
free(request->AuthScheme);
free(request->Authorization);
free(request->ContentType);
free(request->Method);
free(request->URI);
HashTable_Free(request->headers);
free(request);
}
@@ -1527,6 +1462,7 @@ HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
return response;
out_error:
WLog_ERR(TAG, "No response");
http_response_free(response);
return NULL;
}
@@ -1539,7 +1475,7 @@ const BYTE* http_response_get_body(const HttpResponse* response)
return response->BodyContent;
}
static wHashTable* HashTable_New_String(void)
wHashTable* HashTable_New_String(void)
{
wHashTable* table = HashTable_New(FALSE);
if (!table)
@@ -1730,21 +1666,6 @@ void http_response_log_error_status_(wLog* log, DWORD level, const HttpResponse*
http_response_print(log, level, response, file, line, fkt);
}
WINPR_ATTR_FORMAT_ARG(3, 4)
BOOL http_request_append_header(wStream* stream, const char* param,
WINPR_FORMAT_ARG const char* value, ...)
{
va_list ap;
va_start(ap, value);
char* str = NULL;
size_t slen = 0;
winpr_vasprintf(&str, &slen, value, ap);
va_end(ap);
const BOOL rc = http_encode_body_line(stream, param, str);
free(str);
return rc;
}
static BOOL extract_cookie(const void* pkey, void* pvalue, void* arg)
{
const char* key = pkey;
@@ -1765,3 +1686,39 @@ BOOL http_response_extract_cookies(const HttpResponse* response, HttpContext* co
return HashTable_Foreach(response->SetCookie, extract_cookie, context);
}
FREERDP_LOCAL BOOL http_context_set_header(HttpContext* context, const char* key, const char* value,
...)
{
WINPR_ASSERT(context);
va_list ap;
va_start(ap, value);
const BOOL rc = http_context_set_header_va(context, key, value, ap);
va_end(ap);
return rc;
}
BOOL http_request_set_header(HttpRequest* request, const char* key, const char* value, ...)
{
WINPR_ASSERT(request);
char* v = NULL;
size_t vlen = 0;
va_list ap;
va_start(ap, value);
winpr_vasprintf(&v, &vlen, value, ap);
va_end(ap);
const BOOL rc = HashTable_Insert(request->headers, key, v);
free(v);
return rc;
}
BOOL http_context_set_header_va(HttpContext* context, const char* key, const char* value,
va_list ap)
{
char* v = NULL;
size_t vlen = 0;
winpr_vasprintf(&v, &vlen, value, ap);
const BOOL rc = HashTable_Insert(context->headers, key, v);
free(v);
return rc;
}

View File

@@ -77,6 +77,14 @@ FREERDP_LOCAL BOOL http_context_set_rdg_connection_id(HttpContext* context,
const GUID* RdgConnectionId);
FREERDP_LOCAL BOOL http_context_set_rdg_correlation_id(HttpContext* context,
const GUID* RdgConnectionId);
WINPR_ATTR_FORMAT_ARG(3, 4)
FREERDP_LOCAL BOOL http_context_set_header(HttpContext* context, const char* key,
WINPR_FORMAT_ARG const char* value, ...);
WINPR_ATTR_FORMAT_ARG(3, 0)
FREERDP_LOCAL BOOL http_context_set_header_va(HttpContext* context, const char* key,
WINPR_FORMAT_ARG const char* value, va_list ap);
FREERDP_LOCAL BOOL http_context_set_rdg_auth_scheme(HttpContext* context,
const char* RdgAuthScheme);
FREERDP_LOCAL BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable);
@@ -102,11 +110,10 @@ FREERDP_LOCAL BOOL http_request_set_auth_param(HttpRequest* request, const char*
FREERDP_LOCAL BOOL http_request_set_transfer_encoding(HttpRequest* request,
TRANSFER_ENCODING TransferEncoding);
FREERDP_LOCAL wStream* http_request_write(HttpContext* context, HttpRequest* request);
WINPR_ATTR_FORMAT_ARG(3, 4)
FREERDP_LOCAL BOOL http_request_append_header(wStream* stream, const char* param,
WINPR_FORMAT_ARG const char* value, ...);
FREERDP_LOCAL BOOL http_request_set_header(HttpRequest* request, const char* key,
WINPR_FORMAT_ARG const char* value, ...);
FREERDP_LOCAL wStream* http_request_write(HttpContext* context, HttpRequest* request);
/* HTTP response */
typedef struct s_http_response HttpResponse;