Add HTTP API for Lua

Also fix a bug with Requests where any connection that took
longer to finish than 15 seconds would be killed. Should have
used CURLOPT_CONNECTTIMEOUT instead of CURLOPT_TIMEOUT when
specifying the timeout, oops.
This commit is contained in:
Tamás Bálint Misius 2020-01-16 16:35:18 +01:00
parent 4d52531889
commit fe87203eb4
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
3 changed files with 207 additions and 2 deletions

View File

@ -201,7 +201,7 @@ namespace http
curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer); curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer);
error_buffer[0] = 0; error_buffer[0] = 0;
curl_easy_setopt(easy, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, timeout);
curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(easy, CURLOPT_URL, uri.c_str()); curl_easy_setopt(easy, CURLOPT_URL, uri.c_str());

View File

@ -44,6 +44,7 @@
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/http/Request.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
@ -152,6 +153,9 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m):
initFileSystemAPI(); initFileSystemAPI();
initPlatformAPI(); initPlatformAPI();
initEventAPI(); initEventAPI();
#ifndef NOHTTP
initHttpAPI();
#endif
//Old TPT API //Old TPT API
int currentElementMeta, currentElement; int currentElementMeta, currentElement;
@ -3686,6 +3690,204 @@ int LuaScriptInterface::event_getmodifiers(lua_State * l)
return 1; return 1;
} }
class RequestHandle
{
http::Request *request;
bool dead;
public:
RequestHandle(ByteString &uri, std::map<ByteString, ByteString> &post_data, std::map<ByteString, ByteString> &headers)
{
dead = false;
request = new http::Request(uri);
for (auto &header : headers)
{
request->AddHeader(header.first, header.second);
}
request->AddPostData(post_data);
request->Start();
}
~RequestHandle()
{
if (!Dead())
{
Cancel();
}
}
bool Dead() const
{
return dead;
}
bool Done() const
{
return dead || request->CheckDone();
}
bool Running() const
{
return !dead && request->CheckStarted();
}
void Progress(int *total, int *done)
{
if (!dead)
{
request->CheckProgress(total, done);
}
}
void Cancel()
{
if (!dead)
{
request->Cancel();
dead = true;
}
}
ByteString Finish(int *status_out)
{
ByteString data;
if (!dead)
{
if (request->CheckDone())
{
data = request->Finish(status_out);
dead = true;
}
}
return data;
}
};
static int http_request_gc(lua_State *l)
{
auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest");
rh->~RequestHandle();
return 0;
}
static int http_request_status(lua_State *l)
{
auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest");
if (rh->Dead())
{
lua_pushliteral(l, "dead");
}
else if (rh->Done())
{
lua_pushliteral(l, "done");
}
else if (rh->Running())
{
lua_pushliteral(l, "running");
}
else
{
lua_pushliteral(l, "queued");
}
return 1;
}
static int http_request_progress(lua_State *l)
{
auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest");
if (!rh->Dead())
{
int total, done;
rh->Progress(&total, &done);
lua_pushinteger(l, total);
lua_pushinteger(l, done);
return 2;
}
return 0;
}
static int http_request_cancel(lua_State *l)
{
auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest");
if (!rh->Dead())
{
rh->Cancel();
}
return 0;
}
static int http_request_finish(lua_State *l)
{
auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest");
if (!rh->Dead())
{
int status_out;
ByteString data = rh->Finish(&status_out);
lua_pushlstring(l, data.c_str(), data.size());
lua_pushinteger(l, status_out);
return 2;
}
return 0;
}
static int http_request(lua_State *l)
{
ByteString uri(luaL_checkstring(l, 1));
std::map<ByteString, ByteString> post_data;
if (lua_istable(l, 2))
{
lua_pushnil(l);
while (lua_next(l, 2))
{
lua_pushvalue(l, -2);
post_data.emplace(lua_tostring(l, -1), lua_tostring(l, -2));
lua_pop(l, 2);
}
}
std::map<ByteString, ByteString> headers;
if (lua_istable(l, 3))
{
lua_pushnil(l);
while (lua_next(l, 3))
{
lua_pushvalue(l, -2);
headers.emplace(lua_tostring(l, -1), lua_tostring(l, -2));
lua_pop(l, 2);
}
}
auto *rh = (RequestHandle *)lua_newuserdata(l, sizeof(RequestHandle));
if (!rh)
{
return 0;
}
new(rh) RequestHandle(uri, post_data, headers);
luaL_newmetatable(l, "HTTPRequest");
lua_setmetatable(l, -2);
return 1;
}
void LuaScriptInterface::initHttpAPI()
{
luaL_newmetatable(l, "HTTPRequest");
lua_pushcfunction(l, http_request_gc);
lua_setfield(l, -2, "__gc");
lua_newtable(l);
lua_pushcfunction(l, http_request_status);
lua_setfield(l, -2, "status");
lua_pushcfunction(l, http_request_progress);
lua_setfield(l, -2, "progress");
lua_pushcfunction(l, http_request_cancel);
lua_setfield(l, -2, "cancel");
lua_pushcfunction(l, http_request_finish);
lua_setfield(l, -2, "finish");
lua_setfield(l, -2, "__index");
struct luaL_Reg httpAPIMethods [] = {
{"request", http_request},
{NULL, NULL}
};
luaL_register(l, "http", httpAPIMethods);
}
bool LuaScriptInterface::HandleEvent(LuaEvents::EventTypes eventType, Event * event) bool LuaScriptInterface::HandleEvent(LuaEvents::EventTypes eventType, Event * event)
{ {
return LuaEvents::HandleEvent(this, event, ByteString::Build("tptevents-", eventType)); return LuaEvents::HandleEvent(this, event, ByteString::Build("tptevents-", eventType));
@ -3792,7 +3994,6 @@ int strlcmp(const char* a, const char* b, int len)
String highlight(String command) String highlight(String command)
{ {
#define CMP(X) (String(wstart, len) == X)
StringBuilder result; StringBuilder result;
int pos = 0; int pos = 0;
String::value_type const*raw = command.c_str(); String::value_type const*raw = command.c_str();
@ -3806,12 +4007,14 @@ String highlight(String command)
String::value_type const* wstart = raw+pos; String::value_type const* wstart = raw+pos;
while((w = wstart[len]) && ((w >= 'A' && w <= 'Z') || (w >= 'a' && w <= 'z') || (w >= '0' && w <= '9') || w == '_')) while((w = wstart[len]) && ((w >= 'A' && w <= 'Z') || (w >= 'a' && w <= 'z') || (w >= '0' && w <= '9') || w == '_'))
len++; len++;
#define CMP(X) (String(wstart, len) == X)
if(CMP("and") || CMP("break") || CMP("do") || CMP("else") || CMP("elseif") || CMP("end") || CMP("for") || CMP("function") || CMP("if") || CMP("in") || CMP("local") || CMP("not") || CMP("or") || CMP("repeat") || CMP("return") || CMP("then") || CMP("until") || CMP("while")) if(CMP("and") || CMP("break") || CMP("do") || CMP("else") || CMP("elseif") || CMP("end") || CMP("for") || CMP("function") || CMP("if") || CMP("in") || CMP("local") || CMP("not") || CMP("or") || CMP("repeat") || CMP("return") || CMP("then") || CMP("until") || CMP("while"))
result << "\x0F\xB5\x89\x01" << String(wstart, len) << "\bw"; result << "\x0F\xB5\x89\x01" << String(wstart, len) << "\bw";
else if(CMP("false") || CMP("nil") || CMP("true")) else if(CMP("false") || CMP("nil") || CMP("true"))
result << "\x0F\xCB\x4B\x16" << String(wstart, len) << "\bw"; result << "\x0F\xCB\x4B\x16" << String(wstart, len) << "\bw";
else else
result << "\x0F\x2A\xA1\x98" << String(wstart, len) << "\bw"; result << "\x0F\x2A\xA1\x98" << String(wstart, len) << "\bw";
#undef CMP
pos += len; pos += len;
} }
else if((c >= '0' && c <= '9') || (c == '.' && raw[pos + 1] >= '0' && raw[pos + 1] <= '9')) else if((c >= '0' && c <= '9') || (c == '.' && raw[pos + 1] >= '0' && raw[pos + 1] <= '9'))

View File

@ -180,6 +180,8 @@ class LuaScriptInterface: public CommandInterface
static int event_unregister(lua_State * l); static int event_unregister(lua_State * l);
static int event_getmodifiers(lua_State * l); static int event_getmodifiers(lua_State * l);
void initHttpAPI();
std::vector<LuaSmartRef> lua_el_func_v, lua_gr_func_v, lua_cd_func_v; std::vector<LuaSmartRef> lua_el_func_v, lua_gr_func_v, lua_cd_func_v;
std::vector<int> lua_el_mode_v; std::vector<int> lua_el_mode_v;