Skip to content
Permalink
Browse files

Add PUT and DELETE request + specific method value to HTTP API (#9909)

  • Loading branch information
Lejo1 committed Jul 29, 2020
1 parent f34abae commit 715a123a33db7b0f191259ba68cbc9c565d0d4e8
@@ -8071,11 +8071,13 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
timeout = 10,
-- Timeout for connection in seconds. Default is 3 seconds.

post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
-- Optional, if specified a POST request with post_data is performed.
method = "GET", "POST", "PUT" or "DELETE"
-- The http method to use. Defaults to "GET".

data = "Raw request data string" OR {field1 = "data1", field2 = "data2"},
-- Data for the POST, PUT or DELETE request.
-- Accepts both a string and a table. If a table is specified, encodes
-- table as x-www-form-urlencoded key-value pairs.
-- If post_data is not specified, a GET request is performed instead.

user_agent = "ExampleUserAgent",
-- Optional, if specified replaces the default minetest user agent with
@@ -8089,6 +8091,10 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
multipart = boolean
-- Optional, if true performs a multipart HTTP request.
-- Default is false.
-- Post only, data must be array

post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
-- Deprecated, use `data` instead. Forces `method = "POST"`.
}

`HTTPRequestResult` definition
@@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client)
fetch_request.request_id = m_httpfetch_next_id; // == i
fetch_request.timeout = m_httpfetch_timeout;
fetch_request.connect_timeout = m_httpfetch_timeout;
fetch_request.post_data = required_hash_set;
fetch_request.method = HTTP_POST;
fetch_request.raw_data = required_hash_set;
fetch_request.extra_headers.emplace_back(
"Content-Type: application/octet-stream");

@@ -294,13 +294,11 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
}

// Set POST (or GET) data
if (request.post_fields.empty() && request.post_data.empty()) {
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (request.multipart) {
// Set data from fields or raw_data
if (request.multipart) {
curl_httppost *last = NULL;
for (StringMap::iterator it = request.post_fields.begin();
it != request.post_fields.end(); ++it) {
for (StringMap::iterator it = request.fields.begin();
it != request.fields.end(); ++it) {
curl_formadd(&post, &last,
CURLFORM_NAMELENGTH, it->first.size(),
CURLFORM_PTRNAME, it->first.c_str(),
@@ -311,28 +309,42 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
// request.post_fields must now *never* be
// modified until CURLOPT_HTTPPOST is cleared
} else if (request.post_data.empty()) {
curl_easy_setopt(curl, CURLOPT_POST, 1);
std::string str;
for (auto &post_field : request.post_fields) {
if (!str.empty())
str += "&";
str += urlencode(post_field.first);
str += "=";
str += urlencode(post_field.second);
}
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
str.size());
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
str.c_str());
} else {
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
request.post_data.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
request.post_data.c_str());
// request.post_data must now *never* be
// modified until CURLOPT_POSTFIELDS is cleared
switch (request.method) {
case HTTP_GET:
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
break;
case HTTP_POST:
curl_easy_setopt(curl, CURLOPT_POST, 1);
break;
case HTTP_PUT:
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
break;
case HTTP_DELETE:
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
break;
}
if (request.method != HTTP_GET) {
if (!request.raw_data.empty()) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
request.raw_data.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
request.raw_data.c_str());
} else if (!request.fields.empty()) {
std::string str;
for (auto &field : request.fields) {
if (!str.empty())
str += "&";
str += urlencode(field.first);
str += "=";
str += urlencode(field.second);
}
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
str.size());
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
str.c_str());
}
}
}
// Set additional HTTP headers
for (const std::string &extra_header : request.extra_headers) {
@@ -28,6 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define HTTPFETCH_DISCARD 0
#define HTTPFETCH_SYNC 1

// Methods
enum HttpMethod : u8
{
HTTP_GET,
HTTP_POST,
HTTP_PUT,
HTTP_DELETE,
};

struct HTTPFetchRequest
{
std::string url = "";
@@ -50,12 +59,15 @@ struct HTTPFetchRequest
// application/x-www-form-urlencoded. POST-only.
bool multipart = false;

// POST fields. Fields are escaped properly.
// If this is empty a GET request is done instead.
StringMap post_fields;
// The Method to use default = GET
// Avaible methods GET, POST, PUT, DELETE
HttpMethod method = HTTP_GET;

// Fields of the request
StringMap fields;

// Raw POST data, overrides post_fields.
std::string post_data;
// Raw data of the request overrides fields
std::string raw_data;

// If not empty, should contain entries such as "Accept: text/html"
std::vector<std::string> extra_headers;
@@ -1034,7 +1034,7 @@ const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
"This server has experienced an internal error. You will now be disconnected."
};

enum PlayerListModifer: u8
enum PlayerListModifer : u8
{
PLAYER_LIST_INIT,
PLAYER_LIST_ADD,
@@ -49,17 +49,40 @@ void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
req.multipart = getboolfield_default(L, 1, "multipart", false);
req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000;

// post_data: if table, post form data, otherwise raw data
lua_getfield(L, 1, "method");
if (lua_isstring(L, -1)) {
std::string mth = getstringfield_default(L, 1, "method", "");
if (mth == "GET")
req.method = HTTP_GET;
else if (mth == "POST")
req.method = HTTP_POST;
else if (mth == "PUT")
req.method = HTTP_PUT;
else if (mth == "DELETE")
req.method = HTTP_DELETE;
}
lua_pop(L, 1);

// post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead
lua_getfield(L, 1, "post_data");
if (lua_isnil(L, 2)) {
lua_pop(L, 1);
lua_getfield(L, 1, "data");
}
else {
req.method = HTTP_POST;
}

if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
req.post_fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1);
req.fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1);
lua_pop(L, 1);
}
} else if (lua_isstring(L, 2)) {
req.post_data = readParam<std::string>(L, 2);
req.raw_data = readParam<std::string>(L, 2);
}

lua_pop(L, 1);

lua_getfield(L, 1, "extra_headers");
@@ -32,10 +32,10 @@ class ModApiHttp : public ModApiBase {
static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req);
static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true);

// http_fetch_sync({url=, timeout=, post_data=})
// http_fetch_sync({url=, timeout=, data=})
static int l_http_fetch_sync(lua_State *L);

// http_fetch_async({url=, timeout=, post_data=})
// http_fetch_async({url=, timeout=, data=})
static int l_http_fetch_async(lua_State *L);

// http_fetch_async_get(handle)
@@ -261,11 +261,11 @@ void sendAnnounce(AnnounceAction action,

HTTPFetchRequest fetch_request;
fetch_request.url = g_settings->get("serverlist_url") + std::string("/announce");
fetch_request.post_fields["json"] = fastWriteJson(server);
fetch_request.method = HTTP_POST;
fetch_request.fields["json"] = fastWriteJson(server);
fetch_request.multipart = true;
httpfetch_async(fetch_request);
}
#endif

} // namespace ServerList

0 comments on commit 715a123

Please sign in to comment.
You can’t perform that action at this time.