Skip to content

Commit

Permalink
Add native CURL code for emscripten
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugh Sanderson committed Sep 18, 2024
1 parent 8dc898a commit 4a21a19
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 88 deletions.
2 changes: 2 additions & 0 deletions include.nmml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

<haxedef name="nme_api_level=611"/>

<haxedef name="no_haxe_http" if="wasm" />

<section if="NME_HXTELEMETRY || telemetry">
<haxelib name="hxtelemetry"/>
<haxedef name="HXCPP_TELEMETRY"/>
Expand Down
37 changes: 25 additions & 12 deletions project/ToolkitBuild.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,25 @@
<!-- For systems that do not create the egl context for us -->
<set name="NME_EGL" value="1" if="rpi" unless="sdl_rpi" />

<!-- Do we need to implement curl in NME -->
<set name="NME_CURL" value="1" />
<unset name="NME_CURL" if="emscripten||winrt||gcw0||NME_LOCAL_TOOLKIT" />
<!-- It will fallback to using haxe http, which can handle https, without libCurl -->
<!--<set name="NME_CURL" value="1" />-->


<!-- Use native browser API. This will check CORS however. -->
<set name="NME_CURL_NATIVE" value="1" if="emscripten" />

<set name="NME_CURL" value="1" if="NME_CURL_NATIVE" />

<unset name="NME_CURL" if="winrt||gcw0||NME_LOCAL_TOOLKIT||NME_NO_CURL" unless="NME_CURL_NATIVE" />

<set name="NME_CURL_TOOLKIT" value="1" if="NME_CURL" unless="NME_CURL_NATIVE" />

<!-- And allow https ? -->
<set name="NME_SSL" value="1" />
<set name="NME_SSL_EXTRA" value="_ssl" if="NME_SSL"/>
<set name="NATIVE_TOOLKIT_CURL_SSL" value="1" if="NME_SSL" />
<section if="NME_CURL_TOOLKIT" >
<set name="NME_SSL" value="1" />
<set name="NME_SSL_EXTRA" value="_ssl" if="NME_SSL"/>
<set name="NATIVE_TOOLKIT_CURL_SSL" value="1" if="NME_SSL" />
</section>

<!--<setenv name="MACOSX_DEPLOYMENT_TARGET" value="10.12" if="NME_SDL3" />-->
<!--
Expand Down Expand Up @@ -176,7 +187,7 @@
<set name="NME_LIBSDL_MIXER" value="${NATIVE_TOOLKIT_PATH}/sdl-mixer/" />
<include name="${NME_LIBSDL_MIXER}files.xml" if="NME_MIXER" />

<include name="${NATIVE_TOOLKIT_PATH}/curl/files.xml" if="NME_CURL" />
<include name="${NATIVE_TOOLKIT_PATH}/curl/files.xml" if="NME_CURL_TOOLKIT" />
</section>


Expand Down Expand Up @@ -340,6 +351,7 @@
<section if="emscripten">
<compilerflag value="-sUSE_SDL=1"/>
<file name="${SRC_DIR}/emscripten/System.cpp" />
<file name="${SRC_DIR}/emscripten/CurlFetch.cpp" />
<file name="${SRC_DIR}/emscripten/JsPrime.cpp" if="HXCPP_JS_PRIME" />

<file name="${SRC_DIR}/emscripten/_sans.cpp" />
Expand Down Expand Up @@ -373,15 +385,15 @@
<compilerflag value="-I${NME_FREETYPE}include/freetype"/>

<compilerflag value="-I${NME_MODPLUG}" if="modplug" />
<compilerflag value="-I${NATIVE_TOOLKIT_PATH}/curl/include" if="NME_CURL" />
<compilerflag value="-I${NATIVE_TOOLKIT_PATH}/curl/include" if="NME_CURL_TOOLKIT" />

<depend files="native-toolkit-freetype-depends" />
<depend files="native-toolkit-jpeg-depends" />
<depend files="native-toolkit-png-depends" />
<depend files="native-toolkit-vorbis-depends" />
<depend files="native-toolkit-ogg-depends" />
<depend files="native-toolkit-modplug-depends" if="modplug" />
<depend files="native-toolkit-curl-depends" if="NME_CURL" />
<depend files="native-toolkit-curl-depends" if="NME_CURL_TOOLKIT" />

<section if="NME_NATIVE_SDL_SYSTEM" unless="NME_SDL2" >
<compilerflag value="-I/opt/local/include" if="macos" />
Expand Down Expand Up @@ -414,7 +426,7 @@
</section>

<compilerflag value="-DNME_CURL" if="NME_CURL"/>
<section if="NME_CURL">
<section if="NME_CURL" unless="NME_CURL_NATIVE" >
<compilerflag value="-DNME_CURL_SSL" if="NME_SSL"/>
</section>

Expand Down Expand Up @@ -472,7 +484,7 @@
<file name="${SRC_DIR}/common/Input.cpp" unless="iphone" />
<file name="${SRC_DIR}/common/SurfaceIO.cpp" unless="sdl_image" />
<file name="${SRC_DIR}/common/ManagedStage.cpp" unless="winrt" />
<file name="${SRC_DIR}/common/CURL.cpp" if="NME_CURL"/>
<file name="${SRC_DIR}/common/CURL.cpp" if="NME_CURL_TOOLKIT"/>
<file name="${SRC_DIR}/common/Thread.cpp"/>
<file name="${SRC_DIR}/common/Camera.cpp" if="NME_CAMERA" />
<file name="${SRC_DIR}/common/ObjectStream.cpp" />
Expand Down Expand Up @@ -596,7 +608,7 @@
<files id="native-toolkit-zlib" unless="NME_LOCAL_TOOLKIT" />
<files id="hxcpp_zlib" if="NME_LOCAL_TOOLKIT" />
<files id="native-toolkit-angle" if="NME_ANGLE" />
<section if="NME_CURL">
<section if="NME_CURL_TOOLKIT">
<files id="native-toolkit-curl" />
</section>

Expand Down Expand Up @@ -655,6 +667,7 @@

<section if="HXCPP_JS_PRIME || emscripten">
<vflag name="-s" value="EXPORTED_FUNCTIONS=['_malloc','_free','_main','ccall']" />
<vflag name="-s" value="FETCH=1" />
</section>

<section if="android">
Expand Down
2 changes: 1 addition & 1 deletion project/src/common/ExternalInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ void ToValue(value &outVal,const ColorTransform &inTrans)



#ifndef EMSCRIPTEN
#ifdef NME_CURL
void FromValue(value obj, URLRequest &request)
{
request.url = val_string( val_field(obj, _id_url) );
Expand Down
259 changes: 259 additions & 0 deletions project/src/emscripten/CurlFetch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
#include <URL.h>
#include <Utils.h>
#include <map>
#include <string>

#include "emscripten.h"
#include "emscripten/fetch.h"
#include <sys/stat.h>

namespace nme
{

static int sgAvtiveCount = 0;

static void onsuccess(struct emscripten_fetch_t *fetch);
static void onerror(struct emscripten_fetch_t *fetch);
static void onprogress(struct emscripten_fetch_t *fetch);
static void onreadystatechange(struct emscripten_fetch_t *fetch);

static const bool allowUserAgent = false;

class CurlFetch : public URLLoader
{
emscripten_fetch_t *fetch;
URLState state;
ByteArray data;
std::string err;
int status;
std::vector<char> postData;

public:
CurlFetch(URLRequest &request)
{
state = urlInit;
status = 400;
fetch = nullptr;

emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
attr.userData = this;
attr.onsuccess = onsuccess;
attr.onprogress = onprogress;
attr.onerror = onerror;
attr.onreadystatechange = onreadystatechange;
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;

strcpy(attr.requestMethod, request.method);

if (request.postData.Size()>0)
{
postData.resize(request.postData.Size());
memcpy(&postData[0],request.postData.Bytes(), request.postData.Size());
attr.requestData = &postData[0];
attr.requestDataSize = postData.size();
}

std::string user;
std::string pass;
if (request.credentials && request.credentials[0])
{
std::string creds(request.credentials);
auto col = creds.find(":");
if (col!=std::string::npos)
{
user = creds.substr(0,col);
pass = creds.substr(col+1);
attr.userName = user.c_str();
attr.password = pass.c_str();
}
}
std::vector<const char *> allHeaders;
bool seenCookie = false;
bool seenAgent = false;
bool seenContent = false;
for(int i=0;i<request.headers.size();i++)
{
URLRequestHeader &h = request.headers[i];
const char *value = h.value;
if (!strcmp(h.name,"User-Agent"))
{
if (allowUserAgent)
{
seenAgent = true;
if (request.userAgent && request.userAgent[0])
value = request.userAgent;
}
}
else if (!strcmp(h.name,"Cookie"))
{
seenCookie = true;
if (request.cookies && request.cookies[0])
value = request.cookies;
}
else if (!strcmp(h.name,"Content-Type"))
{
seenContent = true;
if (request.contentType && request.contentType[0])
value = request.contentType;
}

allHeaders.push_back(h.name);
allHeaders.push_back(value);
}
if (allowUserAgent && !seenAgent && request.userAgent && request.userAgent[0])
{
allHeaders.push_back("User-Agent");
allHeaders.push_back(request.userAgent);
}
if (!seenCookie && request.cookies && request.cookies[0])
{
allHeaders.push_back("Cookie");
allHeaders.push_back(request.cookies);
}
if (!seenContent && request.contentType && request.contentType[0])
{
allHeaders.push_back("Content-Type");
allHeaders.push_back(request.contentType);
}

if (allHeaders.size())
{
allHeaders.push_back(nullptr);
attr.requestHeaders = &allHeaders[0];
}


fetch = emscripten_fetch(&attr, request.url);

state = urlLoading;
}

~CurlFetch()
{
close();
}

void onSuccess()
{
if (fetch)
{
int len = (int)fetch->numBytes;
if (len)
{
data = ByteArray(len);
memcpy(data.Bytes(), fetch->data, len);
}
status = fetch->status;
}
state = urlComplete;
}
void onError()
{
if (fetch)
{
status = fetch->status;
err = fetch->statusText;
}
close();
state = urlError;
}
void onProgress() { }

void onReadyStateChanged()
{
}


void close()
{
if (fetch)
{
emscripten_fetch_close(fetch);
fetch = nullptr;
}
}
URLState getState()
{
return state;
}

int bytesLoaded()
{
if (fetch)
return (int)fetch->dataOffset;
return 0;
}
int bytesTotal()
{
if (fetch)
return (int)fetch->totalBytes;
return 0;
}
int getHttpCode()
{
return status;
}
const char *getErrorMessage()
{
return err.c_str();
}

ByteArray releaseData()
{
return data;
}

void getCookies( std::vector<std::string> &outCookies )
{
}

void getResponseHeaders( std::vector<std::string> &outHeaders )
{
if (fetch)
{
size_t len = emscripten_fetch_get_response_headers_length(fetch);
if (len)
{
std::vector<char> buf(len);
emscripten_fetch_get_response_headers(fetch, &buf[0], len);
char **headers = emscripten_fetch_unpack_response_headers(&buf[0]);
std::string sep(": ");
for(char **h = headers; *h; h+=2)
outHeaders.push_back( h[0] + sep + h[1] );
emscripten_fetch_free_unpacked_response_headers(headers);
}
}
}

};

static void onsuccess(struct emscripten_fetch_t *fetch) {
((CurlFetch *)fetch->userData)->onSuccess();
}
static void onerror(struct emscripten_fetch_t *fetch) {
((CurlFetch *)fetch->userData)->onError();
}
static void onprogress(struct emscripten_fetch_t *fetch) {
((CurlFetch *)fetch->userData)->onProgress();
}
static void onreadystatechange(struct emscripten_fetch_t *fetch) {
((CurlFetch *)fetch->userData)->onReadyStateChanged();
}



URLLoader *URLLoader::create(URLRequest &inRequest)
{
return new CurlFetch(inRequest);
}

bool URLLoader::processAll()
{
return sgAvtiveCount > 0;
}
void URLLoader::initialize(const char *inCACertFilePath)
{
}

} // End namespace nme
Loading

0 comments on commit 4a21a19

Please sign in to comment.