Add scriptapi interface to HTTPFetchRequest

This allows mods to perform both asynchronous and synchronous HTTP
requests.
For game mods, a special callback API is provided via
  minetest.http_fetch(<request>, <callback function>)
master
Jeija 2014-11-23 17:49:35 +01:00 committed by Jeija
parent 69b75d5fbb
commit 3e9499fca3
6 changed files with 187 additions and 0 deletions

View File

@ -112,3 +112,21 @@ function core.record_protection_violation(pos, name)
end
end
-- If built with cURL
if core.http_fetch_async then
core.http_requests = {}
function core.http_fetch(req, callback)
local handle = core.http_fetch_async(req)
table.insert(core.http_requests, {handle = handle, callback = callback})
end
core.register_globalstep(function()
for i, req in ipairs(core.http_requests) do
local res = core.http_fetch_async_get(req.handle)
if res.code ~= 0 then
table.remove(core.http_requests, i)
req.callback(res)
end
end
end)
end

View File

@ -2118,6 +2118,20 @@ These functions return the leftover itemstack.
* `force_placement` is a boolean indicating whether nodes other than `air` and
`ignore` are replaced by the schematic
### HTTP Requests:
* `minetest.http_fetch(HTTPRequest req, callback)`
* Performs given request asynchronously and calls callback upon completion
* callback: `function(HTTPRequestResult res)`
* `minetest.http_fetch_async(HTTPRequest req)`: returns handle
* Performs given request asynchronously and returns handle for `minetest.http_fetch_async_get`
* `minetest.http_fetch_async_get(handle)`: returns HTTPRequestResult
* Return response data for given asynchronous HTTP request
* `minetest.http_fetch_sync(HTTPRequest req)`: returns HTTPRequestResult
* Blocking. Returns HTTP response data for given request directly.
If possible, do NOT use sychronous HTTP requests, they block the server thread.
These functions only exist if minetest was built with cURL support.
### Misc.
* `minetest.get_connected_players()`: returns list of `ObjectRefs`
* `minetest.hash_node_position({x=,y=,z=})`: returns an 48-bit integer
@ -3267,3 +3281,23 @@ Definition tables
playername = "singleplayer"
-- ^ Playername is optional, if specified spawns particle only on the player's client
}
### `HTTPRequest` definition (`http_fetch`, `http_fetch_async`, `http_fetch_sync`)
{
url = "http://example.org",
timeout = 10,
-- ^ Timeout for connection in seconds
post_data = "Post request data"
-- ^ If supplied, a POST request is performed. Otherwise, performs GET request.
}
### `HTTPRequestResult` definition (`http_fetch` callback, `http_fetch_async_get`)
{
succeeded = true,
-- ^ If true, the request was succesful
timeout = false,
-- ^ If true, the request timed out
code = 200,
-- ^ HTTP status code
data = "response"
}

View File

@ -386,4 +386,13 @@ void setboolfield(lua_State *L, int table,
lua_setfield(L, table, fieldname);
}
void setstringfield(lua_State *L, int table,
const char *fieldname, const char *value)
{
lua_pushstring(L, value);
if(table < 0)
table -= 1;
lua_setfield(L, table, fieldname);
}

View File

@ -69,6 +69,8 @@ void setfloatfield(lua_State *L, int table,
const char *fieldname, float value);
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value);
void setstringfield(lua_State *L, int table,
const char *fieldname, const char *value);
v3f checkFloatPos (lua_State *L, int index);

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_async.h"
#include "serialization.h"
#include "json/json.h"
#include "httpfetch.h"
#include "debug.h"
#include "porting.h"
#include "log.h"
@ -320,6 +321,99 @@ int ModApiUtil::l_decompress(lua_State *L)
return 1;
}
#if USE_CURL
void ModApiUtil::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
{
luaL_checktype(L, 1, LUA_TTABLE);
req.caller = httpfetch_caller_alloc();
getstringfield(L, 1, "url", req.url);
req.post_data = getstringfield_default(L, 1, "post_data", "");
lua_getfield(L, 1, "useragent");
if (lua_isstring(L, -1))
req.useragent = getstringfield_default(L, 1, "useragent", "");
lua_pop(L, 1);
req.multipart = getboolfield_default(L, 1, "multipart", false);
req.timeout = getintfield_default(L, 1, "timeout", 1) * 1000;
lua_getfield(L, 1, "post_fields");
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0)
{
req.post_fields[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1);
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, 1, "extra_headers");
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0)
{
req.extra_headers.push_back(luaL_checkstring(L, -1));
lua_pop(L, 1);
}
}
lua_pop(L, 1);
}
void ModApiUtil::push_http_fetch_result(lua_State *L, HTTPFetchResult &res)
{
lua_newtable(L);
setboolfield(L, -1, "succeeded", res.succeeded);
setboolfield(L, -1, "timeout", res.timeout);
setintfield(L, -1, "code", res.response_code);
setstringfield(L, -1, "data", res.data.c_str());
}
// http_fetch_async({url=, timeout=, post_data=})
int ModApiUtil::l_http_fetch_async(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
HTTPFetchRequest req;
read_http_fetch_request(L, req);
actionstream<<"Mod performs HTTP request with URL "<<req.url<<std::endl;
httpfetch_async(req);
lua_pushnumber(L, req.caller);
return 1;
}
// http_fetch_async_get(handle)
int ModApiUtil::l_http_fetch_async_get(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
int handle = luaL_checknumber(L, 1);
HTTPFetchResult res;
httpfetch_async_get(handle, res);
push_http_fetch_result(L, res);
return 1;
}
// http_fetch_sync({url=, timeout=, post_data=})
int ModApiUtil::l_http_fetch_sync(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
HTTPFetchRequest req;
HTTPFetchResult res;
read_http_fetch_request(L, req);
actionstream<<"Mod performs HTTP request with URL "<<req.url<<std::endl;
httpfetch_sync(req, res);
push_http_fetch_result(L, res);
return 1;
}
#endif
void ModApiUtil::Initialize(lua_State *L, int top)
{
API_FCT(debug);
@ -345,6 +439,12 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(compress);
API_FCT(decompress);
#if USE_CURL
API_FCT(http_fetch_async);
API_FCT(http_fetch_async_get);
API_FCT(http_fetch_sync);
#endif
}
void ModApiUtil::InitializeAsync(AsyncEngine& engine)
@ -367,5 +467,11 @@ void ModApiUtil::InitializeAsync(AsyncEngine& engine)
ASYNC_API_FCT(compress);
ASYNC_API_FCT(decompress);
#if USE_CURL
ASYNC_API_FCT(http_fetch_async);
ASYNC_API_FCT(http_fetch_async_get);
ASYNC_API_FCT(http_fetch_sync);
#endif
}

View File

@ -21,7 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define L_UTIL_H_
#include "lua_api/l_base.h"
#include "config.h"
struct HTTPFetchRequest;
struct HTTPFetchResult;
class AsyncEngine;
class ModApiUtil : public ModApiBase {
@ -87,6 +90,21 @@ private:
// decompress(data, method, ...)
static int l_decompress(lua_State *L);
#if USE_CURL
// Helpers for HTTP fetch functions
static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req);
static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res);
// http_fetch_async({url=, timeout=, post_data=})
static int l_http_fetch_async(lua_State *L);
// http_fetch_async_get(handle)
static int l_http_fetch_async_get(lua_State *L);
// http_fetch_sync({url=, timeout=, post_data=})
static int l_http_fetch_sync(lua_State *L);
#endif
public:
static void Initialize(lua_State *L, int top);