20#include <freerdp/config.h>
26#include <winpr/print.h>
27#include <winpr/stream.h>
28#include <winpr/string.h>
30#include <winpr/sysinfo.h>
32#include <freerdp/log.h>
33#include <freerdp/crypto/crypto.h>
36#include <winpr/crypto.h>
38#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
39#include <valgrind/memcheck.h>
46#define TAG FREERDP_TAG("core.gateway.http")
48#define RESPONSE_SIZE_LIMIT (64ULL * 1024ULL * 1024ULL)
50#define WEBSOCKET_MAGIC_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
58 BOOL websocketUpgrade;
59 char* SecWebsocketKey;
60 wListDictionary* cookies;
72 TRANSFER_ENCODING TransferEncoding;
86 TRANSFER_ENCODING TransferEncoding;
87 char* SecWebsocketVersion;
88 char* SecWebsocketAccept;
93 wHashTable* Authenticates;
94 wHashTable* SetCookie;
98static wHashTable* HashTable_New_String(
void);
100static const char* string_strnstr(
const char* str1,
const char* str2,
size_t slen)
106 if ((c = *str2++) !=
'\0')
108 len = strnlen(str2, slen + 1);
114 if (slen-- < 1 || (sc = *str1++) ==
'\0')
120 }
while (strncmp(str1, str2, len) != 0);
128static BOOL strings_equals_nocase(
const void* obj1,
const void* obj2)
133 return _stricmp(obj1, obj2) == 0;
136HttpContext* http_context_new(
void)
138 HttpContext* context = (HttpContext*)calloc(1,
sizeof(HttpContext));
142 context->headers = HashTable_New_String();
143 if (!context->headers)
146 context->cookies = ListDictionary_New(FALSE);
147 if (!context->cookies)
151 wObject* key = ListDictionary_KeyObject(context->cookies);
152 wObject* value = ListDictionary_ValueObject(context->cookies);
156 key->fnObjectFree = winpr_ObjectStringFree;
157 key->fnObjectNew = winpr_ObjectStringClone;
158 value->fnObjectFree = winpr_ObjectStringFree;
159 value->fnObjectNew = winpr_ObjectStringClone;
165 WINPR_PRAGMA_DIAG_PUSH
166 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
167 http_context_free(context);
168 WINPR_PRAGMA_DIAG_POP
172BOOL http_context_set_method(HttpContext* context,
const char* Method)
174 if (!context || !Method)
177 free(context->Method);
178 context->Method = _strdup(Method);
180 if (!context->Method)
186BOOL http_request_set_content_type(HttpRequest* request,
const char* ContentType)
188 if (!request || !ContentType)
191 return http_request_set_header(request,
"Content-Type",
"%s", ContentType);
194const char* http_context_get_uri(HttpContext* context)
202BOOL http_context_set_uri(HttpContext* context,
const char* URI)
204 if (!context || !URI)
208 context->URI = _strdup(URI);
216BOOL http_context_set_user_agent(HttpContext* context,
const char* UserAgent)
218 if (!context || !UserAgent)
221 return http_context_set_header(context,
"User-Agent",
"%s", UserAgent);
224BOOL http_context_set_x_ms_user_agent(HttpContext* context,
const char* X_MS_UserAgent)
226 if (!context || !X_MS_UserAgent)
229 return http_context_set_header(context,
"X-MS-User-Agent",
"%s", X_MS_UserAgent);
232BOOL http_context_set_host(HttpContext* context,
const char* Host)
234 if (!context || !Host)
237 return http_context_set_header(context,
"Host",
"%s", Host);
240BOOL http_context_set_accept(HttpContext* context,
const char* Accept)
242 if (!context || !Accept)
245 return http_context_set_header(context,
"Accept",
"%s", Accept);
248BOOL http_context_set_cache_control(HttpContext* context,
const char* CacheControl)
250 if (!context || !CacheControl)
253 return http_context_set_header(context,
"Cache-Control",
"%s", CacheControl);
256BOOL http_context_set_connection(HttpContext* context,
const char* Connection)
258 if (!context || !Connection)
261 free(context->Connection);
262 context->Connection = _strdup(Connection);
264 if (!context->Connection)
270WINPR_ATTR_FORMAT_ARG(2, 0)
271static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const
char* str, va_list ap)
276 size_t PragmaSize = 0;
279 const int size = winpr_vasprintf(&Pragma, &PragmaSize, str, ap);
290 winpr_asprintf(&sstr, &slen,
"%s, %s", context->Pragma, Pragma);
297 free(context->Pragma);
298 context->Pragma = sstr;
308WINPR_ATTR_FORMAT_ARG(2, 3)
309BOOL http_context_set_pragma(HttpContext* context, WINPR_FORMAT_ARG const
char* Pragma, ...)
311 if (!context || !Pragma)
314 free(context->Pragma);
315 context->Pragma = NULL;
318 va_start(ap, Pragma);
319 return list_append(context, Pragma, ap);
322WINPR_ATTR_FORMAT_ARG(2, 3)
323BOOL http_context_append_pragma(HttpContext* context, const
char* Pragma, ...)
325 if (!context || !Pragma)
329 va_start(ap, Pragma);
330 return list_append(context, Pragma, ap);
333static char* guid2str(
const GUID* guid,
char* buffer,
size_t len)
337 RPC_CSTR strguid = NULL;
339 RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
341 if (rpcStatus != RPC_S_OK)
344 (void)sprintf_s(buffer, len,
"{%s}", strguid);
345 RpcStringFreeA(&strguid);
349BOOL http_context_set_rdg_connection_id(HttpContext* context,
const GUID* RdgConnectionId)
351 if (!context || !RdgConnectionId)
354 char buffer[64] = { 0 };
355 return http_context_set_header(context,
"RDG-Connection-Id",
"%s",
356 guid2str(RdgConnectionId, buffer,
sizeof(buffer)));
359BOOL http_context_set_rdg_correlation_id(HttpContext* context,
const GUID* RdgCorrelationId)
361 if (!context || !RdgCorrelationId)
364 char buffer[64] = { 0 };
365 return http_context_set_header(context,
"RDG-Correlation-Id",
"%s",
366 guid2str(RdgCorrelationId, buffer,
sizeof(buffer)));
369BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
377 if (RPC_S_OK != UuidCreate(&key))
380 free(context->SecWebsocketKey);
381 context->SecWebsocketKey = crypto_base64_encode((BYTE*)&key,
sizeof(key));
382 if (!context->SecWebsocketKey)
386 context->websocketUpgrade = enable;
390BOOL http_context_is_websocket_upgrade_enabled(HttpContext* context)
392 return context->websocketUpgrade;
395BOOL http_context_set_rdg_auth_scheme(HttpContext* context,
const char* RdgAuthScheme)
397 if (!context || !RdgAuthScheme)
400 return http_context_set_header(context,
"RDG-Auth-Scheme",
"%s", RdgAuthScheme);
403BOOL http_context_set_cookie(HttpContext* context,
const char* CookieName,
const char* CookieValue)
405 if (!context || !CookieName || !CookieValue)
407 if (ListDictionary_Contains(context->cookies, CookieName))
409 if (!ListDictionary_SetItemValue(context->cookies, CookieName, CookieValue))
414 if (!ListDictionary_Add(context->cookies, CookieName, CookieValue))
420void http_context_free(HttpContext* context)
424 free(context->SecWebsocketKey);
426 free(context->Method);
427 free(context->Connection);
428 free(context->Pragma);
429 HashTable_Free(context->headers);
430 ListDictionary_Free(context->cookies);
435BOOL http_request_set_method(HttpRequest* request,
const char* Method)
437 if (!request || !Method)
440 free(request->Method);
441 request->Method = _strdup(Method);
443 if (!request->Method)
449BOOL http_request_set_uri(HttpRequest* request,
const char* URI)
451 if (!request || !URI)
455 request->URI = _strdup(URI);
463BOOL http_request_set_auth_scheme(HttpRequest* request,
const char* AuthScheme)
465 if (!request || !AuthScheme)
468 free(request->AuthScheme);
469 request->AuthScheme = _strdup(AuthScheme);
471 if (!request->AuthScheme)
477BOOL http_request_set_auth_param(HttpRequest* request,
const char* AuthParam)
479 if (!request || !AuthParam)
482 free(request->AuthParam);
483 request->AuthParam = _strdup(AuthParam);
485 if (!request->AuthParam)
491BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
493 if (!request || TransferEncoding == TransferEncodingUnknown)
496 request->TransferEncoding = TransferEncoding;
501WINPR_ATTR_FORMAT_ARG(2, 3)
502static BOOL http_encode_print(
wStream* s, WINPR_FORMAT_ARG const
char* fmt, ...)
513 length = vsnprintf(NULL, 0, fmt, ap) + 1;
516 if (!Stream_EnsureRemainingCapacity(s, (
size_t)length))
519 str = (
char*)Stream_Pointer(s);
521 used = vsnprintf(str, (
size_t)length, fmt, ap);
525 if ((used + 1) != length)
528 Stream_Seek(s, (
size_t)used);
532static BOOL http_encode_body_line(
wStream* s,
const char* param,
const char* value)
534 if (!s || !param || !value)
537 return http_encode_print(s,
"%s: %s\r\n", param, value);
540static BOOL http_encode_content_length_line(
wStream* s,
size_t ContentLength)
542 return http_encode_print(s,
"Content-Length: %" PRIuz
"\r\n", ContentLength);
545static BOOL http_encode_header_line(
wStream* s,
const char* Method,
const char* URI)
547 if (!s || !Method || !URI)
550 return http_encode_print(s,
"%s %s HTTP/1.1\r\n", Method, URI);
553static BOOL http_encode_authorization_line(
wStream* s,
const char* AuthScheme,
554 const char* AuthParam)
556 if (!s || !AuthScheme || !AuthParam)
559 return http_encode_print(s,
"Authorization: %s %s\r\n", AuthScheme, AuthParam);
562static BOOL http_encode_cookie_line(
wStream* s, wListDictionary* cookies)
564 ULONG_PTR* keys = NULL;
570 ListDictionary_Lock(cookies);
571 const size_t count = ListDictionary_GetKeys(cookies, &keys);
576 status = http_encode_print(s,
"Cookie: ");
580 for (
size_t x = 0; status && x < count; x++)
582 char* cur = (
char*)ListDictionary_GetItemValue(cookies, (
void*)keys[x]);
590 status = http_encode_print(s,
"; ");
594 status = http_encode_print(s,
"%s=%s", (
char*)keys[x], cur);
597 status = http_encode_print(s,
"\r\n");
600 ListDictionary_Unlock(cookies);
604static BOOL write_headers(
const void* pkey,
void* pvalue,
void* arg)
606 const char* key = pkey;
607 const char* value = pvalue;
614 return http_encode_body_line(s, key, value);
617wStream* http_request_write(HttpContext* context, HttpRequest* request)
621 if (!context || !request)
624 s = Stream_New(NULL, 1024);
629 if (!http_encode_header_line(s, request->Method, request->URI) ||
631 !http_encode_body_line(s,
"Pragma", context->Pragma))
634 if (!context->websocketUpgrade)
636 if (!http_encode_body_line(s,
"Connection", context->Connection))
641 if (!http_encode_body_line(s,
"Connection",
"Upgrade") ||
642 !http_encode_body_line(s,
"Upgrade",
"websocket") ||
643 !http_encode_body_line(s,
"Sec-Websocket-Version",
"13") ||
644 !http_encode_body_line(s,
"Sec-Websocket-Key", context->SecWebsocketKey))
648 if (request->TransferEncoding != TransferEncodingIdentity)
650 if (request->TransferEncoding == TransferEncodingChunked)
652 if (!http_encode_body_line(s,
"Transfer-Encoding",
"chunked"))
660 if (!http_encode_content_length_line(s, request->ContentLength))
664 if (!utils_str_is_empty(request->Authorization))
666 if (!http_encode_body_line(s,
"Authorization", request->Authorization))
669 else if (!utils_str_is_empty(request->AuthScheme) && !utils_str_is_empty(request->AuthParam))
671 if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
675 if (!HashTable_Foreach(context->headers, write_headers, s))
678 if (!HashTable_Foreach(request->headers, write_headers, s))
681 if (!http_encode_cookie_line(s, context->cookies))
684 if (!http_encode_print(s,
"\r\n"))
687 Stream_SealLength(s);
690 Stream_Free(s, TRUE);
694HttpRequest* http_request_new(
void)
696 HttpRequest* request = (HttpRequest*)calloc(1,
sizeof(HttpRequest));
700 request->headers = HashTable_New_String();
701 if (!request->headers)
703 request->TransferEncoding = TransferEncodingIdentity;
706 http_request_free(request);
710void http_request_free(HttpRequest* request)
715 free(request->AuthParam);
716 free(request->AuthScheme);
717 free(request->Authorization);
718 free(request->Method);
720 HashTable_Free(request->headers);
724static BOOL http_response_parse_header_status_line(HttpResponse* response,
const char* status_line)
727 char* separator = NULL;
728 char* status_code = NULL;
734 separator = strchr(status_line,
' ');
739 status_code = separator + 1;
740 separator = strchr(status_code,
' ');
746 const char* reason_phrase = separator + 1;
750 long val = strtol(status_code, NULL, 0);
752 if ((errno != 0) || (val < 0) || (val > INT16_MAX))
755 response->StatusCode = (UINT16)val;
757 free(response->ReasonPhrase);
758 response->ReasonPhrase = _strdup(reason_phrase);
761 if (!response->ReasonPhrase)
769 WLog_ERR(TAG,
"http_response_parse_header_status_line failed [%s]", status_line);
774static BOOL http_response_parse_header_field(HttpResponse* response,
const char* name,
777 WINPR_ASSERT(response);
782 if (_stricmp(name,
"Content-Length") == 0)
784 unsigned long long val = 0;
786 val = _strtoui64(value, NULL, 0);
788 if ((errno != 0) || (val > INT32_MAX))
791 response->ContentLength = WINPR_ASSERTING_INT_CAST(
size_t, val);
795 if (_stricmp(name,
"Content-Type") == 0)
797 free(response->ContentType);
798 response->ContentType = _strdup(value);
800 return response->ContentType != NULL;
803 if (_stricmp(name,
"Transfer-Encoding") == 0)
805 if (_stricmp(value,
"identity") == 0)
806 response->TransferEncoding = TransferEncodingIdentity;
807 else if (_stricmp(value,
"chunked") == 0)
808 response->TransferEncoding = TransferEncodingChunked;
810 response->TransferEncoding = TransferEncodingUnknown;
815 if (_stricmp(name,
"Sec-WebSocket-Version") == 0)
817 free(response->SecWebsocketVersion);
818 response->SecWebsocketVersion = _strdup(value);
820 return response->SecWebsocketVersion != NULL;
823 if (_stricmp(name,
"Sec-WebSocket-Accept") == 0)
825 free(response->SecWebsocketAccept);
826 response->SecWebsocketAccept = _strdup(value);
828 return response->SecWebsocketAccept != NULL;
831 if (_stricmp(name,
"WWW-Authenticate") == 0)
833 const char* authScheme = value;
834 const char* authValue =
"";
835 char* separator = strchr(value,
' ');
846 authValue = separator + 1;
849 return HashTable_Insert(response->Authenticates, authScheme, authValue);
852 if (_stricmp(name,
"Set-Cookie") == 0)
854 char* separator = strchr(value,
'=');
864 const char* CookieName = value;
865 char* CookieValue = separator + 1;
867 if (*CookieValue ==
'"')
869 char* p = CookieValue;
870 while (*p !=
'"' && *p !=
'\0')
880 char* p = CookieValue;
881 while (*p !=
';' && *p !=
'\0' && *p !=
' ')
887 return HashTable_Insert(response->SetCookie, CookieName, CookieValue);
894static BOOL http_response_parse_header(HttpResponse* response)
900 char* colon_pos = NULL;
901 char* end_of_header = NULL;
902 char end_of_header_char = 0;
907 if (!response->lines)
910 if (!http_response_parse_header_status_line(response, response->lines[0]))
913 for (
size_t count = 1; count < response->count; count++)
915 line = response->lines[count];
927 colon_pos = strchr(line,
':');
931 if ((colon_pos == NULL) || (colon_pos == line))
935 for (end_of_header = colon_pos; end_of_header != line; end_of_header--)
937 c = end_of_header[-1];
939 if (c !=
' ' && c !=
'\t' && c !=
':')
943 if (end_of_header == line)
946 end_of_header_char = *end_of_header;
947 *end_of_header =
'\0';
951 char* value = colon_pos + 1;
952 for (; *value; value++)
954 if ((*value !=
' ') && (*value !=
'\t'))
958 const int res = http_response_parse_header_field(response, name, value);
959 *end_of_header = end_of_header_char;
968 WLog_ERR(TAG,
"parsing failed");
973static void http_response_print(wLog* log, DWORD level,
const HttpResponse* response,
974 const char* file,
size_t line,
const char* fkt)
976 char buffer[64] = { 0 };
979 WINPR_ASSERT(response);
981 if (!WLog_IsLevelActive(log, level))
984 const long status = http_response_get_status_code(response);
985 WLog_PrintTextMessage(log, level, line, file, fkt,
"HTTP status: %s",
986 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
988 if (WLog_IsLevelActive(log, WLOG_DEBUG))
990 for (
size_t i = 0; i < response->count; i++)
991 WLog_PrintTextMessage(log, WLOG_DEBUG, line, file, fkt,
"[%" PRIuz
"] %s", i,
995 if (response->ReasonPhrase)
996 WLog_PrintTextMessage(log, level, line, file, fkt,
"[reason] %s", response->ReasonPhrase);
998 if (WLog_IsLevelActive(log, WLOG_TRACE))
1000 WLog_PrintTextMessage(log, WLOG_TRACE, line, file, fkt,
"[body][%" PRIuz
"] %s",
1001 response->BodyLength, response->BodyContent);
1005static BOOL http_use_content_length(
const char* cur)
1012 if (_strnicmp(cur,
"application/rpc", 15) == 0)
1014 else if (_strnicmp(cur,
"text/plain", 10) == 0)
1016 else if (_strnicmp(cur,
"text/html", 9) == 0)
1018 else if (_strnicmp(cur,
"application/json", 16) == 0)
1023 char end = cur[pos];
1042static int print_bio_error(
const char* str,
size_t len,
void* bp)
1047 WLog_Print(log, WLOG_ERROR,
"%s", str);
1048 if (len > INT32_MAX)
1053int http_chuncked_read(BIO* bio, BYTE* pBuffer,
size_t size,
1057 int effectiveDataLen = 0;
1059 WINPR_ASSERT(pBuffer);
1060 WINPR_ASSERT(encodingContext != NULL);
1061 WINPR_ASSERT(size <= INT32_MAX);
1064 switch (encodingContext->state)
1066 case ChunkStateData:
1069 (size > encodingContext->nextOffset ? encodingContext->nextOffset : size);
1074 status = BIO_read(bio, pBuffer, (
int)rd);
1076 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1078 encodingContext->nextOffset -= WINPR_ASSERTING_INT_CAST(uint32_t, status);
1079 if (encodingContext->nextOffset == 0)
1081 encodingContext->state = ChunkStateFooter;
1082 encodingContext->headerFooterPos = 0;
1084 effectiveDataLen += status;
1086 if ((
size_t)status == size)
1087 return effectiveDataLen;
1090 size -= (size_t)status;
1093 case ChunkStateFooter:
1095 char _dummy[2] = { 0 };
1096 WINPR_ASSERT(encodingContext->nextOffset == 0);
1097 WINPR_ASSERT(encodingContext->headerFooterPos < 2);
1099 status = BIO_read(bio, _dummy, (
int)(2 - encodingContext->headerFooterPos));
1102 encodingContext->headerFooterPos += (size_t)status;
1103 if (encodingContext->headerFooterPos == 2)
1105 encodingContext->state = ChunkStateLenghHeader;
1106 encodingContext->headerFooterPos = 0;
1110 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1113 case ChunkStateLenghHeader:
1115 BOOL _haveNewLine = FALSE;
1116 char* dst = &encodingContext->lenBuffer[encodingContext->headerFooterPos];
1117 WINPR_ASSERT(encodingContext->nextOffset == 0);
1118 while (encodingContext->headerFooterPos < 10 && !_haveNewLine)
1121 status = BIO_read(bio, dst, 1);
1125 _haveNewLine = TRUE;
1126 encodingContext->headerFooterPos += (size_t)status;
1130 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1136 size_t tmp = strtoul(encodingContext->lenBuffer, NULL, 16);
1137 if ((errno != 0) || (tmp > SIZE_MAX))
1140 encodingContext->nextOffset = 0;
1141 encodingContext->state = ChunkStateEnd;
1144 encodingContext->nextOffset = tmp;
1145 encodingContext->state = ChunkStateData;
1147 if (encodingContext->nextOffset == 0)
1149 WLog_DBG(TAG,
"chunked encoding end of stream received");
1150 encodingContext->headerFooterPos = 0;
1151 encodingContext->state = ChunkStateEnd;
1152 return (effectiveDataLen > 0 ? effectiveDataLen : 0);
1163#define sleep_or_timeout(tls, startMS, timeoutMS) \
1164 sleep_or_timeout_((tls), (startMS), (timeoutMS), __FILE__, __func__, __LINE__)
1165static BOOL sleep_or_timeout_(rdpTls* tls, UINT64 startMS, UINT32 timeoutMS,
const char* file,
1166 const char* fkt,
size_t line)
1171 const UINT64 nowMS = GetTickCount64();
1172 if (nowMS - startMS > timeoutMS)
1174 DWORD level = WLOG_ERROR;
1175 wLog* log = WLog_Get(TAG);
1176 if (WLog_IsLevelActive(log, level))
1177 WLog_PrintTextMessage(log, level, line, file, fkt,
"timeout [%" PRIu32
"ms] exceeded",
1181 if (!BIO_should_retry(tls->bio))
1183 DWORD level = WLOG_ERROR;
1184 wLog* log = WLog_Get(TAG);
1185 if (WLog_IsLevelActive(log, level))
1187 WLog_PrintTextMessage(log, level, line, file, fkt,
"Retries exceeded");
1188 ERR_print_errors_cb(print_bio_error, log);
1192 if (freerdp_shall_disconnect_context(tls->context))
1198static SSIZE_T http_response_recv_line(rdpTls* tls, HttpResponse* response)
1201 WINPR_ASSERT(response);
1203 SSIZE_T payloadOffset = -1;
1204 const UINT32 timeoutMS =
1206 const UINT64 startMS = GetTickCount64();
1207 while (payloadOffset <= 0)
1209 size_t bodyLength = 0;
1210 size_t position = 0;
1217 status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);
1220 if (sleep_or_timeout(tls, startMS, timeoutMS))
1225#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
1226 VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
1228 Stream_Seek(response->data, (
size_t)status);
1230 if (!Stream_EnsureRemainingCapacity(response->data, 1024))
1233 position = Stream_GetPosition(response->data);
1237 else if (position > RESPONSE_SIZE_LIMIT)
1239 WLog_ERR(TAG,
"Request header too large! (%" PRIdz
" bytes) Aborting!", bodyLength);
1245 s = (position > 8) ? 8 : position;
1246 end = (
char*)Stream_Pointer(response->data) - s;
1248 if (string_strnstr(end,
"\r\n\r\n", s) != NULL)
1249 payloadOffset = WINPR_ASSERTING_INT_CAST(SSIZE_T, Stream_GetPosition(response->data));
1253 return payloadOffset;
1256static BOOL http_response_recv_body(rdpTls* tls, HttpResponse* response, BOOL readContentLength,
1257 size_t payloadOffset,
size_t bodyLength)
1262 WINPR_ASSERT(response);
1264 const UINT64 startMS = GetTickCount64();
1265 const UINT32 timeoutMS =
1268 if ((response->TransferEncoding == TransferEncodingChunked) && readContentLength)
1271 ctx.state = ChunkStateLenghHeader;
1273 ctx.headerFooterPos = 0;
1277 if (!Stream_EnsureRemainingCapacity(response->data, 2048))
1280 int status = http_chuncked_read(tls->bio, Stream_Pointer(response->data),
1281 Stream_GetRemainingCapacity(response->data), &ctx);
1284 if (sleep_or_timeout(tls, startMS, timeoutMS))
1289 Stream_Seek(response->data, (
size_t)status);
1292 }
while (ctx.state != ChunkStateEnd);
1293 response->BodyLength = WINPR_ASSERTING_INT_CAST(uint32_t, full_len);
1294 if (response->BodyLength > 0)
1295 response->BodyContent = &(Stream_BufferAs(response->data,
char))[payloadOffset];
1299 while (response->BodyLength < bodyLength)
1303 if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
1307 size_t diff = bodyLength - response->BodyLength;
1308 if (diff > INT32_MAX)
1310 status = BIO_read(tls->bio, Stream_Pointer(response->data), (
int)diff);
1314 if (sleep_or_timeout(tls, startMS, timeoutMS))
1319 Stream_Seek(response->data, (
size_t)status);
1320 response->BodyLength += (
unsigned long)status;
1322 if (response->BodyLength > RESPONSE_SIZE_LIMIT)
1324 WLog_ERR(TAG,
"Request body too large! (%" PRIdz
" bytes) Aborting!",
1325 response->BodyLength);
1330 if (response->BodyLength > 0)
1331 response->BodyContent = &(Stream_BufferAs(response->data,
char))[payloadOffset];
1333 if (bodyLength != response->BodyLength)
1335 WLog_WARN(TAG,
"%s unexpected body length: actual: %" PRIuz
", expected: %" PRIuz,
1336 response->ContentType, response->BodyLength, bodyLength);
1339 response->BodyLength = MIN(bodyLength, response->BodyLength);
1343 if (!Stream_EnsureRemainingCapacity(response->data,
sizeof(UINT16)))
1345 Stream_Write_UINT16(response->data, 0);
1353static void clear_lines(HttpResponse* response)
1355 WINPR_ASSERT(response);
1357 for (
size_t x = 0; x < response->count; x++)
1359 WINPR_ASSERT(response->lines);
1360 char* line = response->lines[x];
1364 free((
void*)response->lines);
1365 response->lines = NULL;
1366 response->count = 0;
1369HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
1371 size_t bodyLength = 0;
1372 HttpResponse* response = http_response_new();
1377 response->ContentLength = 0;
1379 const SSIZE_T payloadOffset = http_response_recv_line(tls, response);
1380 if (payloadOffset < 0)
1386 char* buffer = Stream_BufferAs(response->data,
char);
1387 const char* line = Stream_BufferAs(response->data,
char);
1388 char* context = NULL;
1390 while ((line = string_strnstr(line,
"\r\n",
1391 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset) -
1392 WINPR_ASSERTING_INT_CAST(
size_t, (line - buffer)) - 2UL)))
1398 clear_lines(response);
1399 response->count = count;
1403 response->lines = (
char**)calloc(response->count,
sizeof(
char*));
1405 if (!response->lines)
1409 buffer[payloadOffset - 1] =
'\0';
1410 buffer[payloadOffset - 2] =
'\0';
1412 line = strtok_s(buffer,
"\r\n", &context);
1414 while (line && (response->count > count))
1416 response->lines[count] = _strdup(line);
1417 if (!response->lines[count])
1419 line = strtok_s(NULL,
"\r\n", &context);
1423 if (!http_response_parse_header(response))
1426 response->BodyLength =
1427 Stream_GetPosition(response->data) - WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset);
1429 WINPR_ASSERT(response->BodyLength == 0);
1430 bodyLength = response->BodyLength;
1432 if (readContentLength && (response->ContentLength > 0))
1434 const char* cur = response->ContentType;
1438 if (http_use_content_length(cur))
1440 if (response->ContentLength < RESPONSE_SIZE_LIMIT)
1441 bodyLength = response->ContentLength;
1446 readContentLength = FALSE;
1448 cur = strchr(cur,
';');
1452 if (bodyLength > RESPONSE_SIZE_LIMIT)
1454 WLog_ERR(TAG,
"Expected request body too large! (%" PRIdz
" bytes) Aborting!",
1460 if (!http_response_recv_body(tls, response, readContentLength,
1461 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset), bodyLength))
1464 Stream_SealLength(response->data);
1467 if (!Stream_EnsureRemainingCapacity(response->data, 2))
1469 Stream_Write_UINT16(response->data, 0);
1473 WLog_ERR(TAG,
"No response");
1474 http_response_free(response);
1478const char* http_response_get_body(
const HttpResponse* response)
1483 return response->BodyContent;
1486wHashTable* HashTable_New_String(
void)
1488 wHashTable* table = HashTable_New(FALSE);
1492 if (!HashTable_SetupForStringData(table, TRUE))
1494 HashTable_Free(table);
1497 HashTable_KeyObject(table)->fnObjectEquals = strings_equals_nocase;
1498 HashTable_ValueObject(table)->fnObjectEquals = strings_equals_nocase;
1502HttpResponse* http_response_new(
void)
1504 HttpResponse* response = (HttpResponse*)calloc(1,
sizeof(HttpResponse));
1509 response->Authenticates = HashTable_New_String();
1511 if (!response->Authenticates)
1514 response->SetCookie = HashTable_New_String();
1516 if (!response->SetCookie)
1519 response->data = Stream_New(NULL, 2048);
1521 if (!response->data)
1524 response->TransferEncoding = TransferEncodingIdentity;
1527 WINPR_PRAGMA_DIAG_PUSH
1528 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1529 http_response_free(response);
1530 WINPR_PRAGMA_DIAG_POP
1534void http_response_free(HttpResponse* response)
1539 clear_lines(response);
1540 free(response->ReasonPhrase);
1541 free(response->ContentType);
1542 free(response->SecWebsocketAccept);
1543 free(response->SecWebsocketVersion);
1544 HashTable_Free(response->Authenticates);
1545 HashTable_Free(response->SetCookie);
1546 Stream_Free(response->data, TRUE);
1550const char* http_request_get_uri(HttpRequest* request)
1555 return request->URI;
1558SSIZE_T http_request_get_content_length(HttpRequest* request)
1563 return (SSIZE_T)request->ContentLength;
1566BOOL http_request_set_content_length(HttpRequest* request,
size_t length)
1571 request->ContentLength = length;
1575UINT16 http_response_get_status_code(
const HttpResponse* response)
1577 WINPR_ASSERT(response);
1579 return response->StatusCode;
1582size_t http_response_get_body_length(
const HttpResponse* response)
1584 WINPR_ASSERT(response);
1586 return response->BodyLength;
1589const char* http_response_get_auth_token(
const HttpResponse* response,
const char* method)
1591 if (!response || !method)
1594 return HashTable_GetItemValue(response->Authenticates, method);
1597const char* http_response_get_setcookie(
const HttpResponse* response,
const char* cookie)
1599 if (!response || !cookie)
1602 return HashTable_GetItemValue(response->SetCookie, cookie);
1605TRANSFER_ENCODING http_response_get_transfer_encoding(
const HttpResponse* response)
1608 return TransferEncodingUnknown;
1610 return response->TransferEncoding;
1613BOOL http_response_is_websocket(
const HttpContext* http,
const HttpResponse* response)
1615 BOOL isWebsocket = FALSE;
1616 WINPR_DIGEST_CTX* sha1 = NULL;
1617 char* base64accept = NULL;
1618 BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH];
1620 if (!http || !response)
1623 if (!http->websocketUpgrade || response->StatusCode != HTTP_STATUS_SWITCH_PROTOCOLS)
1626 if (response->SecWebsocketVersion && _stricmp(response->SecWebsocketVersion,
"13") != 0)
1629 if (!response->SecWebsocketAccept)
1634 sha1 = winpr_Digest_New();
1638 if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
1641 if (!winpr_Digest_Update(sha1, (BYTE*)http->SecWebsocketKey, strlen(http->SecWebsocketKey)))
1643 if (!winpr_Digest_Update(sha1, (
const BYTE*)WEBSOCKET_MAGIC_GUID, strlen(WEBSOCKET_MAGIC_GUID)))
1646 if (!winpr_Digest_Final(sha1, sha1_digest,
sizeof(sha1_digest)))
1649 base64accept = crypto_base64_encode(sha1_digest, WINPR_SHA1_DIGEST_LENGTH);
1653 if (_stricmp(response->SecWebsocketAccept, base64accept) != 0)
1655 WLog_WARN(TAG,
"Webserver gave Websocket Upgrade response but sanity check failed");
1660 winpr_Digest_Free(sha1);
1665void http_response_log_error_status_(wLog* log, DWORD level,
const HttpResponse* response,
1666 const char* file,
size_t line,
const char* fkt)
1669 WINPR_ASSERT(response);
1671 if (!WLog_IsLevelActive(log, level))
1674 char buffer[64] = { 0 };
1675 const UINT16 status = http_response_get_status_code(response);
1676 WLog_PrintTextMessage(log, level, line, file, fkt,
"Unexpected HTTP status: %s",
1677 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1678 http_response_print(log, level, response, file, line, fkt);
1681static BOOL extract_cookie(
const void* pkey,
void* pvalue,
void* arg)
1683 const char* key = pkey;
1684 const char* value = pvalue;
1685 HttpContext* context = arg;
1689 WINPR_ASSERT(value);
1691 return http_context_set_cookie(context, key, value);
1694BOOL http_response_extract_cookies(
const HttpResponse* response, HttpContext* context)
1696 WINPR_ASSERT(response);
1697 WINPR_ASSERT(context);
1699 return HashTable_Foreach(response->SetCookie, extract_cookie, context);
1702FREERDP_LOCAL BOOL http_context_set_header(HttpContext* context,
const char* key,
const char* value,
1705 WINPR_ASSERT(context);
1707 va_start(ap, value);
1708 const BOOL rc = http_context_set_header_va(context, key, value, ap);
1713BOOL http_request_set_header(HttpRequest* request,
const char* key,
const char* value, ...)
1715 WINPR_ASSERT(request);
1719 va_start(ap, value);
1720 winpr_vasprintf(&v, &vlen, value, ap);
1722 const BOOL rc = HashTable_Insert(request->headers, key, v);
1727BOOL http_context_set_header_va(HttpContext* context,
const char* key,
const char* value,
1732 winpr_vasprintf(&v, &vlen, value, ap);
1733 const BOOL rc = HashTable_Insert(context->headers, key, v);
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
This struct contains function pointer to initialize/free objects.