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>
45#define TAG FREERDP_TAG("core.gateway.http")
47#define RESPONSE_SIZE_LIMIT (64ULL * 1024ULL * 1024ULL)
49#define WEBSOCKET_MAGIC_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
62 char* RdgConnectionId;
63 char* RdgCorrelationId;
65 BOOL websocketUpgrade;
66 char* SecWebsocketKey;
67 wListDictionary* cookies;
79 TRANSFER_ENCODING TransferEncoding;
88 const char* ReasonPhrase;
91 const char* ContentType;
92 TRANSFER_ENCODING TransferEncoding;
93 const char* SecWebsocketVersion;
94 const char* SecWebsocketAccept;
99 wListDictionary* Authenticates;
100 wListDictionary* SetCookie;
104static char* string_strnstr(
char* str1,
const char* str2,
size_t slen)
110 if ((c = *str2++) !=
'\0')
112 len = strnlen(str2, slen + 1);
118 if (slen-- < 1 || (sc = *str1++) ==
'\0')
124 }
while (strncmp(str1, str2, len) != 0);
132static BOOL strings_equals_nocase(
const void* obj1,
const void* obj2)
137 return _stricmp(obj1, obj2) == 0;
140HttpContext* http_context_new(
void)
142 HttpContext* context = (HttpContext*)calloc(1,
sizeof(HttpContext));
146 context->cookies = ListDictionary_New(FALSE);
147 if (!context->cookies)
150 wObject* key = ListDictionary_KeyObject(context->cookies);
151 wObject* value = ListDictionary_ValueObject(context->cookies);
155 key->fnObjectFree = winpr_ObjectStringFree;
156 key->fnObjectNew = winpr_ObjectStringClone;
157 value->fnObjectFree = winpr_ObjectStringFree;
158 value->fnObjectNew = winpr_ObjectStringClone;
163 WINPR_PRAGMA_DIAG_PUSH
164 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
165 http_context_free(context);
166 WINPR_PRAGMA_DIAG_POP
170BOOL http_context_set_method(HttpContext* context,
const char* Method)
172 if (!context || !Method)
175 free(context->Method);
176 context->Method = _strdup(Method);
178 if (!context->Method)
184BOOL http_request_set_content_type(HttpRequest* request,
const char* ContentType)
186 if (!request || !ContentType)
189 free(request->ContentType);
190 request->ContentType = _strdup(ContentType);
192 if (!request->ContentType)
198const char* http_context_get_uri(HttpContext* context)
206BOOL http_context_set_uri(HttpContext* context,
const char* URI)
208 if (!context || !URI)
212 context->URI = _strdup(URI);
220BOOL http_context_set_user_agent(HttpContext* context,
const char* UserAgent)
222 if (!context || !UserAgent)
225 free(context->UserAgent);
226 context->UserAgent = _strdup(UserAgent);
228 if (!context->UserAgent)
234BOOL http_context_set_x_ms_user_agent(HttpContext* context,
const char* X_MS_UserAgent)
236 if (!context || !X_MS_UserAgent)
239 free(context->X_MS_UserAgent);
240 context->X_MS_UserAgent = _strdup(X_MS_UserAgent);
242 if (!context->X_MS_UserAgent)
248BOOL http_context_set_host(HttpContext* context,
const char* Host)
250 if (!context || !Host)
254 context->Host = _strdup(Host);
262BOOL http_context_set_accept(HttpContext* context,
const char* Accept)
264 if (!context || !Accept)
267 free(context->Accept);
268 context->Accept = _strdup(Accept);
270 if (!context->Accept)
276BOOL http_context_set_cache_control(HttpContext* context,
const char* CacheControl)
278 if (!context || !CacheControl)
281 free(context->CacheControl);
282 context->CacheControl = _strdup(CacheControl);
284 if (!context->CacheControl)
290BOOL http_context_set_connection(HttpContext* context,
const char* Connection)
292 if (!context || !Connection)
295 free(context->Connection);
296 context->Connection = _strdup(Connection);
298 if (!context->Connection)
304WINPR_ATTR_FORMAT_ARG(2, 0)
305static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const
char* str, va_list ap)
310 size_t PragmaSize = 0;
313 const int size = winpr_vasprintf(&Pragma, &PragmaSize, str, ap);
323 winpr_asprintf(&sstr, &slen,
"%s, %s", context->Pragma, Pragma);
330 free(context->Pragma);
331 context->Pragma = sstr;
340WINPR_ATTR_FORMAT_ARG(2, 3)
341BOOL http_context_set_pragma(HttpContext* context, WINPR_FORMAT_ARG const
char* Pragma, ...)
343 if (!context || !Pragma)
346 free(context->Pragma);
347 context->Pragma = NULL;
350 va_start(ap, Pragma);
351 return list_append(context, Pragma, ap);
354WINPR_ATTR_FORMAT_ARG(2, 3)
355BOOL http_context_append_pragma(HttpContext* context, const
char* Pragma, ...)
357 if (!context || !Pragma)
361 va_start(ap, Pragma);
362 return list_append(context, Pragma, ap);
365static char* guid2str(
const GUID* guid)
369 char* strguid = NULL;
370 char bracedGuid[64] = { 0 };
372 RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
374 if (rpcStatus != RPC_S_OK)
377 (void)sprintf_s(bracedGuid,
sizeof(bracedGuid),
"{%s}", strguid);
378 RpcStringFreeA(&strguid);
379 return _strdup(bracedGuid);
382BOOL http_context_set_rdg_connection_id(HttpContext* context,
const GUID* RdgConnectionId)
384 if (!context || !RdgConnectionId)
387 free(context->RdgConnectionId);
388 context->RdgConnectionId = guid2str(RdgConnectionId);
390 if (!context->RdgConnectionId)
396BOOL http_context_set_rdg_correlation_id(HttpContext* context,
const GUID* RdgCorrelationId)
398 if (!context || !RdgCorrelationId)
401 free(context->RdgCorrelationId);
402 context->RdgCorrelationId = guid2str(RdgCorrelationId);
404 if (!context->RdgCorrelationId)
410BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
418 if (RPC_S_OK != UuidCreate(&key))
421 free(context->SecWebsocketKey);
422 context->SecWebsocketKey = crypto_base64_encode((BYTE*)&key,
sizeof(key));
423 if (!context->SecWebsocketKey)
427 context->websocketUpgrade = enable;
431BOOL http_context_is_websocket_upgrade_enabled(HttpContext* context)
433 return context->websocketUpgrade;
436BOOL http_context_set_rdg_auth_scheme(HttpContext* context,
const char* RdgAuthScheme)
438 if (!context || !RdgAuthScheme)
441 free(context->RdgAuthScheme);
442 context->RdgAuthScheme = _strdup(RdgAuthScheme);
443 return context->RdgAuthScheme != NULL;
446BOOL http_context_set_cookie(HttpContext* context,
const char* CookieName,
const char* CookieValue)
448 if (!context || !CookieName || !CookieValue)
450 if (ListDictionary_Contains(context->cookies, CookieName))
452 if (!ListDictionary_SetItemValue(context->cookies, CookieName, CookieValue))
457 if (!ListDictionary_Add(context->cookies, CookieName, CookieValue))
463void http_context_free(HttpContext* context)
467 free(context->SecWebsocketKey);
468 free(context->UserAgent);
469 free(context->X_MS_UserAgent);
472 free(context->Accept);
473 free(context->Method);
474 free(context->CacheControl);
475 free(context->Connection);
476 free(context->Pragma);
477 free(context->RdgConnectionId);
478 free(context->RdgCorrelationId);
479 free(context->RdgAuthScheme);
480 ListDictionary_Free(context->cookies);
485BOOL http_request_set_method(HttpRequest* request,
const char* Method)
487 if (!request || !Method)
490 free(request->Method);
491 request->Method = _strdup(Method);
493 if (!request->Method)
499BOOL http_request_set_uri(HttpRequest* request,
const char* URI)
501 if (!request || !URI)
505 request->URI = _strdup(URI);
513BOOL http_request_set_auth_scheme(HttpRequest* request,
const char* AuthScheme)
515 if (!request || !AuthScheme)
518 free(request->AuthScheme);
519 request->AuthScheme = _strdup(AuthScheme);
521 if (!request->AuthScheme)
527BOOL http_request_set_auth_param(HttpRequest* request,
const char* AuthParam)
529 if (!request || !AuthParam)
532 free(request->AuthParam);
533 request->AuthParam = _strdup(AuthParam);
535 if (!request->AuthParam)
541BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
543 if (!request || TransferEncoding == TransferEncodingUnknown)
546 request->TransferEncoding = TransferEncoding;
551WINPR_ATTR_FORMAT_ARG(2, 3)
552static BOOL http_encode_print(
wStream* s, WINPR_FORMAT_ARG const
char* fmt, ...)
563 length = vsnprintf(NULL, 0, fmt, ap) + 1;
566 if (!Stream_EnsureRemainingCapacity(s, (
size_t)length))
569 str = (
char*)Stream_Pointer(s);
571 used = vsnprintf(str, (
size_t)length, fmt, ap);
575 if ((used + 1) != length)
578 Stream_Seek(s, (
size_t)used);
582static BOOL http_encode_body_line(
wStream* s,
const char* param,
const char* value)
584 if (!s || !param || !value)
587 return http_encode_print(s,
"%s: %s\r\n", param, value);
590static BOOL http_encode_content_length_line(
wStream* s,
size_t ContentLength)
592 return http_encode_print(s,
"Content-Length: %" PRIuz
"\r\n", ContentLength);
595static BOOL http_encode_header_line(
wStream* s,
const char* Method,
const char* URI)
597 if (!s || !Method || !URI)
600 return http_encode_print(s,
"%s %s HTTP/1.1\r\n", Method, URI);
603static BOOL http_encode_authorization_line(
wStream* s,
const char* AuthScheme,
604 const char* AuthParam)
606 if (!s || !AuthScheme || !AuthParam)
609 return http_encode_print(s,
"Authorization: %s %s\r\n", AuthScheme, AuthParam);
612static BOOL http_encode_cookie_line(
wStream* s, wListDictionary* cookies)
614 ULONG_PTR* keys = NULL;
620 ListDictionary_Lock(cookies);
621 const size_t count = ListDictionary_GetKeys(cookies, &keys);
626 status = http_encode_print(s,
"Cookie: ");
630 for (
size_t x = 0; status && x < count; x++)
632 char* cur = (
char*)ListDictionary_GetItemValue(cookies, (
void*)keys[x]);
640 status = http_encode_print(s,
"; ");
644 status = http_encode_print(s,
"%s=%s", (
char*)keys[x], cur);
647 status = http_encode_print(s,
"\r\n");
650 ListDictionary_Unlock(cookies);
654wStream* http_request_write(HttpContext* context, HttpRequest* request)
658 if (!context || !request)
661 s = Stream_New(NULL, 1024);
666 if (!http_encode_header_line(s, request->Method, request->URI) ||
667 !http_encode_body_line(s,
"Cache-Control", context->CacheControl) ||
668 !http_encode_body_line(s,
"Pragma", context->Pragma) ||
669 !http_encode_body_line(s,
"Accept", context->Accept) ||
670 !http_encode_body_line(s,
"User-Agent", context->UserAgent) ||
671 !http_encode_body_line(s,
"Host", context->Host))
674 if (!context->websocketUpgrade)
676 if (!http_encode_body_line(s,
"Connection", context->Connection))
681 if (!http_encode_body_line(s,
"Connection",
"Upgrade") ||
682 !http_encode_body_line(s,
"Upgrade",
"websocket") ||
683 !http_encode_body_line(s,
"Sec-Websocket-Version",
"13") ||
684 !http_encode_body_line(s,
"Sec-Websocket-Key", context->SecWebsocketKey))
688 if (context->RdgConnectionId)
690 if (!http_encode_body_line(s,
"RDG-Connection-Id", context->RdgConnectionId))
694 if (context->RdgCorrelationId)
696 if (!http_encode_body_line(s,
"RDG-Correlation-Id", context->RdgCorrelationId))
700 if (context->RdgAuthScheme)
702 if (!http_encode_body_line(s,
"RDG-Auth-Scheme", context->RdgAuthScheme))
706 if (request->TransferEncoding != TransferEncodingIdentity)
708 if (request->TransferEncoding == TransferEncodingChunked)
710 if (!http_encode_body_line(s,
"Transfer-Encoding",
"chunked"))
718 if (!http_encode_content_length_line(s, request->ContentLength))
722 if (request->Authorization)
724 if (!http_encode_body_line(s,
"Authorization", request->Authorization))
727 else if (request->AuthScheme && request->AuthParam)
729 if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
733 if (context->cookies)
735 if (!http_encode_cookie_line(s, context->cookies))
739 if (request->ContentType)
741 if (!http_encode_body_line(s,
"Content-Type", request->ContentType))
745 if (context->X_MS_UserAgent)
747 if (!http_encode_body_line(s,
"X-MS-User-Agent", context->X_MS_UserAgent))
751 if (!http_encode_print(s,
"\r\n"))
754 Stream_SealLength(s);
757 Stream_Free(s, TRUE);
761HttpRequest* http_request_new(
void)
763 HttpRequest* request = (HttpRequest*)calloc(1,
sizeof(HttpRequest));
767 request->TransferEncoding = TransferEncodingIdentity;
771void http_request_free(HttpRequest* request)
776 free(request->AuthParam);
777 free(request->AuthScheme);
778 free(request->Authorization);
779 free(request->ContentType);
780 free(request->Method);
785static BOOL http_response_parse_header_status_line(HttpResponse* response,
const char* status_line)
788 char* separator = NULL;
789 char* status_code = NULL;
790 char* reason_phrase = NULL;
796 separator = strchr(status_line,
' ');
801 status_code = separator + 1;
802 separator = strchr(status_code,
' ');
807 reason_phrase = separator + 1;
811 long val = strtol(status_code, NULL, 0);
813 if ((errno != 0) || (val < 0) || (val > INT16_MAX))
816 response->StatusCode = (INT16)val;
818 response->ReasonPhrase = reason_phrase;
820 if (!response->ReasonPhrase)
828 WLog_ERR(TAG,
"http_response_parse_header_status_line failed [%s]", status_line);
833static BOOL http_response_parse_header_field(HttpResponse* response,
const char* name,
838 WINPR_ASSERT(response);
843 if (_stricmp(name,
"Content-Length") == 0)
845 unsigned long long val = 0;
847 val = _strtoui64(value, NULL, 0);
849 if ((errno != 0) || (val > INT32_MAX))
852 response->ContentLength = WINPR_ASSERTING_INT_CAST(
size_t, val);
854 else if (_stricmp(name,
"Content-Type") == 0)
856 response->ContentType = value;
858 if (!response->ContentType)
861 else if (_stricmp(name,
"Transfer-Encoding") == 0)
863 if (_stricmp(value,
"identity") == 0)
864 response->TransferEncoding = TransferEncodingIdentity;
865 else if (_stricmp(value,
"chunked") == 0)
866 response->TransferEncoding = TransferEncodingChunked;
868 response->TransferEncoding = TransferEncodingUnknown;
870 else if (_stricmp(name,
"Sec-WebSocket-Version") == 0)
872 response->SecWebsocketVersion = value;
874 if (!response->SecWebsocketVersion)
877 else if (_stricmp(name,
"Sec-WebSocket-Accept") == 0)
879 response->SecWebsocketAccept = value;
881 if (!response->SecWebsocketAccept)
884 else if (_stricmp(name,
"WWW-Authenticate") == 0)
886 char* separator = NULL;
887 const char* authScheme = NULL;
888 char* authValue = NULL;
889 separator = strchr(value,
' ');
901 authValue = separator + 1;
903 if (!authScheme || !authValue)
916 status = ListDictionary_Add(response->Authenticates, authScheme, authValue);
918 else if (_stricmp(name,
"Set-Cookie") == 0)
920 char* separator = NULL;
921 const char* CookieName = NULL;
922 char* CookieValue = NULL;
923 separator = strchr(value,
'=');
933 CookieValue = separator + 1;
935 if (!CookieName || !CookieValue)
938 if (*CookieValue ==
'"')
940 char* p = CookieValue;
941 while (*p !=
'"' && *p !=
'\0')
951 char* p = CookieValue;
952 while (*p !=
';' && *p !=
'\0' && *p !=
' ')
964 status = ListDictionary_Add(response->SetCookie, CookieName, CookieValue);
970static BOOL http_response_parse_header(HttpResponse* response)
976 char* colon_pos = NULL;
977 char* end_of_header = NULL;
978 char end_of_header_char = 0;
983 if (!response->lines)
986 if (!http_response_parse_header_status_line(response, response->lines[0]))
989 for (
size_t count = 1; count < response->count; count++)
991 line = response->lines[count];
1003 colon_pos = strchr(line,
':');
1007 if ((colon_pos == NULL) || (colon_pos == line))
1011 for (end_of_header = colon_pos; end_of_header != line; end_of_header--)
1013 c = end_of_header[-1];
1015 if (c !=
' ' && c !=
'\t' && c !=
':')
1019 if (end_of_header == line)
1022 end_of_header_char = *end_of_header;
1023 *end_of_header =
'\0';
1027 char* value = colon_pos + 1;
1028 for (; *value; value++)
1030 if ((*value !=
' ') && (*value !=
'\t'))
1034 const int res = http_response_parse_header_field(response, name, value);
1035 *end_of_header = end_of_header_char;
1044 WLog_ERR(TAG,
"parsing failed");
1049static void http_response_print(wLog* log, DWORD level,
const HttpResponse* response,
1050 const char* file,
size_t line,
const char* fkt)
1052 char buffer[64] = { 0 };
1055 WINPR_ASSERT(response);
1057 if (!WLog_IsLevelActive(log, level))
1060 const long status = http_response_get_status_code(response);
1061 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
"HTTP status: %s",
1062 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1064 if (WLog_IsLevelActive(log, WLOG_DEBUG))
1066 for (
size_t i = 0; i < response->count; i++)
1067 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, WLOG_DEBUG, line, file, fkt,
1068 "[%" PRIuz
"] %s", i, response->lines[i]);
1071 if (response->ReasonPhrase)
1072 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
"[reason] %s",
1073 response->ReasonPhrase);
1075 if (WLog_IsLevelActive(log, WLOG_TRACE))
1077 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, WLOG_TRACE, line, file, fkt,
1078 "[body][%" PRIuz
"] %s", response->BodyLength, response->BodyContent);
1082static BOOL http_use_content_length(
const char* cur)
1089 if (_strnicmp(cur,
"application/rpc", 15) == 0)
1091 else if (_strnicmp(cur,
"text/plain", 10) == 0)
1093 else if (_strnicmp(cur,
"text/html", 9) == 0)
1095 else if (_strnicmp(cur,
"application/json", 16) == 0)
1100 char end = cur[pos];
1119static int print_bio_error(
const char* str,
size_t len,
void* bp)
1124 WLog_Print(log, WLOG_ERROR,
"%s", str);
1125 if (len > INT32_MAX)
1130int http_chuncked_read(BIO* bio, BYTE* pBuffer,
size_t size,
1134 int effectiveDataLen = 0;
1136 WINPR_ASSERT(pBuffer);
1137 WINPR_ASSERT(encodingContext != NULL);
1138 WINPR_ASSERT(size <= INT32_MAX);
1141 switch (encodingContext->state)
1143 case ChunkStateData:
1146 (size > encodingContext->nextOffset ? encodingContext->nextOffset : size);
1151 status = BIO_read(bio, pBuffer, (
int)rd);
1153 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1155 encodingContext->nextOffset -= WINPR_ASSERTING_INT_CAST(uint32_t, status);
1156 if (encodingContext->nextOffset == 0)
1158 encodingContext->state = ChunkStateFooter;
1159 encodingContext->headerFooterPos = 0;
1161 effectiveDataLen += status;
1163 if ((
size_t)status == size)
1164 return effectiveDataLen;
1167 size -= (size_t)status;
1170 case ChunkStateFooter:
1172 char _dummy[2] = { 0 };
1173 WINPR_ASSERT(encodingContext->nextOffset == 0);
1174 WINPR_ASSERT(encodingContext->headerFooterPos < 2);
1176 status = BIO_read(bio, _dummy, (
int)(2 - encodingContext->headerFooterPos));
1179 encodingContext->headerFooterPos += (size_t)status;
1180 if (encodingContext->headerFooterPos == 2)
1182 encodingContext->state = ChunkStateLenghHeader;
1183 encodingContext->headerFooterPos = 0;
1187 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1190 case ChunkStateLenghHeader:
1192 BOOL _haveNewLine = FALSE;
1193 char* dst = &encodingContext->lenBuffer[encodingContext->headerFooterPos];
1194 WINPR_ASSERT(encodingContext->nextOffset == 0);
1195 while (encodingContext->headerFooterPos < 10 && !_haveNewLine)
1198 status = BIO_read(bio, dst, 1);
1202 _haveNewLine = TRUE;
1203 encodingContext->headerFooterPos += (size_t)status;
1207 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1213 size_t tmp = strtoul(encodingContext->lenBuffer, NULL, 16);
1214 if ((errno != 0) || (tmp > SIZE_MAX))
1217 encodingContext->nextOffset = 0;
1218 encodingContext->state = ChunkStateEnd;
1221 encodingContext->nextOffset = tmp;
1222 encodingContext->state = ChunkStateData;
1224 if (encodingContext->nextOffset == 0)
1226 WLog_DBG(TAG,
"chunked encoding end of stream received");
1227 encodingContext->headerFooterPos = 0;
1228 encodingContext->state = ChunkStateEnd;
1229 return (effectiveDataLen > 0 ? effectiveDataLen : 0);
1240#define sleep_or_timeout(tls, startMS, timeoutMS) \
1241 sleep_or_timeout_((tls), (startMS), (timeoutMS), __FILE__, __func__, __LINE__)
1242static BOOL sleep_or_timeout_(rdpTls* tls, UINT64 startMS, UINT32 timeoutMS,
const char* file,
1243 const char* fkt,
size_t line)
1248 const UINT64 nowMS = GetTickCount64();
1249 if (nowMS - startMS > timeoutMS)
1251 DWORD level = WLOG_ERROR;
1252 wLog* log = WLog_Get(TAG);
1253 if (WLog_IsLevelActive(log, level))
1254 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
1255 "timeout [%" PRIu32
"ms] exceeded", timeoutMS);
1258 if (!BIO_should_retry(tls->bio))
1260 DWORD level = WLOG_ERROR;
1261 wLog* log = WLog_Get(TAG);
1262 if (WLog_IsLevelActive(log, level))
1264 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
"Retries exceeded");
1265 ERR_print_errors_cb(print_bio_error, log);
1269 if (freerdp_shall_disconnect_context(tls->context))
1275static SSIZE_T http_response_recv_line(rdpTls* tls, HttpResponse* response)
1278 WINPR_ASSERT(response);
1280 SSIZE_T payloadOffset = -1;
1281 const UINT32 timeoutMS =
1283 const UINT64 startMS = GetTickCount64();
1284 while (payloadOffset <= 0)
1286 size_t bodyLength = 0;
1287 size_t position = 0;
1294 status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);
1297 if (sleep_or_timeout(tls, startMS, timeoutMS))
1302#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
1303 VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
1305 Stream_Seek(response->data, (
size_t)status);
1307 if (!Stream_EnsureRemainingCapacity(response->data, 1024))
1310 position = Stream_GetPosition(response->data);
1314 else if (position > RESPONSE_SIZE_LIMIT)
1316 WLog_ERR(TAG,
"Request header too large! (%" PRIdz
" bytes) Aborting!", bodyLength);
1322 s = (position > 8) ? 8 : position;
1323 end = (
char*)Stream_Pointer(response->data) - s;
1325 if (string_strnstr(end,
"\r\n\r\n", s) != NULL)
1326 payloadOffset = WINPR_ASSERTING_INT_CAST(SSIZE_T, Stream_GetPosition(response->data));
1330 return payloadOffset;
1333static BOOL http_response_recv_body(rdpTls* tls, HttpResponse* response, BOOL readContentLength,
1334 size_t payloadOffset,
size_t bodyLength)
1339 WINPR_ASSERT(response);
1341 const UINT64 startMS = GetTickCount64();
1342 const UINT32 timeoutMS =
1345 if ((response->TransferEncoding == TransferEncodingChunked) && readContentLength)
1348 ctx.state = ChunkStateLenghHeader;
1350 ctx.headerFooterPos = 0;
1354 if (!Stream_EnsureRemainingCapacity(response->data, 2048))
1357 int status = http_chuncked_read(tls->bio, Stream_Pointer(response->data),
1358 Stream_GetRemainingCapacity(response->data), &ctx);
1361 if (sleep_or_timeout(tls, startMS, timeoutMS))
1366 Stream_Seek(response->data, (
size_t)status);
1369 }
while (ctx.state != ChunkStateEnd);
1370 response->BodyLength = WINPR_ASSERTING_INT_CAST(uint32_t, full_len);
1371 if (response->BodyLength > 0)
1372 response->BodyContent = &(Stream_Buffer(response->data))[payloadOffset];
1376 while (response->BodyLength < bodyLength)
1380 if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
1384 size_t diff = bodyLength - response->BodyLength;
1385 if (diff > INT32_MAX)
1387 status = BIO_read(tls->bio, Stream_Pointer(response->data), (
int)diff);
1391 if (sleep_or_timeout(tls, startMS, timeoutMS))
1396 Stream_Seek(response->data, (
size_t)status);
1397 response->BodyLength += (
unsigned long)status;
1399 if (response->BodyLength > RESPONSE_SIZE_LIMIT)
1401 WLog_ERR(TAG,
"Request body too large! (%" PRIdz
" bytes) Aborting!",
1402 response->BodyLength);
1407 if (response->BodyLength > 0)
1408 response->BodyContent = &(Stream_Buffer(response->data))[payloadOffset];
1410 if (bodyLength != response->BodyLength)
1412 WLog_WARN(TAG,
"%s unexpected body length: actual: %" PRIuz
", expected: %" PRIuz,
1413 response->ContentType, response->BodyLength, bodyLength);
1416 response->BodyLength = MIN(bodyLength, response->BodyLength);
1420 if (!Stream_EnsureRemainingCapacity(response->data,
sizeof(UINT16)))
1422 Stream_Write_UINT16(response->data, 0);
1430HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
1432 size_t bodyLength = 0;
1433 HttpResponse* response = http_response_new();
1438 response->ContentLength = 0;
1440 const SSIZE_T payloadOffset = http_response_recv_line(tls, response);
1441 if (payloadOffset < 0)
1447 char* buffer = Stream_BufferAs(response->data,
char);
1448 char* line = Stream_BufferAs(response->data,
char);
1449 char* context = NULL;
1451 while ((line = string_strnstr(line,
"\r\n",
1452 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset) -
1453 WINPR_ASSERTING_INT_CAST(
size_t, (line - buffer)) - 2UL)))
1459 response->count = count;
1463 response->lines = (
char**)calloc(response->count,
sizeof(
char*));
1465 if (!response->lines)
1469 buffer[payloadOffset - 1] =
'\0';
1470 buffer[payloadOffset - 2] =
'\0';
1472 line = strtok_s(buffer,
"\r\n", &context);
1474 while (line && (response->count > count))
1476 response->lines[count] = line;
1477 line = strtok_s(NULL,
"\r\n", &context);
1481 if (!http_response_parse_header(response))
1484 response->BodyLength =
1485 Stream_GetPosition(response->data) - WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset);
1487 WINPR_ASSERT(response->BodyLength == 0);
1488 bodyLength = response->BodyLength;
1490 if (readContentLength)
1492 const char* cur = response->ContentType;
1496 if (http_use_content_length(cur))
1498 if (response->ContentLength < RESPONSE_SIZE_LIMIT)
1499 bodyLength = response->ContentLength;
1504 readContentLength = FALSE;
1506 cur = strchr(cur,
';');
1510 if (bodyLength > RESPONSE_SIZE_LIMIT)
1512 WLog_ERR(TAG,
"Expected request body too large! (%" PRIdz
" bytes) Aborting!",
1518 if (!http_response_recv_body(tls, response, readContentLength,
1519 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset), bodyLength))
1522 Stream_SealLength(response->data);
1525 if (!Stream_EnsureRemainingCapacity(response->data, 2))
1527 Stream_Write_UINT16(response->data, 0);
1531 http_response_free(response);
1535const BYTE* http_response_get_body(
const HttpResponse* response)
1540 return response->BodyContent;
1543static BOOL set_compare(wListDictionary* dict)
1546 wObject* key = ListDictionary_KeyObject(dict);
1547 wObject* value = ListDictionary_KeyObject(dict);
1550 key->fnObjectEquals = strings_equals_nocase;
1551 value->fnObjectEquals = strings_equals_nocase;
1555HttpResponse* http_response_new(
void)
1557 HttpResponse* response = (HttpResponse*)calloc(1,
sizeof(HttpResponse));
1562 response->Authenticates = ListDictionary_New(FALSE);
1564 if (!response->Authenticates)
1567 if (!set_compare(response->Authenticates))
1570 response->SetCookie = ListDictionary_New(FALSE);
1572 if (!response->SetCookie)
1575 if (!set_compare(response->SetCookie))
1578 response->data = Stream_New(NULL, 2048);
1580 if (!response->data)
1583 response->TransferEncoding = TransferEncodingIdentity;
1586 WINPR_PRAGMA_DIAG_PUSH
1587 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1588 http_response_free(response);
1589 WINPR_PRAGMA_DIAG_POP
1593void http_response_free(HttpResponse* response)
1598 free((
void*)response->lines);
1599 ListDictionary_Free(response->Authenticates);
1600 ListDictionary_Free(response->SetCookie);
1601 Stream_Free(response->data, TRUE);
1605const char* http_request_get_uri(HttpRequest* request)
1610 return request->URI;
1613SSIZE_T http_request_get_content_length(HttpRequest* request)
1618 return (SSIZE_T)request->ContentLength;
1621BOOL http_request_set_content_length(HttpRequest* request,
size_t length)
1626 request->ContentLength = length;
1630INT16 http_response_get_status_code(
const HttpResponse* response)
1632 WINPR_ASSERT(response);
1634 return response->StatusCode;
1637size_t http_response_get_body_length(
const HttpResponse* response)
1639 WINPR_ASSERT(response);
1641 return response->BodyLength;
1644const char* http_response_get_auth_token(
const HttpResponse* response,
const char* method)
1646 if (!response || !method)
1649 if (!ListDictionary_Contains(response->Authenticates, method))
1652 return ListDictionary_GetItemValue(response->Authenticates, method);
1655const char* http_response_get_setcookie(
const HttpResponse* response,
const char* cookie)
1657 if (!response || !cookie)
1660 if (!ListDictionary_Contains(response->SetCookie, cookie))
1663 return ListDictionary_GetItemValue(response->SetCookie, cookie);
1666TRANSFER_ENCODING http_response_get_transfer_encoding(
const HttpResponse* response)
1669 return TransferEncodingUnknown;
1671 return response->TransferEncoding;
1674BOOL http_response_is_websocket(
const HttpContext* http,
const HttpResponse* response)
1676 BOOL isWebsocket = FALSE;
1677 WINPR_DIGEST_CTX* sha1 = NULL;
1678 char* base64accept = NULL;
1679 BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH];
1681 if (!http || !response)
1684 if (!http->websocketUpgrade || response->StatusCode != HTTP_STATUS_SWITCH_PROTOCOLS)
1687 if (response->SecWebsocketVersion && _stricmp(response->SecWebsocketVersion,
"13") != 0)
1690 if (!response->SecWebsocketAccept)
1695 sha1 = winpr_Digest_New();
1699 if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
1702 if (!winpr_Digest_Update(sha1, (BYTE*)http->SecWebsocketKey, strlen(http->SecWebsocketKey)))
1704 if (!winpr_Digest_Update(sha1, (
const BYTE*)WEBSOCKET_MAGIC_GUID, strlen(WEBSOCKET_MAGIC_GUID)))
1707 if (!winpr_Digest_Final(sha1, sha1_digest,
sizeof(sha1_digest)))
1710 base64accept = crypto_base64_encode(sha1_digest, WINPR_SHA1_DIGEST_LENGTH);
1714 if (_stricmp(response->SecWebsocketAccept, base64accept) != 0)
1716 WLog_WARN(TAG,
"Webserver gave Websocket Upgrade response but sanity check failed");
1721 winpr_Digest_Free(sha1);
1726void http_response_log_error_status_(wLog* log, DWORD level,
const HttpResponse* response,
1727 const char* file,
size_t line,
const char* fkt)
1730 WINPR_ASSERT(response);
1732 if (!WLog_IsLevelActive(log, level))
1735 char buffer[64] = { 0 };
1736 const long status = http_response_get_status_code(response);
1737 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
"Unexpected HTTP status: %s",
1738 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1739 http_response_print(log, level, response, file, line, fkt);
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.