Allow cancellation of HTTP requests

`NETWORK::HttpRequest()` now returns a HttpRequestFuture with a
`Cancel()` method.
This commit is contained in:
Martin Natano
2022-01-15 22:56:22 +01:00
parent 134fa9a3d9
commit bb826dc0b2
4 changed files with 74 additions and 7 deletions
+4
View File
@@ -2051,6 +2051,9 @@
<Function name='GetHeld'/>
<Function name='GetActive'/>
</Class>
<Class name='HttpRequestFuture'>
<Function name='Cancel'/>
</Class>
<Class base='ActorFrame' name='TextBanner'>
<Function name='Load'/>
<Function name='SetFromSong'/>
@@ -2498,6 +2501,7 @@
<EnumValue name='&apos;HttpErrorCode_TooManyRedirects&apos;' value='13'/>
<EnumValue name='&apos;HttpErrorCode_ChunkReadError&apos;' value='14'/>
<EnumValue name='&apos;HttpErrorCode_CannotReadBody&apos;' value='15'/>
<EnumValue name='&apos;HttpErrorCode_Cancelled&apos;' value='16'/>
</Enum>
<Enum name='ImageCacheMode'>
<EnumValue name='&apos;ImageCacheMode_Off&apos;' value='0'/>
+7 -1
View File
@@ -3560,7 +3560,7 @@ end
<Function name='IsUrlAllowed' return='bool' arguments='string url'>
Returns true if access to <code>url</code> is allowed.
</Function>
<Function name='HttpRequest' return='void' arguments='table params'>
<Function name='HttpRequest' return='HttpRequestFuture' arguments='table params'>
Performs an HTTP request.<br />
Usage example:
<pre><code>
@@ -6210,6 +6210,12 @@ local spr = Def.Sprite{
Returns true if the note was initiated.
</Function>
</Class>
<Class name='HttpRequestFuture'>
<Function name='Cancel' return='void' arguments=''>
Cancels the running HTTP request. Does nothing if the request
has already completed.
</Function>
</Class>
<Class name='TextBanner' grouping='Actor'>
<Function name='Load' return='void' arguments='string sMetricsGroup'>
Loads the TextBanner from the specified metrics group.
+46 -5
View File
@@ -10,6 +10,7 @@
#include <ixwebsocket/IXUrlParser.h>
#include <algorithm>
#include <memory>
#include <unordered_map>
#include <vector>
@@ -94,7 +95,7 @@ bool NetworkManager::IsUrlAllowed(const std::string& url)
return std::find(allowedHosts.begin(), allowedHosts.end(), host) != allowedHosts.end();
}
void NetworkManager::HttpRequest(const HttpRequestArgs& args)
HttpRequestFuturePtr NetworkManager::HttpRequest(const HttpRequestArgs& args)
{
ix::HttpRequestArgsPtr req = this->httpClient.createRequest(args.url, args.method);
req->body = args.body;
@@ -114,6 +115,8 @@ void NetworkManager::HttpRequest(const HttpRequestArgs& args)
}
this->httpClient.performRequest(req, args.onResponse);
return std::make_shared<HttpRequestFuture>(req);
}
std::string NetworkManager::UrlEncode(const std::string& value)
@@ -126,10 +129,43 @@ std::string NetworkManager::EncodeQueryParameters(const std::unordered_map<std::
return this->httpClient.serializeHttpParameters(query);
}
int HttpRequestFuture::gc(lua_State *L)
{
void *udata = luaL_checkudata(L, 1, "HttpRequestFuture");
auto futptr = static_cast<HttpRequestFuturePtr*>(udata);
futptr->~shared_ptr();
return 0;
}
int HttpRequestFuture::Cancel(lua_State *L)
{
void *udata = luaL_checkudata(L, 1, "HttpRequestFuture");
auto fut = *static_cast<HttpRequestFuturePtr*>(udata);
fut->args->cancel = true;
return 0;
}
// lua start
#include "LuaBinding.h"
static void registerHttpRequestMetatable(lua_State *L)
{
const luaL_Reg HttpRequest_meta[] = {
{"__gc", HttpRequestFuture::gc},
{"Cancel", HttpRequestFuture::Cancel},
{NULL, NULL},
};
luaL_newmetatable(L, "HttpRequestFuture");
luaL_register(L, NULL, HttpRequest_meta);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
REGISTER_WITH_LUA_FUNCTION(registerHttpRequestMetatable)
/** @brief Allow Lua to have access to the NetworkManager. */
class LunaNetworkManager: public Luna<NetworkManager>
{
@@ -302,7 +338,13 @@ public:
if (p->IsUrlAllowed(args.url))
{
p->HttpRequest(args);
auto fut = p->HttpRequest(args);
void *vp = lua_newuserdata(L, sizeof(std::shared_ptr<HttpRequestFuture>));
new(vp) std::shared_ptr<::HttpRequestFuture>(fut);
luaL_getmetatable(L, "HttpRequestFuture");
lua_setmetatable(L, -2);
return 1;
}
else
{
@@ -311,10 +353,8 @@ public:
{
handleUrlForbidden(L, args.url, onResponseRef);
}
return 0;
}
lua_pushnil(L);
return 1;
}
static int UrlEncode(T* p, lua_State *L)
@@ -415,6 +455,7 @@ private:
case ix::HttpErrorCode::TooManyRedirects: LuaHelpers::Push(L, HttpErrorCode_TooManyRedirects); break;
case ix::HttpErrorCode::ChunkReadError: LuaHelpers::Push(L, HttpErrorCode_ChunkReadError); break;
case ix::HttpErrorCode::CannotReadBody: LuaHelpers::Push(L, HttpErrorCode_CannotReadBody); break;
case ix::HttpErrorCode::Cancelled: LuaHelpers::Push(L, HttpErrorCode_Cancelled); break;
default: LuaHelpers::Push(L, HttpErrorCode_UnknownError); break;
}
lua_setfield(L, -2, "error");
+17 -1
View File
@@ -5,6 +5,7 @@
#include "StdString.h"
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
@@ -36,6 +37,7 @@ enum HttpErrorCode
HttpErrorCode_TooManyRedirects,
HttpErrorCode_ChunkReadError,
HttpErrorCode_CannotReadBody,
HttpErrorCode_Cancelled,
NUM_HttpErrorCode,
HttpErrorCode_Invalid,
@@ -56,6 +58,20 @@ struct HttpRequestArgs
std::function<void(const ix::HttpResponsePtr& response)> onResponse = [](const ix::HttpResponsePtr& response) {};
};
class HttpRequestFuture
{
public:
HttpRequestFuture(ix::HttpRequestArgsPtr& args) : args(args) {};
static int gc(lua_State *L);
static int Cancel(lua_State *L);
private:
ix::HttpRequestArgsPtr args;
};
typedef std::shared_ptr<HttpRequestFuture> HttpRequestFuturePtr;
class NetworkManager
{
public:
@@ -63,7 +79,7 @@ public:
~NetworkManager();
bool IsUrlAllowed(const std::string& url);
void HttpRequest(const HttpRequestArgs& args);
HttpRequestFuturePtr HttpRequest(const HttpRequestArgs& args);
std::string UrlEncode(const std::string& value);
std::string EncodeQueryParameters(const std::unordered_map<std::string, std::string>& query);