From 31b22502b3d2c0f793bbca3f3dc7b8f681f72dfa Mon Sep 17 00:00:00 2001 From: Mathew Versluys Date: Sat, 28 May 2016 14:05:06 -0700 Subject: [PATCH 1/2] Improvements to http.get and http.download MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was an undocumented return code from http.download which was the numerical CURL error code. This has been replaced with the HTTP response code. The return for http.download is now error message, HTTP response code. This could break code that was relying on the CURL error but my light research is that users of this call were checking for the error message being “OK” and that functionality is preserved. http.get has been updated to have a consistent return signature to http.download and now returns the result, error message, HTTP response code. The error message is now set to “OK” to match http.download. These changes abstract this api from curl and make the focus http. Cleaned up the code to make it easier to follow and removed some memory leaks. http.get and http.download would leak if headers were supplied. http.get would leak the string buffer if curl_init failed. --- src/host/http.c | 89 ++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/src/host/http.c b/src/host/http.c index 6eaea049..31873c48 100644 --- a/src/host/http.c +++ b/src/host/http.c @@ -45,11 +45,12 @@ typedef struct int RefIndex; string S; char errorBuffer[CURL_ERROR_SIZE]; -} CurlCallbackState; + struct curl_slist* headers; +} curl_state; static int curl_progress_cb(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow) { - CurlCallbackState* state = (CurlCallbackState*) userdata; + curl_state* state = (curl_state*) userdata; lua_State* L = state->L; (void)ultotal; @@ -68,7 +69,7 @@ static int curl_progress_cb(void* userdata, double dltotal, double dlnow, double static size_t curl_write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { - CurlCallbackState* state = (CurlCallbackState*) userdata; + curl_state* state = (curl_state*) userdata; string* s = &state->S; size_t new_len = s->len + size * nmemb; @@ -117,10 +118,16 @@ static void get_headers(lua_State* L, int headersIndex, struct curl_slist** head } } -static CURL* curl_request(lua_State* L, CurlCallbackState* state, const char* url, FILE* fp, int optionsIndex, int progressFnIndex, int headersIndex) +static CURL* curl_request(lua_State* L, curl_state* state, const char* url, FILE* fp, int optionsIndex, int progressFnIndex, int headersIndex) { CURL* curl; - struct curl_slist* headers = NULL; + + state->L = 0; + state->RefIndex = 0; + state->S.ptr = NULL; + state->S.len = 0; + state->errorBuffer[0] = '\0'; + state->headers = NULL; curl_init(); curl = curl_easy_init(); @@ -145,8 +152,8 @@ static CURL* curl_request(lua_State* L, CurlCallbackState* state, const char* ur if (!strcmp(key, "headers") && lua_istable(L, -1)) { - get_headers(L, lua_gettop(L), &headers); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + get_headers(L, lua_gettop(L), &state->headers); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers); } else if (!strcmp(key, "progress") && lua_isfunction(L, -1)) { @@ -182,8 +189,8 @@ static CURL* curl_request(lua_State* L, CurlCallbackState* state, const char* ur if (headersIndex && lua_istable(L, headersIndex)) { - get_headers(L, headersIndex, &headers); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + get_headers(L, headersIndex, &state->headers); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers); } } @@ -209,13 +216,24 @@ static CURL* curl_request(lua_State* L, CurlCallbackState* state, const char* ur return curl; } +static void curl_cleanup(CURL* curl, curl_state* state) +{ + if (state->headers) + { + curl_slist_free_all(state->headers); + state->headers = 0; + } + curl_easy_cleanup(curl); +} + int http_get(lua_State* L) { - CurlCallbackState state = { 0, 0, {NULL, 0}, {0} }; + curl_state state; CURL* curl; - CURLcode code; - + CURLcode code = CURLE_FAILED_INIT; + long responseCode = 0; + if (lua_istable(L, 2)) { // http.get(source, { options }) @@ -228,41 +246,42 @@ int http_get(lua_State* L) curl = curl_request(L, &state, luaL_checkstring(L, 1), /*fp=*/NULL, /*optionsIndex=*/0, /*progressFnIndex=*/2, /*headersIndex=*/3); } - if (!curl) - { - lua_pushnil(L); - return 1; - } - string_init(&state.S); - code = curl_easy_perform(curl); - if (code != CURLE_OK) + if (curl) { - char errorBuf[1024]; - snprintf(errorBuf, sizeof(errorBuf) - 1, "%s\n%s\n", curl_easy_strerror(code), state.errorBuffer); - - lua_pushnil(L); - lua_pushfstring(L, errorBuf); - string_free(&state.S); - return 2; + code = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); + curl_cleanup(curl, &state); } - curl_easy_cleanup(curl); + if (code != CURLE_OK) + { + lua_pushnil(L); + char errorBuf[1024]; + snprintf(errorBuf, sizeof(errorBuf) - 1, "%s\n%s\n", curl_easy_strerror(code), state.errorBuffer); + lua_pushstring(L, errorBuf); + } + else + { + lua_pushlstring(L, state.S.ptr, state.S.len); + lua_pushstring(L, "OK"); + } - lua_pushlstring(L, state.S.ptr, state.S.len); string_free(&state.S); - return 1; + lua_pushnumber(L, responseCode); + + return 3; } int http_download(lua_State* L) { - CurlCallbackState state = { 0, 0, {NULL, 0}, {0} }; - + curl_state state; CURL* curl; CURLcode code = CURLE_FAILED_INIT; + long responseCode = 0; FILE* fp; const char* file = luaL_checkstring(L, 2); @@ -290,7 +309,8 @@ int http_download(lua_State* L) if (curl) { code = curl_easy_perform(curl); - curl_easy_cleanup(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); + curl_cleanup(curl, &state); } fclose(fp); @@ -306,7 +326,8 @@ int http_download(lua_State* L) lua_pushstring(L, "OK"); } - lua_pushnumber(L, code); + lua_pushnumber(L, responseCode); + return 2; } From f26c07baee51d5a457a0e2e4518d839c0652abf0 Mon Sep 17 00:00:00 2001 From: Matthew Versluys Date: Sun, 29 May 2016 10:07:37 -0700 Subject: [PATCH 2/2] Added timeout option to http.get/download Allows setting a maximum timeout for a http request. --- src/host/http.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/host/http.c b/src/host/http.c index 31873c48..4038afc7 100644 --- a/src/host/http.c +++ b/src/host/http.c @@ -173,6 +173,10 @@ static CURL* curl_request(lua_State* L, curl_state* state, const char* url, FILE { curl_easy_setopt(curl, CURLOPT_PASSWORD, luaL_checkstring(L, -1)); } + else if (!strcmp(key, "timeout") && lua_isnumber(L, -1)) + { + curl_easy_setopt(curl, CURLOPT_TIMEOUT, luaL_checknumber(L, -1)); + } // pop the value, leave the key for lua_next lua_pop(L, 1);