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 BOOL strings_equals_nocase(
const void* obj1,
const void* obj2)
105 return _stricmp(obj1, obj2) == 0;
108HttpContext* http_context_new(
void)
110 HttpContext* context = (HttpContext*)calloc(1,
sizeof(HttpContext));
114 context->headers = HashTable_New_String();
115 if (!context->headers)
118 context->cookies = ListDictionary_New(FALSE);
119 if (!context->cookies)
123 wObject* key = ListDictionary_KeyObject(context->cookies);
124 wObject* value = ListDictionary_ValueObject(context->cookies);
138 WINPR_PRAGMA_DIAG_PUSH
139 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
140 http_context_free(context);
141 WINPR_PRAGMA_DIAG_POP
145BOOL http_context_set_method(HttpContext* context,
const char* Method)
147 if (!context || !Method)
150 free(context->Method);
151 context->Method = _strdup(Method);
153 return (context->Method !=
nullptr);
156BOOL http_request_set_content_type(HttpRequest* request,
const char* ContentType)
158 if (!request || !ContentType)
161 return http_request_set_header(request,
"Content-Type",
"%s", ContentType);
164const char* http_context_get_uri(HttpContext* context)
172BOOL http_context_set_uri(HttpContext* context,
const char* URI)
174 if (!context || !URI)
178 context->URI = _strdup(URI);
180 return (context->URI !=
nullptr);
183BOOL http_context_set_user_agent(HttpContext* context,
const char* UserAgent)
185 if (!context || !UserAgent)
188 return http_context_set_header(context,
"User-Agent",
"%s", UserAgent);
191BOOL http_context_set_x_ms_user_agent(HttpContext* context,
const char* X_MS_UserAgent)
193 if (!context || !X_MS_UserAgent)
196 return http_context_set_header(context,
"X-MS-User-Agent",
"%s", X_MS_UserAgent);
199BOOL http_context_set_host(HttpContext* context,
const char* Host)
201 if (!context || !Host)
204 return http_context_set_header(context,
"Host",
"%s", Host);
207BOOL http_context_set_accept(HttpContext* context,
const char* Accept)
209 if (!context || !Accept)
212 return http_context_set_header(context,
"Accept",
"%s", Accept);
215BOOL http_context_set_cache_control(HttpContext* context,
const char* CacheControl)
217 if (!context || !CacheControl)
220 return http_context_set_header(context,
"Cache-Control",
"%s", CacheControl);
223BOOL http_context_set_connection(HttpContext* context,
const char* Connection)
225 if (!context || !Connection)
228 free(context->Connection);
229 context->Connection = _strdup(Connection);
231 return (context->Connection !=
nullptr);
234WINPR_ATTR_FORMAT_ARG(2, 0)
235static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const
char* str, va_list ap)
238 va_list vat = WINPR_C_ARRAY_INIT;
239 char* Pragma =
nullptr;
240 size_t PragmaSize = 0;
243 const int size = winpr_vasprintf(&Pragma, &PragmaSize, str, ap);
250 char* sstr =
nullptr;
254 winpr_asprintf(&sstr, &slen,
"%s, %s", context->Pragma, Pragma);
261 free(context->Pragma);
262 context->Pragma = sstr;
272WINPR_ATTR_FORMAT_ARG(2, 3)
273BOOL http_context_set_pragma(HttpContext* context, WINPR_FORMAT_ARG const
char* Pragma, ...)
275 if (!context || !Pragma)
278 free(context->Pragma);
279 context->Pragma =
nullptr;
281 va_list ap = WINPR_C_ARRAY_INIT;
282 va_start(ap, Pragma);
283 return list_append(context, Pragma, ap);
286WINPR_ATTR_FORMAT_ARG(2, 3)
287BOOL http_context_append_pragma(HttpContext* context, const
char* Pragma, ...)
289 if (!context || !Pragma)
292 va_list ap = WINPR_C_ARRAY_INIT;
293 va_start(ap, Pragma);
294 return list_append(context, Pragma, ap);
297BOOL http_context_set_rdg_connection_id(HttpContext* context)
302 GUID RdgConnectionId;
303 if (UuidCreate(&RdgConnectionId) != RPC_S_OK)
306 char buffer[64] = WINPR_C_ARRAY_INIT;
307 return http_context_set_header(context,
"RDG-Connection-Id",
"{%s}",
308 guid2str(&RdgConnectionId, buffer,
sizeof(buffer)));
311BOOL http_context_set_rdg_correlation_id(HttpContext* context,
const GUID* RdgCorrelationId)
313 if (!context || !RdgCorrelationId)
316 char buffer[64] = WINPR_C_ARRAY_INIT;
317 return http_context_set_header(context,
"RDG-Correlation-Id",
"{%s}",
318 guid2str(RdgCorrelationId, buffer,
sizeof(buffer)));
321BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
323 WINPR_ASSERT(context);
327 GUID key = WINPR_C_ARRAY_INIT;
328 if (RPC_S_OK != UuidCreate(&key))
331 free(context->SecWebsocketKey);
332 context->SecWebsocketKey = crypto_base64_encode((BYTE*)&key,
sizeof(key));
333 if (!context->SecWebsocketKey)
337 context->websocketUpgrade = enable;
341BOOL http_context_is_websocket_upgrade_enabled(HttpContext* context)
343 return context->websocketUpgrade;
346BOOL http_context_set_rdg_auth_scheme(HttpContext* context,
const char* RdgAuthScheme)
348 if (!context || !RdgAuthScheme)
351 return http_context_set_header(context,
"RDG-Auth-Scheme",
"%s", RdgAuthScheme);
354BOOL http_context_set_cookie(HttpContext* context,
const char* CookieName,
const char* CookieValue)
356 if (!context || !CookieName || !CookieValue)
358 if (ListDictionary_Contains(context->cookies, CookieName))
360 if (!ListDictionary_SetItemValue(context->cookies, CookieName, CookieValue))
365 if (!ListDictionary_Add(context->cookies, CookieName, CookieValue))
371void http_context_free(HttpContext* context)
375 free(context->SecWebsocketKey);
377 free(context->Method);
378 free(context->Connection);
379 free(context->Pragma);
380 HashTable_Free(context->headers);
381 ListDictionary_Free(context->cookies);
386BOOL http_request_set_method(HttpRequest* request,
const char* Method)
388 if (!request || !Method)
391 free(request->Method);
392 request->Method = _strdup(Method);
394 return (request->Method !=
nullptr);
397BOOL http_request_set_uri(HttpRequest* request,
const char* URI)
399 if (!request || !URI)
403 request->URI = _strdup(URI);
405 return (request->URI !=
nullptr);
408BOOL http_request_set_auth_scheme(HttpRequest* request,
const char* AuthScheme)
410 if (!request || !AuthScheme)
413 free(request->AuthScheme);
414 request->AuthScheme = _strdup(AuthScheme);
416 return (request->AuthScheme !=
nullptr);
419BOOL http_request_set_auth_param(HttpRequest* request,
const char* AuthParam)
421 if (!request || !AuthParam)
424 free(request->AuthParam);
425 request->AuthParam = _strdup(AuthParam);
427 return (request->AuthParam !=
nullptr);
430BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
432 if (!request || TransferEncoding == TransferEncodingUnknown)
435 request->TransferEncoding = TransferEncoding;
440WINPR_ATTR_FORMAT_ARG(2, 3)
441static BOOL http_encode_print(
wStream* s, WINPR_FORMAT_ARG const
char* fmt, ...)
444 va_list ap = WINPR_C_ARRAY_INIT;
452 length = vsnprintf(
nullptr, 0, fmt, ap) + 1;
455 if (!Stream_EnsureRemainingCapacity(s, (
size_t)length))
458 str = (
char*)Stream_Pointer(s);
460 used = vsnprintf(str, (
size_t)length, fmt, ap);
464 if ((used + 1) != length)
467 Stream_Seek(s, (
size_t)used);
471static BOOL http_encode_body_line(
wStream* s,
const char* param,
const char* value)
473 if (!s || !param || !value)
476 return http_encode_print(s,
"%s: %s\r\n", param, value);
479static BOOL http_encode_content_length_line(
wStream* s,
size_t ContentLength)
481 return http_encode_print(s,
"Content-Length: %" PRIuz
"\r\n", ContentLength);
484static BOOL http_encode_header_line(
wStream* s,
const char* Method,
const char* URI)
486 if (!s || !Method || !URI)
489 return http_encode_print(s,
"%s %s HTTP/1.1\r\n", Method, URI);
492static BOOL http_encode_authorization_line(
wStream* s,
const char* AuthScheme,
493 const char* AuthParam)
495 if (!s || !AuthScheme || !AuthParam)
498 return http_encode_print(s,
"Authorization: %s %s\r\n", AuthScheme, AuthParam);
501static BOOL http_encode_cookie_line(
wStream* s, wListDictionary* cookies)
503 ULONG_PTR* keys =
nullptr;
509 ListDictionary_Lock(cookies);
510 const size_t count = ListDictionary_GetKeys(cookies, &keys);
515 status = http_encode_print(s,
"Cookie: ");
519 for (
size_t x = 0; status && x < count; x++)
521 char* cur = (
char*)ListDictionary_GetItemValue(cookies, (
void*)keys[x]);
529 status = http_encode_print(s,
"; ");
533 status = http_encode_print(s,
"%s=%s", (
char*)keys[x], cur);
536 status = http_encode_print(s,
"\r\n");
539 ListDictionary_Unlock(cookies);
543static BOOL write_headers(
const void* pkey,
void* pvalue,
void* arg)
545 const char* key = pkey;
546 const char* value = pvalue;
553 return http_encode_body_line(s, key, value);
556wStream* http_request_write(HttpContext* context, HttpRequest* request)
558 if (!context || !request)
561 wStream* s = Stream_New(
nullptr, 1024);
566 if (!http_encode_header_line(s, request->Method, request->URI) ||
568 !http_encode_body_line(s,
"Pragma", context->Pragma))
571 if (!context->websocketUpgrade)
573 if (!http_encode_body_line(s,
"Connection", context->Connection))
578 if (!http_encode_body_line(s,
"Connection",
"Upgrade") ||
579 !http_encode_body_line(s,
"Upgrade",
"websocket") ||
580 !http_encode_body_line(s,
"Sec-Websocket-Version",
"13") ||
581 !http_encode_body_line(s,
"Sec-Websocket-Key", context->SecWebsocketKey))
585 if (request->TransferEncoding != TransferEncodingIdentity)
587 if (request->TransferEncoding == TransferEncodingChunked)
589 if (!http_encode_body_line(s,
"Transfer-Encoding",
"chunked"))
597 if (!http_encode_content_length_line(s, request->ContentLength))
601 if (!utils_str_is_empty(request->Authorization))
603 if (!http_encode_body_line(s,
"Authorization", request->Authorization))
606 else if (!utils_str_is_empty(request->AuthScheme) && !utils_str_is_empty(request->AuthParam))
608 if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
612 if (!HashTable_Foreach(context->headers, write_headers, s))
615 if (!HashTable_Foreach(request->headers, write_headers, s))
618 if (!http_encode_cookie_line(s, context->cookies))
621 if (!http_encode_print(s,
"\r\n"))
624 Stream_SealLength(s);
627 Stream_Free(s, TRUE);
631HttpRequest* http_request_new(
void)
633 HttpRequest* request = (HttpRequest*)calloc(1,
sizeof(HttpRequest));
637 request->headers = HashTable_New_String();
638 if (!request->headers)
640 request->TransferEncoding = TransferEncodingIdentity;
643 http_request_free(request);
647void http_request_free(HttpRequest* request)
652 free(request->AuthParam);
653 free(request->AuthScheme);
654 free(request->Authorization);
655 free(request->Method);
657 HashTable_Free(request->headers);
661static BOOL http_response_parse_header_status_line(HttpResponse* response,
const char* status_line)
664 char* separator =
nullptr;
665 char* status_code =
nullptr;
671 separator = strchr(status_line,
' ');
676 status_code = separator + 1;
677 separator = strchr(status_code,
' ');
683 const char* reason_phrase = separator + 1;
687 long val = strtol(status_code,
nullptr, 0);
689 if ((errno != 0) || (val < 0) || (val > INT16_MAX))
692 response->StatusCode = (UINT16)val;
694 free(response->ReasonPhrase);
695 response->ReasonPhrase = _strdup(reason_phrase);
698 if (!response->ReasonPhrase)
706 WLog_ERR(TAG,
"http_response_parse_header_status_line failed [%s]", status_line);
711static BOOL http_response_parse_header_field(HttpResponse* response,
const char* name,
714 WINPR_ASSERT(response);
719 if (_stricmp(name,
"Content-Length") == 0)
721 unsigned long long val = 0;
723 val = _strtoui64(value,
nullptr, 0);
725 if ((errno != 0) || (val > INT32_MAX))
728 response->ContentLength = WINPR_ASSERTING_INT_CAST(
size_t, val);
732 if (_stricmp(name,
"Content-Type") == 0)
734 free(response->ContentType);
735 response->ContentType = _strdup(value);
737 return response->ContentType !=
nullptr;
740 if (_stricmp(name,
"Transfer-Encoding") == 0)
742 if (_stricmp(value,
"identity") == 0)
743 response->TransferEncoding = TransferEncodingIdentity;
744 else if (_stricmp(value,
"chunked") == 0)
745 response->TransferEncoding = TransferEncodingChunked;
747 response->TransferEncoding = TransferEncodingUnknown;
752 if (_stricmp(name,
"Sec-WebSocket-Version") == 0)
754 free(response->SecWebsocketVersion);
755 response->SecWebsocketVersion = _strdup(value);
757 return response->SecWebsocketVersion !=
nullptr;
760 if (_stricmp(name,
"Sec-WebSocket-Accept") == 0)
762 free(response->SecWebsocketAccept);
763 response->SecWebsocketAccept = _strdup(value);
765 return response->SecWebsocketAccept !=
nullptr;
768 if (_stricmp(name,
"WWW-Authenticate") == 0)
770 const char* authScheme = value;
771 const char* authValue =
"";
772 char* separator = strchr(value,
' ');
783 authValue = separator + 1;
786 return HashTable_Insert(response->Authenticates, authScheme, authValue);
789 if (_stricmp(name,
"Set-Cookie") == 0)
791 char* separator = strchr(value,
'=');
801 const char* CookieName = value;
802 char* CookieValue = separator + 1;
804 if (*CookieValue ==
'"')
806 char* p = CookieValue;
807 while (*p !=
'"' && *p !=
'\0')
817 char* p = CookieValue;
818 while (*p !=
';' && *p !=
'\0' && *p !=
' ')
824 return HashTable_Insert(response->SetCookie, CookieName, CookieValue);
831static BOOL http_response_parse_header(HttpResponse* response)
835 char* line =
nullptr;
836 char* name =
nullptr;
837 char* colon_pos =
nullptr;
838 char* end_of_header =
nullptr;
839 char end_of_header_char = 0;
844 if (!response->lines)
847 if (!http_response_parse_header_status_line(response, response->lines[0]))
850 for (
size_t count = 1; count < response->count; count++)
852 line = response->lines[count];
864 colon_pos = strchr(line,
':');
868 if ((colon_pos ==
nullptr) || (colon_pos == line))
872 for (end_of_header = colon_pos; end_of_header != line; end_of_header--)
874 c = end_of_header[-1];
876 if (c !=
' ' && c !=
'\t' && c !=
':')
880 if (end_of_header == line)
883 end_of_header_char = *end_of_header;
884 *end_of_header =
'\0';
888 char* value = colon_pos + 1;
889 for (; *value; value++)
891 if ((*value !=
' ') && (*value !=
'\t'))
895 const int res = http_response_parse_header_field(response, name, value);
896 *end_of_header = end_of_header_char;
905 WLog_ERR(TAG,
"parsing failed");
910static void http_response_print(wLog* log, DWORD level,
const HttpResponse* response,
911 const char* file,
size_t line,
const char* fkt)
913 char buffer[64] = WINPR_C_ARRAY_INIT;
916 WINPR_ASSERT(response);
918 if (!WLog_IsLevelActive(log, level))
921 const long status = http_response_get_status_code(response);
922 WLog_PrintTextMessage(log, level, line, file, fkt,
"HTTP status: %s",
923 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
925 if (WLog_IsLevelActive(log, WLOG_DEBUG))
927 for (
size_t i = 0; i < response->count; i++)
928 WLog_PrintTextMessage(log, WLOG_DEBUG, line, file, fkt,
"[%" PRIuz
"] %s", i,
932 if (response->ReasonPhrase)
933 WLog_PrintTextMessage(log, level, line, file, fkt,
"[reason] %s", response->ReasonPhrase);
935 if (WLog_IsLevelActive(log, WLOG_TRACE))
937 WLog_PrintTextMessage(log, WLOG_TRACE, line, file, fkt,
"[body][%" PRIuz
"] %s",
938 response->BodyLength, response->BodyContent);
942static BOOL http_use_content_length(
const char* cur)
949 if (_strnicmp(cur,
"application/rpc", 15) == 0)
951 else if (_strnicmp(cur,
"text/plain", 10) == 0)
953 else if (_strnicmp(cur,
"text/html", 9) == 0)
955 else if (_strnicmp(cur,
"application/json", 16) == 0)
979static int print_bio_error(
const char* str,
size_t len,
void* bp)
984 WLog_Print(log, WLOG_ERROR,
"%s", str);
990int http_chuncked_read(BIO* bio, BYTE* pBuffer,
size_t size,
994 int effectiveDataLen = 0;
996 WINPR_ASSERT(pBuffer);
997 WINPR_ASSERT(encodingContext !=
nullptr);
998 WINPR_ASSERT(size <= INT32_MAX);
1001 switch (encodingContext->state)
1003 case ChunkStateData:
1006 (size > encodingContext->nextOffset ? encodingContext->nextOffset : size);
1011 status = BIO_read(bio, pBuffer, (
int)rd);
1013 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1015 encodingContext->nextOffset -= WINPR_ASSERTING_INT_CAST(uint32_t, status);
1016 if (encodingContext->nextOffset == 0)
1018 encodingContext->state = ChunkStateFooter;
1019 encodingContext->headerFooterPos = 0;
1021 effectiveDataLen += status;
1023 if ((
size_t)status == size)
1024 return effectiveDataLen;
1027 size -= (size_t)status;
1030 case ChunkStateFooter:
1032 char _dummy[2] = WINPR_C_ARRAY_INIT;
1033 WINPR_ASSERT(encodingContext->nextOffset == 0);
1034 WINPR_ASSERT(encodingContext->headerFooterPos < 2);
1036 status = BIO_read(bio, _dummy, (
int)(2 - encodingContext->headerFooterPos));
1039 encodingContext->headerFooterPos += (size_t)status;
1040 if (encodingContext->headerFooterPos == 2)
1042 encodingContext->state = ChunkStateLenghHeader;
1043 encodingContext->headerFooterPos = 0;
1047 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1050 case ChunkStateLenghHeader:
1052 BOOL _haveNewLine = FALSE;
1053 char* dst = &encodingContext->lenBuffer[encodingContext->headerFooterPos];
1054 WINPR_ASSERT(encodingContext->nextOffset == 0);
1055 while (encodingContext->headerFooterPos < 10 && !_haveNewLine)
1058 status = BIO_read(bio, dst, 1);
1062 _haveNewLine = TRUE;
1063 encodingContext->headerFooterPos += (size_t)status;
1067 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1073 size_t tmp = strtoul(encodingContext->lenBuffer,
nullptr, 16);
1074 if ((errno != 0) || (tmp > SIZE_MAX))
1077 encodingContext->nextOffset = 0;
1078 encodingContext->state = ChunkStateEnd;
1081 encodingContext->nextOffset = tmp;
1082 encodingContext->state = ChunkStateData;
1084 if (encodingContext->nextOffset == 0)
1086 WLog_DBG(TAG,
"chunked encoding end of stream received");
1087 encodingContext->headerFooterPos = 0;
1088 encodingContext->state = ChunkStateEnd;
1089 return (effectiveDataLen > 0 ? effectiveDataLen : 0);
1100#define sleep_or_timeout(tls, startMS, timeoutMS) \
1101 sleep_or_timeout_((tls), (startMS), (timeoutMS), __FILE__, __func__, __LINE__)
1102static BOOL sleep_or_timeout_(rdpTls* tls, UINT64 startMS, UINT32 timeoutMS,
const char* file,
1103 const char* fkt,
size_t line)
1108 const UINT64 nowMS = GetTickCount64();
1109 if (nowMS - startMS > timeoutMS)
1111 DWORD level = WLOG_ERROR;
1112 wLog* log = WLog_Get(TAG);
1113 if (WLog_IsLevelActive(log, level))
1114 WLog_PrintTextMessage(log, level, line, file, fkt,
"timeout [%" PRIu32
"ms] exceeded",
1118 if (!BIO_should_retry(tls->bio))
1120 DWORD level = WLOG_ERROR;
1121 wLog* log = WLog_Get(TAG);
1122 if (WLog_IsLevelActive(log, level))
1124 WLog_PrintTextMessage(log, level, line, file, fkt,
"Retries exceeded");
1125 ERR_print_errors_cb(print_bio_error, log);
1129 if (freerdp_shall_disconnect_context(tls->context))
1135static SSIZE_T http_response_recv_line(rdpTls* tls, HttpResponse* response)
1138 WINPR_ASSERT(response);
1140 SSIZE_T payloadOffset = -1;
1141 const UINT32 timeoutMS =
1143 const UINT64 startMS = GetTickCount64();
1144 while (payloadOffset <= 0)
1146 size_t bodyLength = 0;
1147 size_t position = 0;
1150 char* end =
nullptr;
1154 status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);
1157 if (sleep_or_timeout(tls, startMS, timeoutMS))
1162#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
1163 VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
1165 Stream_Seek(response->data, (
size_t)status);
1167 if (!Stream_EnsureRemainingCapacity(response->data, 1024))
1170 position = Stream_GetPosition(response->data);
1174 else if (position > RESPONSE_SIZE_LIMIT)
1176 WLog_ERR(TAG,
"Request header too large! (%" PRIuz
" bytes) Aborting!", bodyLength);
1182 s = (position > 8) ? 8 : position;
1183 end = (
char*)Stream_Pointer(response->data) - s;
1185 if (winpr_strnstr(end,
"\r\n\r\n", s) !=
nullptr)
1186 payloadOffset = WINPR_ASSERTING_INT_CAST(SSIZE_T, Stream_GetPosition(response->data));
1190 return payloadOffset;
1193static BOOL http_response_recv_body(rdpTls* tls, HttpResponse* response, BOOL readContentLength,
1194 size_t payloadOffset,
size_t bodyLength)
1199 WINPR_ASSERT(response);
1201 const UINT64 startMS = GetTickCount64();
1202 const UINT32 timeoutMS =
1205 if ((response->TransferEncoding == TransferEncodingChunked) && readContentLength)
1208 ctx.state = ChunkStateLenghHeader;
1210 ctx.headerFooterPos = 0;
1214 if (!Stream_EnsureRemainingCapacity(response->data, 2048))
1217 int status = http_chuncked_read(tls->bio, Stream_Pointer(response->data),
1218 Stream_GetRemainingCapacity(response->data), &ctx);
1221 if (sleep_or_timeout(tls, startMS, timeoutMS))
1226 Stream_Seek(response->data, (
size_t)status);
1229 }
while (ctx.state != ChunkStateEnd);
1230 response->BodyLength = WINPR_ASSERTING_INT_CAST(uint32_t, full_len);
1231 if (response->BodyLength > 0)
1232 response->BodyContent = &(Stream_BufferAs(response->data,
char))[payloadOffset];
1236 while (response->BodyLength < bodyLength)
1240 if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
1244 size_t diff = bodyLength - response->BodyLength;
1245 if (diff > INT32_MAX)
1247 status = BIO_read(tls->bio, Stream_Pointer(response->data), (
int)diff);
1251 if (sleep_or_timeout(tls, startMS, timeoutMS))
1256 Stream_Seek(response->data, (
size_t)status);
1257 response->BodyLength += (
unsigned long)status;
1259 if (response->BodyLength > RESPONSE_SIZE_LIMIT)
1261 WLog_ERR(TAG,
"Request body too large! (%" PRIuz
" bytes) Aborting!",
1262 response->BodyLength);
1267 if (response->BodyLength > 0)
1268 response->BodyContent = &(Stream_BufferAs(response->data,
char))[payloadOffset];
1270 if (bodyLength != response->BodyLength)
1272 WLog_WARN(TAG,
"%s unexpected body length: actual: %" PRIuz
", expected: %" PRIuz,
1273 response->ContentType, response->BodyLength, bodyLength);
1276 response->BodyLength = MIN(bodyLength, response->BodyLength);
1280 if (!Stream_EnsureRemainingCapacity(response->data,
sizeof(UINT16)))
1282 Stream_Write_UINT16(response->data, 0);
1290static void clear_lines(HttpResponse* response)
1292 WINPR_ASSERT(response);
1294 for (
size_t x = 0; x < response->count; x++)
1296 WINPR_ASSERT(response->lines);
1297 char* line = response->lines[x];
1301 free((
void*)response->lines);
1302 response->lines =
nullptr;
1303 response->count = 0;
1306HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
1308 size_t bodyLength = 0;
1309 HttpResponse* response = http_response_new();
1314 response->ContentLength = 0;
1316 const SSIZE_T payloadOffset = http_response_recv_line(tls, response);
1317 if (payloadOffset < 0)
1323 char* buffer = Stream_BufferAs(response->data,
char);
1324 char* line = Stream_BufferAs(response->data,
char);
1325 char* context =
nullptr;
1327 while ((line = winpr_strnstr(line,
"\r\n",
1328 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset) -
1329 WINPR_ASSERTING_INT_CAST(
size_t, (line - buffer)) - 2UL)))
1335 clear_lines(response);
1336 response->count = count;
1340 response->lines = (
char**)calloc(response->count,
sizeof(
char*));
1342 if (!response->lines)
1346 buffer[payloadOffset - 1] =
'\0';
1347 buffer[payloadOffset - 2] =
'\0';
1349 line = strtok_s(buffer,
"\r\n", &context);
1351 while (line && (response->count > count))
1353 response->lines[count] = _strdup(line);
1354 if (!response->lines[count])
1356 line = strtok_s(
nullptr,
"\r\n", &context);
1360 if (!http_response_parse_header(response))
1363 response->BodyLength =
1364 Stream_GetPosition(response->data) - WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset);
1366 WINPR_ASSERT(response->BodyLength == 0);
1367 bodyLength = response->BodyLength;
1369 if (readContentLength && (response->ContentLength > 0))
1371 const char* cur = response->ContentType;
1373 while (cur !=
nullptr)
1375 if (http_use_content_length(cur))
1377 if (response->ContentLength < RESPONSE_SIZE_LIMIT)
1378 bodyLength = response->ContentLength;
1383 readContentLength = FALSE;
1385 cur = strchr(cur,
';');
1389 if (bodyLength > RESPONSE_SIZE_LIMIT)
1391 WLog_ERR(TAG,
"Expected request body too large! (%" PRIuz
" bytes) Aborting!",
1397 if (!http_response_recv_body(tls, response, readContentLength,
1398 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset), bodyLength))
1401 Stream_SealLength(response->data);
1404 if (!Stream_EnsureRemainingCapacity(response->data, 2))
1406 Stream_Write_UINT16(response->data, 0);
1410 WLog_ERR(TAG,
"No response");
1411 http_response_free(response);
1415const char* http_response_get_body(
const HttpResponse* response)
1420 return response->BodyContent;
1423wHashTable* HashTable_New_String(
void)
1425 wHashTable* table = HashTable_New(FALSE);
1429 if (!HashTable_SetupForStringData(table, TRUE))
1431 HashTable_Free(table);
1434 HashTable_KeyObject(table)->
fnObjectEquals = strings_equals_nocase;
1435 HashTable_ValueObject(table)->
fnObjectEquals = strings_equals_nocase;
1439HttpResponse* http_response_new(
void)
1441 HttpResponse* response = (HttpResponse*)calloc(1,
sizeof(HttpResponse));
1446 response->Authenticates = HashTable_New_String();
1448 if (!response->Authenticates)
1451 response->SetCookie = HashTable_New_String();
1453 if (!response->SetCookie)
1456 response->data = Stream_New(
nullptr, 2048);
1458 if (!response->data)
1461 response->TransferEncoding = TransferEncodingIdentity;
1464 WINPR_PRAGMA_DIAG_PUSH
1465 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1466 http_response_free(response);
1467 WINPR_PRAGMA_DIAG_POP
1471void http_response_free(HttpResponse* response)
1476 clear_lines(response);
1477 free(response->ReasonPhrase);
1478 free(response->ContentType);
1479 free(response->SecWebsocketAccept);
1480 free(response->SecWebsocketVersion);
1481 HashTable_Free(response->Authenticates);
1482 HashTable_Free(response->SetCookie);
1483 Stream_Free(response->data, TRUE);
1487const char* http_request_get_uri(HttpRequest* request)
1492 return request->URI;
1495SSIZE_T http_request_get_content_length(HttpRequest* request)
1500 return (SSIZE_T)request->ContentLength;
1503BOOL http_request_set_content_length(HttpRequest* request,
size_t length)
1508 request->ContentLength = length;
1512UINT16 http_response_get_status_code(
const HttpResponse* response)
1514 WINPR_ASSERT(response);
1516 return response->StatusCode;
1519size_t http_response_get_body_length(
const HttpResponse* response)
1521 WINPR_ASSERT(response);
1523 return response->BodyLength;
1526const char* http_response_get_auth_token(
const HttpResponse* response,
const char* method)
1528 if (!response || !method)
1531 return HashTable_GetItemValue(response->Authenticates, method);
1534const char* http_response_get_setcookie(
const HttpResponse* response,
const char* cookie)
1536 if (!response || !cookie)
1539 return HashTable_GetItemValue(response->SetCookie, cookie);
1542TRANSFER_ENCODING http_response_get_transfer_encoding(
const HttpResponse* response)
1545 return TransferEncodingUnknown;
1547 return response->TransferEncoding;
1550BOOL http_response_is_websocket(
const HttpContext* http,
const HttpResponse* response)
1552 BOOL isWebsocket = FALSE;
1553 WINPR_DIGEST_CTX* sha1 =
nullptr;
1554 char* base64accept =
nullptr;
1555 BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH];
1557 if (!http || !response)
1560 if (!http->websocketUpgrade || response->StatusCode != HTTP_STATUS_SWITCH_PROTOCOLS)
1563 if (response->SecWebsocketVersion && _stricmp(response->SecWebsocketVersion,
"13") != 0)
1566 if (!response->SecWebsocketAccept)
1571 sha1 = winpr_Digest_New();
1575 if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
1578 if (!winpr_Digest_Update(sha1, (BYTE*)http->SecWebsocketKey, strlen(http->SecWebsocketKey)))
1580 if (!winpr_Digest_Update(sha1, (
const BYTE*)WEBSOCKET_MAGIC_GUID, strlen(WEBSOCKET_MAGIC_GUID)))
1583 if (!winpr_Digest_Final(sha1, sha1_digest,
sizeof(sha1_digest)))
1586 base64accept = crypto_base64_encode(sha1_digest, WINPR_SHA1_DIGEST_LENGTH);
1590 if (_stricmp(response->SecWebsocketAccept, base64accept) != 0)
1592 WLog_WARN(TAG,
"Webserver gave Websocket Upgrade response but sanity check failed");
1597 winpr_Digest_Free(sha1);
1602void http_response_log_error_status_(wLog* log, DWORD level,
const HttpResponse* response,
1603 const char* file,
size_t line,
const char* fkt)
1606 WINPR_ASSERT(response);
1608 if (!WLog_IsLevelActive(log, level))
1611 char buffer[64] = WINPR_C_ARRAY_INIT;
1612 const UINT16 status = http_response_get_status_code(response);
1613 WLog_PrintTextMessage(log, level, line, file, fkt,
"Unexpected HTTP status: %s",
1614 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1615 http_response_print(log, level, response, file, line, fkt);
1618static BOOL extract_cookie(
const void* pkey,
void* pvalue,
void* arg)
1620 const char* key = pkey;
1621 const char* value = pvalue;
1622 HttpContext* context = arg;
1626 WINPR_ASSERT(value);
1628 return http_context_set_cookie(context, key, value);
1631BOOL http_response_extract_cookies(
const HttpResponse* response, HttpContext* context)
1633 WINPR_ASSERT(response);
1634 WINPR_ASSERT(context);
1636 return HashTable_Foreach(response->SetCookie, extract_cookie, context);
1639FREERDP_LOCAL BOOL http_context_set_header(HttpContext* context,
const char* key,
const char* value,
1642 WINPR_ASSERT(context);
1643 va_list ap = WINPR_C_ARRAY_INIT;
1644 va_start(ap, value);
1645 const BOOL rc = http_context_set_header_va(context, key, value, ap);
1650BOOL http_request_set_header(HttpRequest* request,
const char* key,
const char* value, ...)
1652 WINPR_ASSERT(request);
1655 va_list ap = WINPR_C_ARRAY_INIT;
1656 va_start(ap, value);
1657 winpr_vasprintf(&v, &vlen, value, ap);
1659 const BOOL rc = HashTable_Insert(request->headers, key, v);
1664BOOL http_context_set_header_va(HttpContext* context,
const char* key,
const char* value,
1669 winpr_vasprintf(&v, &vlen, value, ap);
1670 const BOOL rc = HashTable_Insert(context->headers, key, v);
WINPR_ATTR_NODISCARD 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.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew