From 32aa6d6e8fffa9cc9416811cd37693a3c9d2beef Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 7 Nov 2025 09:41:07 +0100 Subject: [PATCH 1/4] [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. --- libfreerdp/core/gateway/http.c | 231 ++++++++++++++------------------- libfreerdp/core/gateway/http.h | 15 ++- 2 files changed, 105 insertions(+), 141 deletions(-) diff --git a/libfreerdp/core/gateway/http.c b/libfreerdp/core/gateway/http.c index b44818974..86c8eb14c 100644 --- a/libfreerdp/core/gateway/http.c +++ b/libfreerdp/core/gateway/http.c @@ -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; +} diff --git a/libfreerdp/core/gateway/http.h b/libfreerdp/core/gateway/http.h index 07d4556ee..eaa07a91c 100644 --- a/libfreerdp/core/gateway/http.h +++ b/libfreerdp/core/gateway/http.h @@ -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; From 35ad10c345fb55e38134599e81fb986c63d4f3ba Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 7 Nov 2025 09:16:09 +0100 Subject: [PATCH 2/4] [gateway,websocket] use a dynamic logger --- libfreerdp/core/gateway/wst.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libfreerdp/core/gateway/wst.c b/libfreerdp/core/gateway/wst.c index 0a32d7db9..0ce48c437 100644 --- a/libfreerdp/core/gateway/wst.c +++ b/libfreerdp/core/gateway/wst.c @@ -66,11 +66,12 @@ struct rdp_wst uint16_t gwport; char* gwpath; websocket_context* wscontext; + wLog* log; }; static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=%s"; -static BOOL wst_get_gateway_credentials(rdpContext* context, rdp_auth_reason reason) +static BOOL wst_get_gateway_credentials(wLog* log, rdpContext* context, rdp_auth_reason reason) { WINPR_ASSERT(context); freerdp* instance = context->instance; @@ -85,7 +86,7 @@ static BOOL wst_get_gateway_credentials(rdpContext* context, rdp_auth_reason rea freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_CANCELLED); return FALSE; case AUTH_NO_CREDENTIALS: - WLog_INFO(TAG, "No credentials provided - using NULL identity"); + WLog_Print(log, WLOG_INFO, "No credentials provided - using NULL identity"); return TRUE; case AUTH_FAILED: default: @@ -108,7 +109,7 @@ static BOOL wst_auth_init(rdpWst* wst, rdpTls* tls, TCHAR* authPkg) if (!credssp_auth_init(wst->auth, authPkg, tls->Bindings)) return FALSE; - if (!wst_get_gateway_credentials(context, GW_AUTH_RDG)) + if (!wst_get_gateway_credentials(wst->log, context, GW_AUTH_RDG)) return FALSE; if (!identity_set_from_settings(&identity, settings, FreeRDP_GatewayUsername, @@ -226,7 +227,7 @@ static BOOL wst_tls_connect(rdpWst* wst, rdpTls* tls, UINT32 timeout) sockfd = freerdp_tcp_connect(wst->context, peerHostname, peerPort, timeout); - WLog_DBG(TAG, "connecting to %s %d", peerHostname, peerPort); + WLog_Print(wst->log, WLOG_DEBUG, "connecting to %s %d", peerHostname, peerPort); if (sockfd < 0) { return FALSE; @@ -340,8 +341,9 @@ static BOOL wst_send_http_request(rdpWst* wst, rdpTls* tls) return FALSE; const size_t sz = Stream_Length(s); - int status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz); + WLog_Print(wst->log, WLOG_TRACE, "header [%" PRIuz "]: %s", sz, Stream_Buffer(s)); + const int status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz); Stream_Free(s, TRUE); return (status >= 0); } @@ -360,8 +362,8 @@ static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, D if ((affinity || samesite) && freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport)) { - WLog_INFO(TAG, "Got ARRAffinity cookie %s", affinity); - WLog_INFO(TAG, "Got ARRAffinitySameSite cookie %s", samesite); + WLog_Print(wst->log, WLOG_INFO, "Got ARRAffinity cookie %s", affinity); + WLog_Print(wst->log, WLOG_INFO, "Got ARRAffinitySameSite cookie %s", samesite); if (affinity) http_context_set_cookie(wst->http, "ARRAffinity", affinity); if (samesite) @@ -487,8 +489,8 @@ static BOOL wst_handle_http_code(rdpWst* wst, UINT16 StatusCode) } char buffer[64] = { 0 }; - WLog_ERR(TAG, "Unexpected HTTP status: %s", - freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer))); + WLog_Print(wst->log, WLOG_ERROR, "Unexpected HTTP status: %s", + freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer))); freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_FAILED); return FALSE; } @@ -779,7 +781,8 @@ static BOOL wst_parse_url(rdpWst* wst, const char* url) { if (strncmp("https://", url, 8) != 0) { - WLog_ERR(TAG, "Websocket URL is invalid. Only wss:// or https:// URLs are supported"); + WLog_Print(wst->log, WLOG_ERROR, + "Websocket URL is invalid. Only wss:// or https:// URLs are supported"); return FALSE; } else @@ -834,6 +837,7 @@ rdpWst* wst_new(rdpContext* context) if (!wst) return NULL; + wst->log = WLog_Get(TAG); wst->context = context; wst->gwhostname = NULL; From f232d2ad5595893c3f240cde878e5b8661caaf26 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 7 Nov 2025 09:33:08 +0100 Subject: [PATCH 3/4] [gateway,websocket] make x-ms-user-agent optional --- libfreerdp/core/gateway/wst.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libfreerdp/core/gateway/wst.c b/libfreerdp/core/gateway/wst.c index 0ce48c437..fa8da6a31 100644 --- a/libfreerdp/core/gateway/wst.c +++ b/libfreerdp/core/gateway/wst.c @@ -69,7 +69,7 @@ struct rdp_wst wLog* log; }; -static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=%s"; +static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s"; static BOOL wst_get_gateway_credentials(wLog* log, rdpContext* context, rdp_auth_reason reason) { @@ -396,6 +396,16 @@ static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, D return FALSE; free(wst->gwpath); wst->gwpath = urlWithAuth; + if (!utils_str_is_empty(ua)) + { + size_t ualen = 0; + char* uastr = NULL; + winpr_asprintf(&uastr, &ualen, "%s&X-MS-User-Agent=%s", wst->gwpath, ua); + if (!uastr) + return FALSE; + free(wst->gwpath); + wst->gwpath = uastr; + } if (!http_context_set_uri(wst->http, wst->gwpath)) return FALSE; if (!http_context_enable_websocket_upgrade(wst->http, TRUE)) From c69aaef8e896ad317dd73725af2fc592b1a7a69c Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 7 Nov 2025 09:33:13 +0100 Subject: [PATCH 4/4] [gateway,arm] improve logging and url detection --- libfreerdp/core/gateway/arm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libfreerdp/core/gateway/arm.c b/libfreerdp/core/gateway/arm.c index 7c4bc5fdf..e547498be 100644 --- a/libfreerdp/core/gateway/arm.c +++ b/libfreerdp/core/gateway/arm.c @@ -297,6 +297,8 @@ static BOOL arm_send_http_request(rdpArm* arm, rdpTls* tls, const char* method, return FALSE; const size_t sz = Stream_Length(s); + WLog_Print(arm->log, WLOG_TRACE, "header [%" PRIuz "]: %s", sz, Stream_Buffer(s)); + WLog_Print(arm->log, WLOG_TRACE, "body [%" PRIuz "]: %s", content_length, data); status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz); Stream_Free(s, TRUE); @@ -987,7 +989,9 @@ static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t return FALSE; rdpSettings* settings = arm->context->settings; - WINPR_JSON* gwurl = WINPR_JSON_GetObjectItemCaseSensitive(json, "gatewayLocation"); + WINPR_JSON* gwurl = WINPR_JSON_GetObjectItemCaseSensitive(json, "gatewayLocationPreWebSocket"); + if (!gwurl) + gwurl = WINPR_JSON_GetObjectItemCaseSensitive(json, "gatewayLocation"); const char* gwurlstr = WINPR_JSON_GetStringValue(gwurl); if (gwurlstr != NULL) { @@ -1155,7 +1159,7 @@ static BOOL arm_handle_request(rdpArm* arm, BOOL* retry, DWORD timeout) const char* msuseragent = freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpMsUserAgent); if (!http_context_set_uri(arm->http, "/api/arm/v2/connections") || - !http_context_set_accept(arm->http, "application/json") || + !http_context_set_accept(arm->http, "*/*") || !http_context_set_cache_control(arm->http, "no-cache") || !http_context_set_pragma(arm->http, "no-cache") || !http_context_set_connection(arm->http, "Keep-Alive") ||