Return HTTP response headers to Lua

Also accept request headers in a string array format, beside the old string-string dictionary format.
This commit is contained in:
Tamás Bálint Misius
2022-08-21 20:54:43 +02:00
parent 22805e14f1
commit 3c6bd74389
3 changed files with 68 additions and 22 deletions

View File

@@ -77,10 +77,10 @@ namespace http
#endif
}
void Request::AddHeader(ByteString name, ByteString value)
void Request::AddHeader(ByteString header)
{
#ifndef NOHTTP
headers = curl_slist_append(headers, (name + ": " + value).c_str());
headers = curl_slist_append(headers, header.c_str());
#endif
}
@@ -131,17 +131,32 @@ namespace http
{
if (session.size())
{
AddHeader("X-Auth-User-Id", ID);
AddHeader("X-Auth-Session-Key", session);
AddHeader("X-Auth-User-Id: " + ID);
AddHeader("X-Auth-Session-Key: " + session);
}
else
{
AddHeader("X-Auth-User", ID);
AddHeader("X-Auth-User: " + ID);
}
}
}
#ifndef NOHTTP
size_t Request::HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata)
{
Request *req = (Request *)userdata;
auto actual_size = size * count;
if (actual_size >= 2 && ptr[actual_size - 2] == '\r' && ptr[actual_size - 1] == '\n')
{
if (actual_size > 2) // don't include header list terminator (but include the status line)
{
req->response_headers.push_back(ByteString(ptr, ptr + actual_size - 2));
}
return actual_size;
}
return 0;
}
size_t Request::WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata)
{
Request *req = (Request *)userdata;
@@ -231,6 +246,9 @@ namespace http
curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this);
curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent.c_str());
curl_easy_setopt(easy, CURLOPT_HEADERDATA, (void *)this);
curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, Request::HeaderDataHandler);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *)this);
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, Request::WriteDataHandler);
}
@@ -245,7 +263,7 @@ namespace http
// finish the request (if called before the request is done, this will block)
ByteString Request::Finish(int *status_out)
ByteString Request::Finish(int *status_out, std::vector<ByteString> *headers_out)
{
#ifndef NOHTTP
if (CheckCanceled())
@@ -263,6 +281,10 @@ namespace http
{
*status_out = status;
}
if (headers_out)
{
*headers_out = std::move(response_headers);
}
response_out = std::move(response_body);
}

View File

@@ -31,6 +31,7 @@ namespace http
{
#ifndef NOHTTP
ByteString uri;
std::vector<ByteString> response_headers;
ByteString response_body;
CURL *easy;
@@ -58,19 +59,20 @@ namespace http
std::condition_variable done_cv;
static size_t WriteDataHandler(char * ptr, size_t size, size_t count, void * userdata);
static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata);
static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata);
#endif
public:
Request(ByteString uri);
virtual ~Request();
void AddHeader(ByteString name, ByteString value);
void AddHeader(ByteString header);
void AddPostData(std::map<ByteString, ByteString> data);
void AuthHeaders(ByteString ID, ByteString session);
void Start();
ByteString Finish(int *status);
ByteString Finish(int *status, std::vector<ByteString> *headers = nullptr);
void Cancel();
void CheckProgress(int *total, int *done);

View File

@@ -4048,13 +4048,13 @@ class RequestHandle
bool dead;
public:
RequestHandle(ByteString &uri, bool isPost, std::map<ByteString, ByteString> &post_data, std::map<ByteString, ByteString> &headers)
RequestHandle(ByteString &uri, bool isPost, std::map<ByteString, ByteString> &post_data, std::vector<ByteString> &headers)
{
dead = false;
request = new http::Request(uri);
for (auto &header : headers)
{
request->AddHeader(header.first, header.second);
request->AddHeader(header);
}
if (isPost)
request->AddPostData(post_data);
@@ -4096,14 +4096,14 @@ public:
}
}
ByteString Finish(int *status_out)
ByteString Finish(int &status_out, std::vector<ByteString> &headers)
{
ByteString data;
if (!dead)
{
if (request->CheckDone())
{
data = request->Finish(status_out);
data = request->Finish(&status_out, &headers);
dead = true;
}
}
@@ -4166,10 +4166,17 @@ static int http_request_finish(lua_State *l)
if (!rh->Dead())
{
int status_out;
ByteString data = rh->Finish(&status_out);
std::vector<ByteString> headers;
ByteString data = rh->Finish(status_out, headers);
lua_pushlstring(l, data.c_str(), data.size());
lua_pushinteger(l, status_out);
return 2;
lua_newtable(l);
for (auto i = 0; i < int(headers.size()); ++i)
{
lua_pushlstring(l, headers[i].data(), headers[i].size());
lua_rawseti(l, -2, i + 1);
}
return 3;
}
return 0;
}
@@ -4192,15 +4199,30 @@ static int http_request(lua_State *l, bool isPost)
}
}
std::map<ByteString, ByteString> headers;
if (lua_istable(l, isPost ? 3 : 2))
std::vector<ByteString> headers;
auto headersIndex = isPost ? 3 : 2;
if (lua_istable(l, headersIndex))
{
lua_pushnil(l);
while (lua_next(l, isPost ? 3 : 2))
auto size = lua_objlen(l, headersIndex);
if (size)
{
lua_pushvalue(l, -2);
headers.emplace(lua_tostring(l, -1), lua_tostring(l, -2));
lua_pop(l, 2);
for (auto i = 0U; i < size; ++i)
{
lua_rawgeti(l, headersIndex, i + 1);
headers.push_back(lua_tostring(l, -1));
lua_pop(l, 1);
}
}
else
{
// old dictionary format
lua_pushnil(l);
while (lua_next(l, headersIndex))
{
lua_pushvalue(l, -2);
headers.push_back(lua_tostring(l, -1) + ByteString(": ") + lua_tostring(l, -2));
lua_pop(l, 2);
}
}
}
auto *rh = (RequestHandle *)lua_newuserdata(l, sizeof(RequestHandle));