Cleanup more of the networking layer

This commit is contained in:
doylet 2025-11-08 18:56:21 +11:00
parent c66830650f
commit 3aba851aef
10 changed files with 391 additions and 401 deletions

View File

@ -1,4 +1,4 @@
// Generated by the DN single header generator 2025-11-08 17:58:44
// Generated by the DN single header generator 2025-11-08 18:54:58
#define DN_BASE_INC_CPP

View File

@ -1,4 +1,4 @@
// Generated by the DN single header generator 2025-11-08 17:58:44
// Generated by the DN single header generator 2025-11-08 18:54:58
#if !defined(DN_BASE_INC_H)
#define DN_BASE_INC_H
@ -3098,7 +3098,7 @@ DN_API DN_Str8 DN_Str8FromArena (DN_Arena *arena, DN
DN_API DN_Str8 DN_Str8FromPool (DN_Pool *pool, DN_USize size);
DN_API DN_Str8 DN_Str8FromPtrArena (DN_Arena *arena, void const *data, DN_USize size);
DN_API DN_Str8 DN_Str8FromPtrPool (DN_Pool *pool, void const *data, DN_USize size);
DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Arena *pool, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Arena *arena, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Pool *pool, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Str8FromFmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);

View File

@ -952,7 +952,7 @@ DN_API DN_Str8 DN_Str8FromArena (DN_Arena *arena, DN
DN_API DN_Str8 DN_Str8FromPool (DN_Pool *pool, DN_USize size);
DN_API DN_Str8 DN_Str8FromPtrArena (DN_Arena *arena, void const *data, DN_USize size);
DN_API DN_Str8 DN_Str8FromPtrPool (DN_Pool *pool, void const *data, DN_USize size);
DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Arena *pool, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Arena *arena, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Pool *pool, DN_Str8 string);
DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Str8FromFmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);

View File

@ -3,18 +3,18 @@
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
DN_NETRequestInternal *DN_NET_RequestFromHandle(DN_NETRequest request)
DN_NETRequest *DN_NET_RequestFromHandle(DN_NETRequestHandle handle)
{
DN_NETRequestInternal *ptr = DN_Cast(DN_NETRequestInternal *) request.handle;
DN_NETRequestInternal *result = nullptr;
if (ptr && ptr->gen == request.gen)
DN_NETRequest *ptr = DN_Cast(DN_NETRequest *) handle.handle;
DN_NETRequest *result = nullptr;
if (ptr && ptr->gen == handle.gen)
result = ptr;
return result;
}
DN_NETRequest DN_NET_HandleFromRequest(DN_NETRequestInternal *request)
DN_NETRequestHandle DN_NET_HandleFromRequest(DN_NETRequest *request)
{
DN_NETRequest result = {};
DN_NETRequestHandle result = {};
if (request) {
result.handle = DN_Cast(DN_UPtr) request;
result.gen = request->gen;
@ -22,30 +22,10 @@ DN_NETRequest DN_NET_HandleFromRequest(DN_NETRequestInternal *request)
return result;
}
DN_NETResponse DN_NET_MakeResponseFromFinishedRequest_(DN_NETRequest request, DN_Arena *arena)
{
DN_NETResponse result = {};
DN_NETRequestInternal *request_ptr = DN_Cast(DN_NETRequestInternal *) request.handle;
if (request_ptr && request_ptr->gen == request.gen) {
DN_NETResponseInternal const *response = &request_ptr->response;
// NOTE: Construct the response from the request
result.request = request;
result.state = response->state;
result.http_status = response->http_status;
result.body = DN_Str8BuilderBuild(&response->body, arena);
if (response->error_str8.size)
result.error_str8 = DN_Str8FromStr8Arena(arena, response->error_str8);
}
return result;
}
void DN_NET_EndFinishedRequest_(DN_NETRequestInternal *request)
void DN_NET_EndFinishedRequest_(DN_NETRequest *request)
{
// NOTE: Deallocate the memory used in the request and reset the string builder
DN_ArenaPopTo(&request->arena, request->start_response_arena_pos);
request->response.body = DN_Str8BuilderFromArena(&request->arena);
// NOTE: Check that the request is completely detached
DN_Assert(request->next == nullptr);
}
@ -58,11 +38,10 @@ void DN_NET_BaseInit_(DN_NETCore *net, char *base, DN_U64 base_size)
net->completion_sem = DN_OS_SemaphoreInit(0);
}
DN_NETRequest DN_NET_SetupRequest_(DN_NETRequestInternal *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
DN_NETRequestHandle DN_NET_SetupRequest_(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
{
// NOTE: Setup request
DN_Assert(request);
DN_NETRequest result = {};
if (request) {
if (!request->arena.curr)
request->arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(1), DN_ArenaFlags_Nil);
@ -87,13 +66,11 @@ DN_NETRequest DN_NET_SetupRequest_(DN_NETRequestInternal *request, DN_Str8 url,
}
}
request->response.body = DN_Str8BuilderFromArena(&request->arena);
request->completion_sem = DN_OS_SemaphoreInit(0);
request->start_response_arena_pos = DN_ArenaPos(&request->arena);
result.handle = DN_Cast(DN_UPtr) request;
result.gen = request->gen;
}
DN_NETRequestHandle result = DN_NET_HandleFromRequest(request);
request->response.request = result;
return result;
}

View File

@ -52,26 +52,17 @@ struct DN_NETDoHTTPArgs
DN_Str8 payload;
};
struct DN_NETRequest
struct DN_NETRequestHandle
{
DN_UPtr handle;
DN_U64 gen;
};
struct DN_NETResponseInternal
{
DN_NETResponseState state;
bool ws_has_more;
DN_Str8Builder body;
DN_U32 http_status;
DN_Str8 error_str8;
};
struct DN_NETResponse
{
// NOTE: Common to WS and HTTP responses
DN_NETResponseState state;
DN_NETRequest request;
DN_NETRequestHandle request;
DN_Str8 error_str8;
DN_Str8 body;
@ -79,9 +70,8 @@ struct DN_NETResponse
DN_U32 http_status;
};
struct DN_NETRequestInternal
struct DN_NETRequest
{
// NOTE: Initialised in user thread, then read-only and shared to networking thread until reset
DN_Arena arena;
DN_USize start_response_arena_pos;
DN_NETRequestType type;
@ -90,9 +80,9 @@ struct DN_NETRequestInternal
DN_Str8 method;
DN_OSSemaphore completion_sem;
DN_NETDoHTTPArgs args;
DN_NETResponseInternal response;
DN_NETRequestInternal *next;
DN_NETRequestInternal *prev;
DN_NETResponse response;
DN_NETRequest *next;
DN_NETRequest *prev;
DN_U64 context[2];
};
@ -107,10 +97,10 @@ struct DN_NETCore
typedef void (DN_NETInitFunc) (DN_NETCore *net, char *base, DN_U64 base_size);
typedef void (DN_NETDeinitFunc) (DN_NETCore *net);
typedef DN_NETRequest (DN_NETDoHTTPFunc) (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
typedef DN_NETRequest (DN_NETDoWSFunc) (DN_NETCore *net, DN_Str8 url);
typedef void (DN_NETDoWSSendFunc) (DN_NETRequest request, DN_Str8 data, DN_NETWSSend send);
typedef DN_NETResponse (DN_NETWaitForResponseFunc) (DN_NETRequest request, DN_Arena *arena, DN_U32 timeout_ms);
typedef DN_NETRequestHandle(DN_NETDoHTTPFunc) (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
typedef DN_NETRequestHandle(DN_NETDoWSFunc) (DN_NETCore *net, DN_Str8 url);
typedef void (DN_NETDoWSSendFunc) (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
typedef DN_NETResponse (DN_NETWaitForResponseFunc) (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
typedef DN_NETResponse (DN_NETWaitForAnyResponseFunc)(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
struct DN_NETInterface
@ -124,13 +114,12 @@ struct DN_NETInterface
DN_NETWaitForAnyResponseFunc* wait_for_any_response;
};
DN_NETRequestInternal *DN_NET_RequestFromHandle (DN_NETRequest request);
DN_NETRequest DN_NET_HandleFromRequest (DN_NETRequestInternal *request);
DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle);
DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request);
// NOTE: Internal functions for different networking implementations to use
void DN_NET_BaseInit_ (DN_NETCore *net, char *base, DN_U64 base_size);
DN_NETRequest DN_NET_SetupRequest_ (DN_NETRequestInternal *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type);
DN_NETResponse DN_NET_MakeResponseFromFinishedRequest_(DN_NETRequest request, DN_Arena *arena);
void DN_NET_EndFinishedRequest_ (DN_NETRequestInternal *request);
DN_NETRequestHandle DN_NET_SetupRequest_ (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type);
void DN_NET_EndFinishedRequest_ (DN_NETRequest *request);
#endif // DN_NET_H

View File

@ -1,11 +1,13 @@
#include "dn_net.h"
#include "dn_net_curl.h"
struct DN_NETCurlConn
struct DN_NETCurlRequest
{
void *curl;
struct curl_slist *curl_slist;
void *handle;
struct curl_slist *slist;
char error[CURL_ERROR_SIZE];
bool ws_has_more;
DN_Str8Builder str8_builder;
};
enum DN_NETCurlRingEventType
@ -20,7 +22,7 @@ enum DN_NETCurlRingEventType
struct DN_NETCurlRingEvent
{
DN_NETCurlRingEventType type;
DN_NETRequest request;
DN_NETRequestHandle request;
DN_USize ws_send_size;
DN_NETWSSend ws_send;
};
@ -33,27 +35,39 @@ struct DN_NETCurlCore
bool kill_thread;
DN_OSMutex list_mutex; // Lock for request, response, deinit, free list
DN_NETRequestInternal *request_list; // Current requests submitted by the user thread awaiting to move into the thread request list
DN_NETRequestInternal *thread_request_list; // Current requests being executed by the CURL thread.
// This list is exclusively owned by the CURL thread so no locking is needed
DN_NETRequestInternal *response_list; // Finished requests that are to be deqeued by the user via wait for response
DN_NETRequestInternal *deinit_list; // Requests that are finished and are awaiting to be de-initialised by the CURL thread
DN_NETRequestInternal *free_list; // Request pool that new requests will use before allocating
DN_NETRequest *request_list; // Current requests submitted by the user thread awaiting to move into the thread request list
DN_NETRequest *response_list; // Finished requests that are to be deqeued by the user via wait for response
DN_NETRequest *deinit_list; // Requests that are finished and are awaiting to be de-initialised by the CURL thread
DN_NETRequest *free_list; // Request pool that new requests will use before allocating
// NOTE: Networking thread only
DN_NETRequest *thread_request_list; // Current requests being executed by the CURL thread.
// This list is exclusively owned by the CURL thread so no locking is needed
DN_OSThread thread;
void *thread_curlm;
};
static bool DN_NET_CurlRequestIsInList(DN_NETRequestInternal const *first, DN_NETRequestInternal const *find)
static DN_NETCurlRequest *DN_NET_CurlRequestFromRequest_(DN_NETRequest *req)
{
DN_NETCurlRequest *result = req ? DN_Cast(DN_NETCurlRequest *) req->context[0] : 0;
return result;
}
static DN_NETCore *DN_NET_CurlNetFromRequest(DN_NETRequest *req)
{
DN_NETCore *result = req ? DN_Cast(DN_NETCore *) req->context[1] : 0;
return result;
}
static bool DN_NET_CurlRequestIsInList(DN_NETRequest const *first, DN_NETRequest const *find)
{
bool result = false;
for (DN_NETRequestInternal const *it = first; !result && it; it = it->next)
for (DN_NETRequest const *it = first; !result && it; it = it->next)
result = find == it;
return result;
}
static void DN_NET_CurlMarkRequestDone_(DN_NETCore *net, DN_NETRequestInternal *request)
static void DN_NET_CurlMarkRequestDone_(DN_NETCore *net, DN_NETRequest *request)
{
DN_Assert(request);
DN_Assert(net);
@ -77,10 +91,11 @@ static void DN_NET_CurlMarkRequestDone_(DN_NETCore *net, DN_NETRequestInternal *
static DN_USize DN_NET_CurlHTTPCallback_(char *payload, DN_USize size, DN_USize count, void *user_data)
{
auto *request = DN_Cast(DN_NETRequestInternal *) user_data;
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
DN_USize result = 0;
DN_USize payload_size = size * count;
if (DN_Str8BuilderAppendBytesCopy(&request->response.body, payload, payload_size))
if (DN_Str8BuilderAppendBytesCopy(&curl_req->str8_builder, payload, payload_size))
result = payload_size;
return result;
}
@ -102,25 +117,25 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
DN_Ring_Read(&curl->ring, &event, sizeof(event));
}
DN_NETRequest *req = DN_NET_RequestFromHandle(event.request);
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
switch (event.type) {
case DN_NETCurlRingEventType_Nil: dequeue_ring = false; break;
case DN_NETCurlRingEventType_DoRequest: {
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *)event.request.handle;
DN_Assert(request->response.state == DN_NETResponseState_Nil);
DN_Assert(request->type != DN_NETRequestType_Nil);
DN_Assert(req->response.state == DN_NETResponseState_Nil);
DN_Assert(req->type != DN_NETRequestType_Nil);
// NOTE: Attach it to the CURL thread's request list
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, request));
DN_DoublyLLDetach(curl->request_list, request);
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, req));
DN_DoublyLLDetach(curl->request_list, req);
}
DN_DoublyLLAppend(curl->thread_request_list, request);
DN_DoublyLLAppend(curl->thread_request_list, req);
// NOTE: Add the connection to CURLM and start ticking it once we finish handling all the
// ring events
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *) request->context[0];
CURLMcode multi_add = curl_multi_add_handle(curl->thread_curlm, conn->curl);
CURLMcode multi_add = curl_multi_add_handle(curl->thread_curlm, curl_req->handle);
DN_Assert(multi_add == CURLM_OK);
} break;
@ -141,46 +156,42 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
case DN_NETWSSend_Pong: curlws_flag = CURLWS_PONG; break;
}
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) event.request.handle;
DN_Assert(request->type == DN_NETRequestType_WS);
DN_Assert(request->response.state == DN_NETResponseState_WSOpen);
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, request));
DN_Assert(req->type == DN_NETRequestType_WS);
DN_Assert(req->response.state == DN_NETResponseState_WSOpen);
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, req));
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *) request->context[0];
DN_USize sent = 0;
CURLcode send_result = curl_ws_send(conn->curl, payload.data, payload.size, &sent, 0, curlws_flag);
CURLcode send_result = curl_ws_send(curl_req->handle, payload.data, payload.size, &sent, 0, curlws_flag);
DN_AssertF(send_result == CURLE_OK, "Failed to send: %s", curl_easy_strerror(send_result));
DN_AssertF(sent == payload.size, "Failed to send all bytes (%zu vs %zu)", sent, payload.size);
} break;
case DN_NETCurlRingEventType_ReceivedWSReceipt: {
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) event.request.handle;
DN_Assert(request->type == DN_NETRequestType_WS);
DN_Assert(request->response.state >= DN_NETResponseState_WSOpen && request->response.state <= DN_NETResponseState_WSPong);
request->response.state = DN_NETResponseState_WSOpen;
DN_Assert(req->type == DN_NETRequestType_WS);
DN_Assert(req->response.state >= DN_NETResponseState_WSOpen && req->response.state <= DN_NETResponseState_WSPong);
req->response.state = DN_NETResponseState_WSOpen;
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, request));
DN_DoublyLLDetach(curl->request_list, request);
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, req));
DN_DoublyLLDetach(curl->request_list, req);
}
DN_DoublyLLAppend(curl->thread_request_list, request);
DN_DoublyLLAppend(curl->thread_request_list, req);
} break;
case DN_NETCurlRingEventType_DeinitRequest: {
DN_Assert(event.request.handle != 0);
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) event.request.handle;
DN_NETRequest *request = DN_Cast(DN_NETRequest *) event.request.handle;
// NOTE: Release resources
DN_ArenaClear(&request->arena);
DN_OS_SemaphoreDeinit(&request->completion_sem);
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *) request->context[0];
curl_multi_remove_handle(curl->thread_curlm, conn->curl);
curl_easy_reset(conn->curl);
curl_slist_free_all(conn->curl_slist);
curl_multi_remove_handle(curl->thread_curlm, curl_req->handle);
curl_easy_reset(curl_req->handle);
curl_slist_free_all(curl_req->slist);
// NOTE: Zero the struct preserving just the data we need to retain
DN_NETRequestInternal resetter = {};
DN_NETRequest resetter = {};
resetter.arena = request->arena;
resetter.gen = request->gen;
DN_Memcpy(resetter.context, request->context, sizeof(resetter.context));
@ -208,42 +219,42 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
CURLMsg *msg = curl_multi_info_read(curl->thread_curlm, &msgs_in_queue);
if (msg) {
// NOTE: Get request handle
DN_NETRequestInternal *request = nullptr;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, DN_Cast(void **) & request);
DN_Assert(request);
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, request));
DN_NETRequest *req = nullptr;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, DN_Cast(void **) & req);
DN_Assert(req);
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, req));
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *)request->context[0];
DN_Assert(conn->curl == msg->easy_handle);
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
DN_Assert(curl_req->handle == msg->easy_handle);
if (msg->data.result == CURLE_OK) {
// NOTE: Get HTTP response code
CURLcode get_result = curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &request->response.http_status);
CURLcode get_result = curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &req->response.http_status);
if (get_result == CURLE_OK) {
if (request->type == DN_NETRequestType_HTTP) {
request->response.state = DN_NETResponseState_HTTP;
if (req->type == DN_NETRequestType_HTTP) {
req->response.state = DN_NETResponseState_HTTP;
} else {
DN_Assert(request->type == DN_NETRequestType_WS);
request->response.state = DN_NETResponseState_WSOpen;
DN_Assert(req->type == DN_NETRequestType_WS);
req->response.state = DN_NETResponseState_WSOpen;
}
} else {
request->response.error_str8 = DN_Str8FromFmtArena(&request->arena, "Failed to get HTTP response status (CURL %d): %s", msg->data.result, curl_easy_strerror(get_result));
request->response.state = DN_NETResponseState_Error;
req->response.error_str8 = DN_Str8FromFmtArena(&req->arena, "Failed to get HTTP response status (CURL %d): %s", msg->data.result, curl_easy_strerror(get_result));
req->response.state = DN_NETResponseState_Error;
}
} else {
DN_USize curl_extended_error_size = DN_CStr8Size(conn->error);
request->response.state = DN_NETResponseState_Error;
request->response.error_str8 = DN_Str8FromFmtArena(&request->arena,
DN_USize curl_extended_error_size = DN_CStr8Size(curl_req->error);
req->response.state = DN_NETResponseState_Error;
req->response.error_str8 = DN_Str8FromFmtArena(&req->arena,
"HTTP request '%.*s' failed (CURL %d): %s%s%s%s",
DN_Str8PrintFmt(request->url),
DN_Str8PrintFmt(req->url),
msg->data.result,
curl_easy_strerror(msg->data.result),
curl_extended_error_size ? " (" : "",
curl_extended_error_size ? conn->error : "",
curl_extended_error_size ? curl_req->error : "",
curl_extended_error_size ? ")" : "");
}
if (request->type == DN_NETRequestType_HTTP || request->response.state == DN_NETResponseState_Error) {
if (req->type == DN_NETRequestType_HTTP || req->response.state == DN_NETResponseState_Error) {
// NOTE: Remove the request from the multi handle if we're a HTTP request
// because it typically terminates the connection. In websockets the
// connection remains in the multi-handle to allow you to send and
@ -256,7 +267,7 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
curl_multi_remove_handle(curl->thread_curlm, msg->easy_handle);
}
DN_NET_CurlMarkRequestDone_(net, request);
DN_NET_CurlMarkRequestDone_(net, req);
}
if (msgs_in_queue == 0)
@ -265,52 +276,52 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
// NOTE: Check websockets
DN_USize ws_count = 0;
for (DN_NETRequestInternal *request = curl->thread_request_list; request; request = request->next) {
DN_Assert(request->type == DN_NETRequestType_WS || request->type == DN_NETRequestType_HTTP);
if (request->type != DN_NETRequestType_WS || !(request->response.state >= DN_NETResponseState_WSOpen && request->response.state <= DN_NETResponseState_WSPong))
for (DN_NETRequest *req = curl->thread_request_list; req; req = req->next) {
DN_Assert(req->type == DN_NETRequestType_WS || req->type == DN_NETRequestType_HTTP);
if (req->type != DN_NETRequestType_WS || !(req->response.state >= DN_NETResponseState_WSOpen && req->response.state <= DN_NETResponseState_WSPong))
continue;
ws_count++;
const curl_ws_frame *meta = nullptr;
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *) request->context[0];
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
CURLcode receive_result = CURLE_OK;
while (receive_result == CURLE_OK) {
// NOTE: Determine WS payload size received. Note that since we pass in a null pointer CURL
// will set meta->len to 0 and say that there's meta->bytesleft in the next chunk.
DN_USize bytes_read = 0;
receive_result = curl_ws_recv(conn->curl, nullptr, 0, &bytes_read, &meta);
receive_result = curl_ws_recv(curl_req->handle, nullptr, 0, &bytes_read, &meta);
if (receive_result != CURLE_OK)
continue;
DN_Assert(meta->len == 0);
if (meta->flags & CURLWS_TEXT)
request->response.state = DN_NETResponseState_WSText;
req->response.state = DN_NETResponseState_WSText;
if (meta->flags & CURLWS_BINARY)
request->response.state = DN_NETResponseState_WSBinary;
req->response.state = DN_NETResponseState_WSBinary;
if (meta->flags & CURLWS_PING)
request->response.state = DN_NETResponseState_WSPing;
req->response.state = DN_NETResponseState_WSPing;
if (meta->flags & CURLWS_PONG)
request->response.state = DN_NETResponseState_WSPong;
req->response.state = DN_NETResponseState_WSPong;
if (meta->flags & CURLWS_CLOSE)
request->response.state = DN_NETResponseState_WSClose;
req->response.state = DN_NETResponseState_WSClose;
request->response.ws_has_more = meta->flags & CURLWS_CONT;
if (request->response.ws_has_more) {
bool is_text_or_binary = request->response.state == DN_NETResponseState_WSText ||
request->response.state == DN_NETResponseState_WSBinary;
curl_req->ws_has_more = meta->flags & CURLWS_CONT;
if (curl_req->ws_has_more) {
bool is_text_or_binary = req->response.state == DN_NETResponseState_WSText ||
req->response.state == DN_NETResponseState_WSBinary;
DN_Assert(is_text_or_binary);
}
// NOTE: Allocate and read (we use meta->bytesleft as per comment from initial recv)
if (meta->bytesleft) {
DN_Str8 buffer = DN_Str8FromArena(&request->arena, meta->bytesleft, DN_ZMem_No);
DN_Str8 buffer = DN_Str8FromArena(&req->arena, meta->bytesleft, DN_ZMem_No);
DN_Assert(buffer.size == DN_Cast(DN_USize)meta->bytesleft);
receive_result = curl_ws_recv(conn->curl, buffer.data, buffer.size, &buffer.size, &meta);
receive_result = curl_ws_recv(curl_req->handle, buffer.data, buffer.size, &buffer.size, &meta);
DN_Assert(buffer.size == DN_Cast(DN_USize)meta->len);
DN_Str8BuilderAppendRef(&request->response.body, buffer);
DN_Str8BuilderAppendRef(&curl_req->str8_builder, buffer);
}
// NOTE: There are more bytes coming if meta->bytesleft is set, (e.g. the next chunk. We
@ -318,18 +329,18 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
//
// > If this is not a complete fragment, the bytesleft field informs about how many
// additional bytes are expected to arrive before this fragment is complete.
request->response.ws_has_more |= meta && meta->bytesleft > 0;
curl_req->ws_has_more |= meta && meta->bytesleft > 0;
}
// NOTE: curl_ws_recv returns CURLE_GOT_NOTHING if the associated connection is closed.
if (receive_result == CURLE_GOT_NOTHING)
request->response.ws_has_more = false;
curl_req->ws_has_more = false;
// NOTE: We read all the possible bytes that CURL has received for this message, but, there are
// more bytes left that we will receive on subsequent calls. We will continue to the next
// request and return back to this one when PumpRequests is called again where hopefully that
// data has arrived.
if (request->response.ws_has_more)
if (curl_req->ws_has_more)
continue;
// For CURLE_AGAIN
@ -344,31 +355,31 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
// if we received data, e.g. state was set to Text, Binary ... e.t.c we bypass this and
// report it to the user first. When the user waits for the response, they consume the data
// and then that will reinsert it into request list for CURL to read from the socket again.
bool received_data = (request->response.state >= DN_NETResponseState_WSText && request->response.state <= DN_NETResponseState_WSPong);
bool received_data = (req->response.state >= DN_NETResponseState_WSText && req->response.state <= DN_NETResponseState_WSPong);
if (receive_result == CURLE_AGAIN && !received_data)
continue;
if (!received_data) {
if (receive_result == CURLE_GOT_NOTHING) {
request->response.state = DN_NETResponseState_WSClose;
req->response.state = DN_NETResponseState_WSClose;
} else if (receive_result != CURLE_OK) {
DN_USize curl_extended_error_size = DN_CStr8Size(conn->error);
request->response.state = DN_NETResponseState_Error;
request->response.error_str8 = DN_Str8FromFmtArena(&request->arena,
DN_USize curl_extended_error_size = DN_CStr8Size(curl_req->error);
req->response.state = DN_NETResponseState_Error;
req->response.error_str8 = DN_Str8FromFmtArena(&req->arena,
"Websocket receive '%.*s' failed (CURL %d): %s%s%s%s",
DN_Str8PrintFmt(request->url),
DN_Str8PrintFmt(req->url),
receive_result,
curl_easy_strerror(receive_result),
curl_extended_error_size ? " (" : "",
curl_extended_error_size ? conn->error : "",
curl_extended_error_size ? curl_req->error : "",
curl_extended_error_size ? ")" : "");
}
}
DN_NETRequestInternal *request_copy = request;
request = request->prev;
DN_NETRequest *request_copy = req;
req = req->prev;
DN_NET_CurlMarkRequestDone_(net, request_copy);
if (!request)
if (!req)
break;
}
@ -419,62 +430,66 @@ void DN_NET_CurlDeinit(DN_NETCore *net)
DN_OS_ThreadDeinit(&curl->thread);
}
static DN_NETRequest DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
{
// NOTE: Allocate the request
DN_NETCurlCore *curl_core = DN_Cast(DN_NETCurlCore *) net->context;
DN_NETRequestInternal *request = nullptr;
DN_NETRequest result = {};
DN_NETRequest *req = nullptr;
DN_NETRequestHandle result = {};
{
// NOTE: The free list is modified by both the calling thread and the CURLM thread (which ticks
// all the requests in the background for us)
for (DN_OS_MutexScope(&curl_core->list_mutex)) {
request = curl_core->free_list;
DN_DoublyLLDetach(curl_core->free_list, request);
req = curl_core->free_list;
DN_DoublyLLDetach(curl_core->free_list, req);
}
// NOTE None in the free list so allocate one
if (!request) {
if (!req) {
DN_U64 arena_pos = DN_ArenaPos(&net->arena);
request = DN_ArenaNew(&net->arena, DN_NETRequestInternal, DN_ZMem_Yes);
DN_NETCurlConn *conn = DN_ArenaNew(&net->arena, DN_NETCurlConn, DN_ZMem_Yes);
if (!request || !conn) {
req = DN_ArenaNew(&net->arena, DN_NETRequest, DN_ZMem_Yes);
DN_NETCurlRequest *curl_req = DN_ArenaNew(&net->arena, DN_NETCurlRequest, DN_ZMem_Yes);
if (!req || !curl_req) {
DN_ArenaPopTo(&net->arena, arena_pos);
return result;
}
conn->curl = DN_Cast(CURL *) curl_easy_init();
request->context[0] = DN_Cast(DN_UPtr) conn;
curl_req->handle = DN_Cast(CURL *) curl_easy_init();
req->context[0] = DN_Cast(DN_UPtr) curl_req;
}
}
// NOTE: Setup the request
result = DN_NET_SetupRequest_(request, url, method, args, type);
request->context[1] = DN_Cast(DN_UPtr) net;
// NOTE: Setup the request for curl
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
{
DN_NETCurlConn *conn = DN_Cast(DN_NETCurlConn *) request->context[0];
CURL *curl = conn->curl;
curl_easy_setopt(curl, CURLOPT_PRIVATE, request);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, conn->error);
result = DN_NET_SetupRequest_(req, url, method, args, type);
req->response.request = result;
req->context[1] = DN_Cast(DN_UPtr) net;
curl_req->str8_builder = DN_Str8BuilderFromArena(&req->arena);
}
// NOTE: Setup the request for curl API
{
CURL *curl = curl_req->handle;
curl_easy_setopt(curl, CURLOPT_PRIVATE, req);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_req->error);
// NOTE: Perform request and read all response headers before handing
// control back to app.
curl_easy_setopt(curl, CURLOPT_URL, request->url.data);
curl_easy_setopt(curl, CURLOPT_URL, req->url.data);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
// NOTE: Setup response handler
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DN_NET_CurlHTTPCallback_);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, request);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req);
// NOTE: Assign HTTP headers
for (DN_ForItSize(it, DN_Str8, request->args.headers, request->args.headers_size))
conn->curl_slist = curl_slist_append(conn->curl_slist, it.data->data);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, conn->curl_slist);
for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size))
curl_req->slist = curl_slist_append(curl_req->slist, it.data->data);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_req->slist);
// NOTE: Setup handle for protocol
switch (request->type) {
switch (req->type) {
case DN_NETRequestType_Nil: DN_InvalidCodePath; break;
case DN_NETRequestType_WS: {
@ -485,15 +500,15 @@ static DN_NETRequest DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8
DN_Str8 const GET = DN_Str8Lit("GET");
DN_Str8 const POST = DN_Str8Lit("POST");
if (DN_Str8EqInsensitive(request->method, GET)) {
if (DN_Str8EqInsensitive(req->method, GET)) {
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (DN_Str8EqInsensitive(request->method, POST)) {
} else if (DN_Str8EqInsensitive(req->method, POST)) {
curl_easy_setopt(curl, CURLOPT_POST, 1);
if (request->args.payload.size > DN_Gigabytes(2))
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, request->args.payload.size);
if (req->args.payload.size > DN_Gigabytes(2))
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, req->args.payload.size);
else
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request->args.payload.size);
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request->args.payload.data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->args.payload.size);
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, req->args.payload.data);
} else {
DN_InvalidCodePathF("Unimplemented");
}
@ -501,12 +516,12 @@ static DN_NETRequest DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8
}
// NOTE: Handle basic auth
if (request->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (request->args.username.size && request->args.password.size) {
DN_Assert(request->args.username.data[request->args.username.size] == 0);
DN_Assert(request->args.password.data[request->args.password.size] == 0);
curl_easy_setopt(curl, CURLOPT_USERNAME, request->args.username.data);
curl_easy_setopt(curl, CURLOPT_PASSWORD, request->args.password.data);
if (req->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (req->args.username.size && req->args.password.size) {
DN_Assert(req->args.username.data[req->args.username.size] == 0);
DN_Assert(req->args.password.data[req->args.password.size] == 0);
curl_easy_setopt(curl, CURLOPT_USERNAME, req->args.username.data);
curl_easy_setopt(curl, CURLOPT_PASSWORD, req->args.password.data);
}
}
}
@ -517,7 +532,7 @@ static DN_NETRequest DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8
// calling thread. If the calling thread deinitialises this layer before the CURL thread can be
// pre-empted, we can lose track of this request.
for (DN_OS_MutexScope(&curl_core->list_mutex))
DN_DoublyLLAppend(curl_core->request_list, request);
DN_DoublyLLAppend(curl_core->request_list, req);
// NOTE: Enqueue request to go into CURL's ring queue. The CURL thread will sleep and wait for
// bytes to come in for the request and then dump the response into the done list to be consumed
@ -534,37 +549,37 @@ static DN_NETRequest DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8
return result;
}
DN_NETRequest DN_NET_CurlDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
DN_NETRequestHandle DN_NET_CurlDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
{
DN_NETRequest result = DN_NET_CurlDoRequest_(net, url, method, args, DN_NETRequestType_HTTP);
DN_NETRequestHandle result = DN_NET_CurlDoRequest_(net, url, method, args, DN_NETRequestType_HTTP);
return result;
}
DN_NETRequest DN_NET_CurlDoWSArgs(DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args)
DN_NETRequestHandle DN_NET_CurlDoWSArgs(DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args)
{
DN_NETRequest result = DN_NET_CurlDoRequest_(net, url, DN_Str8Lit(""), args, DN_NETRequestType_WS);
DN_NETRequestHandle result = DN_NET_CurlDoRequest_(net, url, DN_Str8Lit(""), args, DN_NETRequestType_WS);
return result;
}
DN_NETRequest DN_NET_CurlDoWS(DN_NETCore *net, DN_Str8 url)
DN_NETRequestHandle DN_NET_CurlDoWS(DN_NETCore *net, DN_Str8 url)
{
DN_NETRequest result = DN_NET_CurlDoWSArgs(net, url, nullptr);
DN_NETRequestHandle result = DN_NET_CurlDoWSArgs(net, url, nullptr);
return result;
}
void DN_NET_CurlDoWSSend(DN_NETRequest request, DN_Str8 payload, DN_NETWSSend send)
void DN_NET_CurlDoWSSend(DN_NETRequestHandle handle, DN_Str8 payload, DN_NETWSSend send)
{
DN_NETRequestInternal *request_ptr = DN_NET_RequestFromHandle(request);
if (!request_ptr)
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
if (!req)
return;
DN_NETCore *net = DN_Cast(DN_NETCore *) request_ptr->context[1];
DN_NETCore *net = DN_NET_CurlNetFromRequest(req);
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_Assert(curl);
DN_NETCurlRingEvent event = {};
event.type = DN_NETCurlRingEventType_SendWS;
event.request = request;
event.request = handle;
event.ws_send_size = payload.size;
event.ws_send = send;
@ -576,16 +591,23 @@ void DN_NET_CurlDoWSSend(DN_NETRequest request, DN_Str8 payload, DN_NETWSSend se
curl_multi_wakeup(curl->thread_curlm);
}
static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN_NETRequest request, DN_NETRequestInternal *request_ptr, DN_Arena *arena)
static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN_NETRequest *req, DN_Arena *arena)
{
// NOTE: Process the response
DN_NETResponse result = DN_NET_MakeResponseFromFinishedRequest_(request, arena);
DN_NET_EndFinishedRequest_(request_ptr);
// NOTE: Generate the response, copy out the strings into the user given memory
DN_NETResponse result = req->response;
{
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
result.body = DN_Str8BuilderBuild(&curl_req->str8_builder, arena);
if (result.error_str8.size)
result.error_str8 = DN_Str8FromStr8Arena(arena, result.error_str8);
curl_req->str8_builder = DN_Str8BuilderFromArena(&req->arena);
}
DN_NET_EndFinishedRequest_(req);
bool continue_ws_request = false;
if (request_ptr->type == DN_NETRequestType_WS &&
request_ptr->response.state != DN_NETResponseState_Error &&
request_ptr->response.state != DN_NETResponseState_WSClose) {
if (req->type == DN_NETRequestType_WS &&
req->response.state != DN_NETResponseState_Error &&
req->response.state != DN_NETResponseState_WSClose) {
continue_ws_request = true;
}
@ -593,23 +615,23 @@ static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN
for (DN_OS_MutexScope(&curl->list_mutex)) {
// NOTE: Dequeue the request, it _must_ have been in the response list at this point for it to
// have ben waitable in the first place.
DN_AssertF(DN_NET_CurlRequestIsInList(curl->response_list, request_ptr),
DN_AssertF(DN_NET_CurlRequestIsInList(curl->response_list, req),
"A completed response should only signal the completion semaphore when it's in the response list");
DN_DoublyLLDetach(curl->response_list, request_ptr);
DN_DoublyLLDetach(curl->response_list, req);
// NOTE: A websocket that is continuing to get data should go back into the request list because
// there's more data to be received. All other requests need to go into the deinit list (so that
// we keep track of it in the time inbetween it takes for the CURL thread to be scheduled and
// release the CURL handle from CURLM and release resources e.t.c.)
if (continue_ws_request)
DN_DoublyLLAppend(curl->request_list, request_ptr);
DN_DoublyLLAppend(curl->request_list, req);
else
DN_DoublyLLAppend(curl->deinit_list, request_ptr);
DN_DoublyLLAppend(curl->deinit_list, req);
}
// NOTE: Submit the post-request event to the CURL thread
DN_NETCurlRingEvent event = {};
event.request = request;
event.request = DN_NET_HandleFromRequest(req);
if (continue_ws_request) {
event.type = DN_NETCurlRingEventType_ReceivedWSReceipt;
} else {
@ -626,18 +648,18 @@ static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN
return result;
}
DN_NETResponse DN_NET_CurlWaitForResponse(DN_NETRequest request, DN_Arena *arena, DN_U32 timeout_ms)
DN_NETResponse DN_NET_CurlWaitForResponse(DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETResponse result = {};
DN_NETRequestInternal *request_ptr = DN_NET_RequestFromHandle(request);
if (!request_ptr)
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
if (!req)
return result;
DN_NETCore *net = DN_Cast(DN_NETCore *) request_ptr->context[1];
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[1];
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_Assert(curl);
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&request_ptr->completion_sem, timeout_ms);
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&req->completion_sem, timeout_ms);
if (wait != DN_OSSemaphoreWaitResult_Success)
return result;
@ -647,7 +669,7 @@ DN_NETResponse DN_NET_CurlWaitForResponse(DN_NETRequest request, DN_Arena *arena
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
// NOTE: Finish handling the response
result = DN_NET_CurlHandleFinishedRequest_(curl, request, request_ptr, arena);
result = DN_NET_CurlHandleFinishedRequest_(curl, req, arena);
return result;
}
@ -657,23 +679,23 @@ DN_NETResponse DN_NET_CurlWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, D
DN_Assert(curl);
DN_NETResponse result = {};
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&net->completion_sem, timeout_ms);
if (wait != DN_OSSemaphoreWaitResult_Success)
DN_OSSemaphoreWaitResult req_wait = DN_OS_SemaphoreWait(&net->completion_sem, timeout_ms);
if (req_wait != DN_OSSemaphoreWaitResult_Success)
return result;
// NOTE: Just grab the handle, handle finished request will dequeue for us
DN_NETRequest request = {};
DN_NETRequestHandle handle = {};
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(curl->response_list);
request = DN_NET_HandleFromRequest(curl->response_list);
handle = DN_NET_HandleFromRequest(curl->response_list);
}
// NOTE: Decrement the request's completion semaphore since the user consumed the global semaphore
DN_NETRequestInternal *request_ptr = DN_NET_RequestFromHandle(request);
DN_OSSemaphoreWaitResult net_wait_result = DN_OS_SemaphoreWait(&request_ptr->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
DN_OSSemaphoreWaitResult net_wait = DN_OS_SemaphoreWait(&req->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait);
// NOTE: Finish handling the response
result = DN_NET_CurlHandleFinishedRequest_(curl, request, request_ptr, arena);
result = DN_NET_CurlHandleFinishedRequest_(curl, req, arena);
return result;
}

View File

@ -6,11 +6,11 @@
DN_NETInterface DN_NET_CurlInterface ();
void DN_NET_CurlInit (DN_NETCore *net, char *base, DN_U64 base_size);
void DN_NET_CurlDeinit (DN_NETCore *net);
DN_NETRequest DN_NET_CurlDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequest DN_NET_CurlDoWSArgs (DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args);
DN_NETRequest DN_NET_CurlDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_CurlDoWSSend (DN_NETRequest request, DN_Str8 payload, DN_NETWSSend send);
DN_NETResponse DN_NET_CurlWaitForResponse (DN_NETRequest request, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETRequestHandle DN_NET_CurlDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWSArgs (DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_CurlDoWSSend (DN_NETRequestHandle handle, DN_Str8 payload, DN_NETWSSend send);
DN_NETResponse DN_NET_CurlWaitForResponse (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse DN_NET_CurlWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // !defined(DN_NET_CURL_H)

View File

@ -1,3 +1,7 @@
#if !defined(__EMSCRIPTEN__)
#error "This file can only be compiled with Emscripten"
#endif
#include <emscripten.h>
#include <emscripten/fetch.h>
#include <emscripten/websocket.h>
@ -15,9 +19,9 @@ struct DN_NETEmcWSEvent
struct DN_NETEmcCore
{
DN_Pool pool;
DN_NETRequestInternal *request_list; // Current requests being executed
DN_NETRequestInternal *response_list; // Responses received that are to be deqeued via wait for response
DN_NETRequestInternal *free_list; // Request pool that new requests will use before allocating
DN_NETRequest *request_list; // Current requests being executed
DN_NETRequest *response_list; // Responses received that are to be deqeued via wait for response
DN_NETRequest *free_list; // Request pool that new requests will use before allocating
};
struct DN_NETEmcRequest
@ -40,7 +44,7 @@ DN_NETInterface DN_NET_EmcInterface()
return result;
}
static DN_NETEmcWSEvent *DN_NET_EmcAllocWSEvent_(DN_NETRequestInternal *request)
static DN_NETEmcWSEvent *DN_NET_EmcAllocWSEvent_(DN_NETRequest *request)
{
// NOTE: Allocate the event and attach to the request
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
@ -55,7 +59,7 @@ static DN_NETEmcWSEvent *DN_NET_EmcAllocWSEvent_(DN_NETRequestInternal *request)
return result;
}
static void DN_NET_EmcOnRequestDone_(DN_NETCore *net, DN_NETRequestInternal *request)
static void DN_NET_EmcOnRequestDone_(DN_NETCore *net, DN_NETRequest *request)
{
// NOTE: This may be call multiple times if we get multiple responses when we yield to the javascript event loop
if (!request->next) {
@ -70,68 +74,67 @@ static void DN_NET_EmcOnRequestDone_(DN_NETCore *net, DN_NETRequestInternal *req
// TODO: Need to enqueue the results since they can accumulate when you yield to the javascript event loop
static bool DN_NET_EmcWSOnOpen(int eventType, EmscriptenWebSocketOpenEvent const *event, void *user_data)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(request);
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_WSOpen;
DN_NET_EmcOnRequestDone_(net, request);
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnMessage(int eventType, const EmscriptenWebSocketMessageEvent *event, void *user_data)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(request);
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = event->isText ? DN_NETResponseState_WSText : DN_NETResponseState_WSBinary;
if (event->numBytes > 0) {
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
net_event->payload = DN_Str8FromPtrPool(&emc->pool, event->data, event->numBytes);
}
DN_NET_EmcOnRequestDone_(net, request);
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnError(int eventType, EmscriptenWebSocketErrorEvent const *event, void *user_data)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(request);
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_Error;
DN_NET_EmcOnRequestDone_(net, request);
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnClose(int eventType, EmscriptenWebSocketCloseEvent const *event, void *user_data)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(request);
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_WSClose;
net_event->payload = DN_Str8FromFmtPool(&emc->pool, "Websocket closed '%.*s': (%u) %s (was %s close)", DN_Str8PrintFmt(request->url), event->code, event->reason, event->wasClean ? "clean" : "unclean");
DN_NET_EmcOnRequestDone_(net, request);
net_event->payload = DN_Str8FromFmtPool(&emc->pool, "Websocket closed '%.*s': (%u) %s (was %s close)", DN_Str8PrintFmt(req->url), event->code, event->reason, event->wasClean ? "clean" : "unclean");
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static void DN_NET_EmcHTTPSuccessCallback(emscripten_fetch_t *fetch)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
request->response.http_status = fetch->status;
request->response.state = DN_NETResponseState_HTTP;
DN_Str8BuilderAppendCopy(&request->response.body, DN_Str8FromPtr(fetch->data, fetch->numBytes - 1));
DN_NET_EmcOnRequestDone_(net, request);
DN_NETRequest *req = DN_Cast(DN_NETRequest *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
req->response.http_status = fetch->status;
req->response.state = DN_NETResponseState_HTTP;
req->response.body = DN_Str8FromStr8Arena(&req->arena, DN_Str8FromPtr(fetch->data, fetch->numBytes - 1));
DN_NET_EmcOnRequestDone_(net, req);
}
static void DN_NET_EmcHTTPFailCallback(emscripten_fetch_t *fetch)
{
DN_NETRequestInternal *request = DN_Cast(DN_NETRequestInternal *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) request->context[0];
request->response.http_status = fetch->status;
request->response.state = DN_NETResponseState_Error;
DN_NET_EmcOnRequestDone_(net, request);
DN_NETRequest *req = DN_Cast(DN_NETRequest *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
req->response.http_status = fetch->status;
req->response.state = DN_NETResponseState_Error;
DN_NET_EmcOnRequestDone_(net, req);
}
static void DN_NET_EmcHTTPProgressCallback(emscripten_fetch_t *fetch)
@ -152,42 +155,42 @@ void DN_NET_EmcDeinit(DN_NETCore *net)
// TODO: Track all the request handles and clean it up
}
DN_NETRequest DN_NET_EmcDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
DN_NETRequestHandle DN_NET_EmcDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
{
// NOTE: Allocate request
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_NETRequestInternal *request = emc->free_list;
if (request) {
DN_NETRequest *req = emc->free_list;
if (req) {
emc->free_list = emc->free_list->next;
request->next = nullptr;
req->next = nullptr;
} else {
request = DN_ArenaNew(&net->arena, DN_NETRequestInternal, DN_ZMem_Yes);
req = DN_ArenaNew(&net->arena, DN_NETRequest, DN_ZMem_Yes);
}
DN_NETRequest result = DN_NET_SetupRequest_(request, url, method, args, DN_NETRequestType_HTTP);
DN_NETRequestHandle result = DN_NET_SetupRequest_(req, url, method, args, DN_NETRequestType_HTTP);
// NOTE: Setup some emscripten specific data into our request context
request->context[0] = DN_Cast(DN_UPtr) net;
req->context[0] = DN_Cast(DN_UPtr) net;
// NOTE: Setup the HTTP request via Emscripten
emscripten_fetch_attr_t fetch_attribs = {};
{
DN_Assert(request->args.payload.data[request->args.payload.size] == 0);
DN_Assert(request->url.data[request->url.size] == 0);
DN_Assert(req->args.payload.data[req->args.payload.size] == 0);
DN_Assert(req->url.data[req->url.size] == 0);
// NOTE: Setup request for emscripten
emscripten_fetch_attr_init(&fetch_attribs);
fetch_attribs.requestData = request->args.payload.data;
fetch_attribs.requestDataSize = request->args.payload.size;
DN_Assert(request->method.size < DN_ArrayCountU(fetch_attribs.requestMethod));
DN_Memcpy(fetch_attribs.requestMethod, request->method.data, request->method.size);
fetch_attribs.requestMethod[request->method.size] = 0;
fetch_attribs.requestData = req->args.payload.data;
fetch_attribs.requestDataSize = req->args.payload.size;
DN_Assert(req->method.size < DN_ArrayCountU(fetch_attribs.requestMethod));
DN_Memcpy(fetch_attribs.requestMethod, req->method.data, req->method.size);
fetch_attribs.requestMethod[req->method.size] = 0;
// NOTE: Assign HTTP headers
if (request->args.headers_size) {
char **headers = DN_ArenaNewArray(&request->arena, char *, request->args.headers_size + 1, DN_ZMem_Yes);
for (DN_ForItSize(it, DN_Str8, request->args.headers, request->args.headers_size)) {
if (req->args.headers_size) {
char **headers = DN_ArenaNewArray(&req->arena, char *, req->args.headers_size + 1, DN_ZMem_Yes);
for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size)) {
DN_Assert(it.data->data[it.data->size] == 0);
headers[it.index] = it.data->data;
}
@ -195,13 +198,13 @@ DN_NETRequest DN_NET_EmcDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_
}
// NOTE: Handle basic auth
if (request->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (request->args.username.size && request->args.password.size) {
DN_Assert(request->args.username.data[request->args.username.size] == 0);
DN_Assert(request->args.password.data[request->args.password.size] == 0);
if (req->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (req->args.username.size && req->args.password.size) {
DN_Assert(req->args.username.data[req->args.username.size] == 0);
DN_Assert(req->args.password.data[req->args.password.size] == 0);
fetch_attribs.withCredentials = true;
fetch_attribs.userName = request->args.username.data;
fetch_attribs.password = request->args.password.data;
fetch_attribs.userName = req->args.username.data;
fetch_attribs.password = req->args.password.data;
}
}
@ -217,64 +220,64 @@ DN_NETRequest DN_NET_EmcDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_
fetch_attribs.onsuccess = DN_NET_EmcHTTPSuccessCallback;
fetch_attribs.onerror = DN_NET_EmcHTTPFailCallback;
fetch_attribs.onprogress = DN_NET_EmcHTTPProgressCallback;
fetch_attribs.userData = request;
fetch_attribs.userData = req;
}
// NOTE: Update the pop to position for the request
request->start_response_arena_pos = DN_ArenaPos(&request->arena);
req->start_response_arena_pos = DN_ArenaPos(&req->arena);
// NOTE: Dispatch the asynchronous fetch
emscripten_fetch(&fetch_attribs, request->url.data);
emscripten_fetch(&fetch_attribs, req->url.data);
return result;
}
DN_NETRequest DN_NET_EmcDoWS(DN_NETCore *net, DN_Str8 url)
DN_NETRequestHandle DN_NET_EmcDoWS(DN_NETCore *net, DN_Str8 url)
{
DN_Assert(emscripten_websocket_is_supported());
// NOTE: Allocate request
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_NETRequestInternal *request = emc->free_list;
if (request) {
DN_NETRequest *req = emc->free_list;
if (req) {
emc->free_list = emc->free_list->next;
request->next = nullptr;
req->next = nullptr;
} else {
request = DN_ArenaNew(&net->arena, DN_NETRequestInternal, DN_ZMem_Yes);
req = DN_ArenaNew(&net->arena, DN_NETRequest, DN_ZMem_Yes);
}
DN_NETRequest result = DN_NET_SetupRequest_(request, url, /*method=*/ DN_Str8Lit(""), /*args=*/nullptr, DN_NETRequestType_WS);
if (!request)
DN_NETRequestHandle result = DN_NET_SetupRequest_(req, url, /*method=*/DN_Str8Lit(""), /*args=*/nullptr, DN_NETRequestType_WS);
if (!req)
return result;
// NOTE: Setup some emscripten specific data into our request context
request->context[0] = DN_Cast(DN_UPtr) net;
request->context[1] = DN_Cast(DN_UPtr) DN_ArenaNew(&request->arena, DN_NETEmcRequest, DN_ZMem_Yes);
request->start_response_arena_pos = DN_ArenaPos(&request->arena);
req->context[0] = DN_Cast(DN_UPtr) net;
req->context[1] = DN_Cast(DN_UPtr) DN_ArenaNew(&req->arena, DN_NETEmcRequest, DN_ZMem_Yes);
req->start_response_arena_pos = DN_ArenaPos(&req->arena);
// NOTE: Create the websocket request and dispatch it via emscripten
EmscriptenWebSocketCreateAttributes attr;
emscripten_websocket_init_create_attributes(&attr);
attr.url = request->url.data;
attr.url = req->url.data;
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) req->context[1];
emc_request->socket = emscripten_websocket_new(&attr);
DN_Assert(emc_request->socket > 0);
emscripten_websocket_set_onopen_callback(emc_request->socket, /*userData=*/ request, DN_NET_EmcWSOnOpen);
emscripten_websocket_set_onmessage_callback(emc_request->socket, /*userData=*/ request, DN_NET_EmcWSOnMessage);
emscripten_websocket_set_onerror_callback(emc_request->socket, /*userData=*/ request, DN_NET_EmcWSOnError);
emscripten_websocket_set_onclose_callback(emc_request->socket, /*userData=*/ request, DN_NET_EmcWSOnClose);
emscripten_websocket_set_onopen_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnOpen);
emscripten_websocket_set_onmessage_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnMessage);
emscripten_websocket_set_onerror_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnError);
emscripten_websocket_set_onclose_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnClose);
return result;
}
void DN_NET_EmcDoWSSend(DN_NETRequest request, DN_Str8 data, DN_NETWSSend send)
void DN_NET_EmcDoWSSend(DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send)
{
DN_AssertF(send == DN_NETWSSend_Binary || send == DN_NETWSSend_Text || send == DN_NETWSSend_Close,
"Unimplemented, Emscripten only supports some of the available operations");
int result = 0;
DN_NETRequestInternal *request_ptr = DN_Cast(DN_NETRequestInternal *) request.handle;
if (request_ptr && request_ptr->gen == request.gen) {
DN_NETRequest *request_ptr = DN_Cast(DN_NETRequest *) handle.handle;
if (request_ptr && request_ptr->gen == handle.gen) {
DN_Assert(request_ptr->type == DN_NETRequestType_WS);
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request_ptr->context[1];
switch (send) {
@ -299,15 +302,16 @@ void DN_NET_EmcDoWSSend(DN_NETRequest request, DN_Str8 data, DN_NETWSSend send)
(void)result;
}
static DN_NETResponse DN_NET_EmcHandleFinishedRequest_(DN_NETCore *net, DN_NETEmcCore *emc, DN_NETRequest request, DN_NETRequestInternal *request_ptr, DN_Arena *arena)
static DN_NETResponse DN_NET_EmcHandleFinishedRequest_(DN_NETCore *net, DN_NETEmcCore *emc, DN_NETRequestHandle handle, DN_NETRequest *request, DN_Arena *arena)
{
DN_NETResponse result = {};
// NOTE: Generate the response, copy out the strings into the user given memory
DN_NETResponse result = request->response;
bool end_request = true;
if (request_ptr->type == DN_NETRequestType_HTTP) {
result = DN_NET_MakeResponseFromFinishedRequest_(request, arena);
if (request->type == DN_NETRequestType_HTTP) {
result.body = DN_Str8FromStr8Arena(arena, result.body);
} else {
// NOTE: Get emscripten contexts
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request_ptr->context[1];
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
DN_NETEmcWSEvent *emc_event = emc_request->first_event;
emc_request->first_event = emc_event->next; // Advance the list pointer
DN_Assert(emc_event);
@ -316,7 +320,7 @@ static DN_NETResponse DN_NET_EmcHandleFinishedRequest_(DN_NETCore *net, DN_NETEm
// NOTE: Build the result
result.state = emc_event->state;
result.request = request;
result.request = handle;
result.body = DN_Str8FromStr8Arena(arena, emc_event->payload);
@ -331,17 +335,15 @@ static DN_NETResponse DN_NET_EmcHandleFinishedRequest_(DN_NETCore *net, DN_NETEm
}
// NOTE: Deallocate the memory used in the request and reset the string builder
DN_ArenaPopTo(&request_ptr->arena, 0);
request_ptr->response.body = DN_Str8BuilderFromArena(&request_ptr->arena);
DN_ArenaPopTo(&request->arena, 0);
if (end_request) {
DN_NET_EndFinishedRequest_(request_ptr);
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request_ptr->context[1];
DN_NET_EndFinishedRequest_(request);
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
emscripten_websocket_delete(emc_request->socket);
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
request_ptr->next = emc->free_list;
emc->free_list = request_ptr;
request->next = emc->free_list;
emc->free_list = request;
}
return result;
@ -375,11 +377,11 @@ static DN_OSSemaphoreWaitResult DN_NET_EmcSemaphoreWait_(DN_OSSemaphore *sem, DN
return result;
}
DN_NETResponse DN_NET_EmcWaitForResponse(DN_NETRequest request, DN_Arena *arena, DN_U32 timeout_ms)
DN_NETResponse DN_NET_EmcWaitForResponse(DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETResponse result = {};
DN_NETRequestInternal *request_ptr = DN_Cast(DN_NETRequestInternal *) request.handle;
if (request_ptr && request_ptr->gen == request.gen) {
DN_NETRequest *request_ptr = DN_Cast(DN_NETRequest *) handle.handle;
if (request_ptr && request_ptr->gen == handle.gen) {
DN_NETCore *net = DN_Cast(DN_NETCore *) request_ptr->context[0];
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_Assert(emc);
@ -390,7 +392,7 @@ DN_NETResponse DN_NET_EmcWaitForResponse(DN_NETRequest request, DN_Arena *arena,
// NOTE: Remove request from the done list
request_ptr->next = nullptr;
emc->response_list = emc->response_list->next;
result = DN_NET_EmcHandleFinishedRequest_(net, emc, request, request_ptr, arena);
result = DN_NET_EmcHandleFinishedRequest_(net, emc, handle, request_ptr, arena);
// NOTE: Decrement the global 'request done' completion semaphore since the user consumed the
// request individually.
@ -414,7 +416,7 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN
DN_AssertF(emc->response_list,
"This should be set otherwise we bumped the completion sem without queueing into the "
"done list or we forgot to wait on the global semaphore after a request finished");
DN_NETRequestInternal *request_ptr = emc->response_list;
DN_NETRequest *request_ptr = emc->response_list;
DN_Assert(request_ptr == emc->response_list);
request_ptr->next = nullptr;
emc->response_list = emc->response_list->next;
@ -423,7 +425,7 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN
DN_OSSemaphoreWaitResult net_wait_result = DN_OS_SemaphoreWait(&request_ptr->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
DN_NETRequest request = {};
DN_NETRequestHandle request = {};
request.handle = DN_Cast(DN_UPtr) request_ptr;
request.gen = request_ptr->gen;
result = DN_NET_EmcHandleFinishedRequest_(net, emc, request, request_ptr, arena);

View File

@ -6,10 +6,10 @@
DN_NETInterface DN_NET_EmcInterface();
void DN_NET_EmcInit (DN_NETCore *net, char *base, DN_U64 base_size);
void DN_NET_EmcDeinit (DN_NETCore *net);
DN_NETRequest DN_NET_EmcDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequest DN_NET_EmcDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_EmcDoWSSend (DN_NETRequest request, DN_Str8 data, DN_NETWSSend send);
DN_NETResponse DN_NET_EmcWaitForResponse (DN_NETRequest request, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETRequestHandle DN_NET_EmcDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_EmcDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_EmcDoWSSend (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
DN_NETResponse DN_NET_EmcWaitForResponse (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // DN_NET_EMSCRIPTEN_H

View File

@ -2513,7 +2513,7 @@ static DN_UTCore DN_Tests_Net()
DN_U64 arena_reset_p = DN_ArenaPos(&arena);
for (DN_UT_Test(&result, "%.*s WaitForResponse HTTP GET request", DN_Str8PrintFmt(label))) {
DN_NETRequest request = net_interface.do_http(&net, remote_http_server_url, DN_Str8Lit("GET"), nullptr);
DN_NETRequestHandle request = net_interface.do_http(&net, remote_http_server_url, DN_Str8Lit("GET"), nullptr);
DN_NETResponse response = net_interface.wait_for_response(request, &arena, UINT32_MAX);
DN_UT_AssertF(&result, response.http_status == 200, "http_status=%u", response.http_status);
DN_UT_AssertF(&result, response.state == DN_NETResponseState_HTTP, "state=%u", response.state);
@ -2531,7 +2531,7 @@ static DN_UTCore DN_Tests_Net()
}
for (DN_UT_Test(&result, "%.*s WaitForResponse WS request", DN_Str8PrintFmt(label))) {
DN_NETRequest request = net_interface.do_ws(&net, remote_ws_server_url);
DN_NETRequestHandle request = net_interface.do_ws(&net, remote_ws_server_url);
DN_USize const WS_TIMEOUT_MS = 16;
// NOTE: Wait for WS connection to open