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 #endif
} }
void Request::AddHeader(ByteString name, ByteString value) void Request::AddHeader(ByteString header)
{ {
#ifndef NOHTTP #ifndef NOHTTP
headers = curl_slist_append(headers, (name + ": " + value).c_str()); headers = curl_slist_append(headers, header.c_str());
#endif #endif
} }
@@ -131,17 +131,32 @@ namespace http
{ {
if (session.size()) if (session.size())
{ {
AddHeader("X-Auth-User-Id", ID); AddHeader("X-Auth-User-Id: " + ID);
AddHeader("X-Auth-Session-Key", session); AddHeader("X-Auth-Session-Key: " + session);
} }
else else
{ {
AddHeader("X-Auth-User", ID); AddHeader("X-Auth-User: " + ID);
} }
} }
} }
#ifndef NOHTTP #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) size_t Request::WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata)
{ {
Request *req = (Request *)userdata; Request *req = (Request *)userdata;
@@ -231,6 +246,9 @@ namespace http
curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this); curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this);
curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent.c_str()); 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_WRITEDATA, (void *)this);
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, Request::WriteDataHandler); 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) // 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 #ifndef NOHTTP
if (CheckCanceled()) if (CheckCanceled())
@@ -263,6 +281,10 @@ namespace http
{ {
*status_out = status; *status_out = status;
} }
if (headers_out)
{
*headers_out = std::move(response_headers);
}
response_out = std::move(response_body); response_out = std::move(response_body);
} }

View File

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

View File

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