Skip to content

Commit

Permalink
added libuv support for the future use
Browse files Browse the repository at this point in the history
  • Loading branch information
nesbox committed Jan 22, 2021
1 parent 41c4655 commit be5055f
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Expand Up @@ -49,3 +49,9 @@
[submodule "vendor/circle-stdlib"]
path = vendor/circle-stdlib
url = https://github.com/smuehlst/circle-stdlib.git
[submodule "vendor/libuv"]
path = vendor/libuv
url = https://github.com/libuv/libuv.git
[submodule "vendor/http-parser"]
path = vendor/http-parser
url = https://github.com/nodejs/http-parser.git
22 changes: 22 additions & 0 deletions CMakeLists.txt
Expand Up @@ -590,6 +590,23 @@ if (USE_CURL)

endif()

################################
# libuv
################################

if(USE_LIBUV)
add_subdirectory(${THIRDPARTY_DIR}/libuv)
endif()

################################
# HTTP parser
################################

if(USE_LIBUV)
add_library(http_parser STATIC ${THIRDPARTY_DIR}/http-parser/http_parser.c)
target_include_directories(http_parser INTERFACE ${THIRDPARTY_DIR}/http-parser)
endif()

################################
# TIC-80 studio
################################
Expand Down Expand Up @@ -634,6 +651,11 @@ if(USE_CURL)
target_link_libraries(tic80studio libcurl)
endif()

if(USE_LIBUV)
target_compile_definitions(tic80studio PRIVATE USE_LIBUV)
target_link_libraries(tic80studio uv_a http_parser)
endif()

if(BUILD_PRO)
target_compile_definitions(tic80studio PRIVATE TIC80_PRO)
endif()
Expand Down
2 changes: 1 addition & 1 deletion build/android/app/build.gradle
Expand Up @@ -4,7 +4,7 @@ android {
compileSdkVersion 26
defaultConfig {
applicationId 'com.nesbox.tic'
minSdkVersion 16
minSdkVersion 21
targetSdkVersion 26
versionCode 9000
versionName '0.90.00'
Expand Down
2 changes: 1 addition & 1 deletion build/android/app/src/main/AndroidManifest.xml
Expand Up @@ -8,7 +8,7 @@
android:versionName="0.90.00"
android:installLocation="auto">

<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26" />

<!-- OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00020000" />
Expand Down
212 changes: 212 additions & 0 deletions src/studio/net.c
Expand Up @@ -379,6 +379,218 @@ void netClose(Net* net) {}
void netTickStart(Net *net) {}
void netTickEnd(Net *net) {}

#elif defined(USE_LIBUV)

#include "defines.h"

#include <uv.h>
#include <http_parser.h>

struct Net
{
const char* host;
char* path;

HttpGetCallback callback;
void* calldata;

uv_tcp_t tcp;
http_parser parser;

struct
{
u8* data;
s32 size;
s32 total;
} content;
};

static s32 onBody(http_parser* parser, const char *at, size_t length)
{
Net* net = parser->data;

net->content.data = realloc(net->content.data, net->content.size + length);
memcpy(net->content.data + net->content.size, at, length);

net->content.size += length;

net->callback(&(HttpGetData)
{
.calldata = net->calldata,
.type = HttpGetProgress,
.progress = {net->content.size, net->content.total},
.url = net->path
});

return 0;
}

static s32 onMessageComplete(http_parser* parser)
{
Net* net = parser->data;

if (parser->status_code == HTTP_STATUS_OK)
{
net->callback(&(HttpGetData)
{
.calldata = net->calldata,
.type = HttpGetDone,
.done = { .data = net->content.data, .size = net->content.size },
.url = net->path
});

free(net->content.data);
free(net->path);
}

uv_close((uv_handle_t*)&net->tcp, NULL);

return 0;
}

static s32 onHeadersComplete(http_parser* parser)
{
Net* net = parser->data;

bool hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX);

ZEROMEM(net->content);
net->content.total = parser->content_length;

// !TODO: handle HTTP_STATUS_MOVED_PERMANENTLY here
if (!hasBody || parser->status_code != HTTP_STATUS_OK)
return 1;

return 0;
}

static s32 onStatus(http_parser* parser, const char* at, size_t length)
{
return parser->status_code != HTTP_STATUS_OK;
}

static void onError(Net* net, s32 code)
{
net->callback(&(HttpGetData)
{
.calldata = net->calldata,
.type = HttpGetError,
.error = { .code = code }
});

uv_close((uv_handle_t*)&net->tcp, NULL);
free(net->path);
}

static void allocBuffer(uv_handle_t *handle, size_t size, uv_buf_t *buf)
{
buf->base = malloc(size);
buf->len = size;
}

static void onResponse(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
Net* net = stream->data;

if(nread > 0)
{
static const http_parser_settings ParserSettings =
{
.on_status = onStatus,
.on_body = onBody,
.on_message_complete = onMessageComplete,
.on_headers_complete = onHeadersComplete,
};

s32 parsed = http_parser_execute(&net->parser, &ParserSettings, buf->base, nread);

if(parsed != nread)
onError(net, net->parser.status_code);

free(buf->base);
}
else onError(net, 0);
}

static void onHeaderSent(uv_write_t *write, s32 status)
{
Net* net = write->data;
http_parser_init(&net->parser, HTTP_RESPONSE);
net->parser.data = net;


uv_stream_t* handle = write->handle;
free(write);

handle->data = net;
uv_read_start(handle, allocBuffer, onResponse);
}

static void onConnect(uv_connect_t *req, s32 status)
{
Net* net = req->data;

char httpReq[2048];
snprintf(httpReq, sizeof httpReq, "GET %s HTTP/1.1\nHost: %s\n\n", net->path, net->host);

uv_buf_t http = uv_buf_init(httpReq, strlen(httpReq));
uv_write(OBJCOPY((uv_write_t){.data = net}), req->handle, &http, 1, onHeaderSent);

free(req);
}

static void onResolved(uv_getaddrinfo_t *resolver, s32 status, struct addrinfo *res)
{
Net* net = resolver->data;

if (res)
{
uv_tcp_connect(OBJCOPY((uv_connect_t){.data = net}), &net->tcp, res->ai_addr, onConnect);
uv_freeaddrinfo(res);
}
else onError(net, 0);

free(resolver);
}

void netGet(Net* net, const char* path, HttpGetCallback callback, void* calldata)
{
uv_loop_t* loop = uv_default_loop();

net->callback = callback;
net->calldata = calldata;
net->path = strdup(path);

uv_tcp_init(loop, &net->tcp);
uv_getaddrinfo(loop, OBJCOPY((uv_getaddrinfo_t){.data = net}), onResolved, net->host, "80", NULL);
}

void netTickStart(Net *net)
{
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
}

void netTickEnd(Net *net) {}

Net* netCreate(const char* host)
{
Net* net = (Net*)malloc(sizeof(Net));
memset(net, 0, sizeof(Net));

net->host = host;

static const char Http[] = "http://";
if(strstr(host, Http) == host)
net->host += sizeof Http - 1;

return net;
}

void netClose(Net* net)
{
free(net);
}

#else

#include <curl/curl.h>
Expand Down
1 change: 1 addition & 0 deletions vendor/http-parser
Submodule http-parser added at ec8b5e
1 change: 1 addition & 0 deletions vendor/libuv
Submodule libuv added at 9c3d69

0 comments on commit be5055f

Please sign in to comment.