diff --git a/src/client/http/Request.cpp b/src/client/http/Request.cpp index 9a197f699..10aa91145 100644 --- a/src/client/http/Request.cpp +++ b/src/client/http/Request.cpp @@ -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 *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); } diff --git a/src/client/http/Request.h b/src/client/http/Request.h index 1807b067d..6c705ce25 100644 --- a/src/client/http/Request.h +++ b/src/client/http/Request.h @@ -31,6 +31,7 @@ namespace http { #ifndef NOHTTP ByteString uri; + std::vector 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 data); void AuthHeaders(ByteString ID, ByteString session); void Start(); - ByteString Finish(int *status); + ByteString Finish(int *status, std::vector *headers = nullptr); void Cancel(); void CheckProgress(int *total, int *done); diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp index 023570151..63284fbbf 100644 --- a/src/lua/LuaScriptInterface.cpp +++ b/src/lua/LuaScriptInterface.cpp @@ -4048,13 +4048,13 @@ class RequestHandle bool dead; public: - RequestHandle(ByteString &uri, bool isPost, std::map &post_data, std::map &headers) + RequestHandle(ByteString &uri, bool isPost, std::map &post_data, std::vector &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 &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 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 headers; - if (lua_istable(l, isPost ? 3 : 2)) + std::vector 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));