From b2dfde8c8c29d0b118ba9018be202de21bb28505 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 19 Jun 2014 16:00:22 -0400 Subject: [PATCH] Add support for multipart/form-data to HTTPFetch for server announcing --- src/clientmedia.cpp | 2 +- src/httpfetch.cpp | 59 ++++++++++++++++++++++++++++++++++++++------- src/httpfetch.h | 13 +++++++--- src/serverlist.cpp | 7 ++---- 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp index b3e7e8e06..c1043c769 100644 --- a/src/clientmedia.cpp +++ b/src/clientmedia.cpp @@ -266,7 +266,7 @@ void ClientMediaDownloader::initialStep(Client *client) fetchrequest.request_id = m_httpfetch_next_id; // == i fetchrequest.timeout = m_httpfetch_timeout; fetchrequest.connect_timeout = m_httpfetch_timeout; - fetchrequest.post_fields = required_hash_set; + fetchrequest.post_data = required_hash_set; fetchrequest.extra_headers.push_back( "Content-Type: application/octet-stream"); httpfetch_async(fetchrequest); diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 69c366ee0..c651055bc 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -46,6 +46,7 @@ HTTPFetchRequest::HTTPFetchRequest() request_id = 0; timeout = g_settings->getS32("curl_timeout"); connect_timeout = timeout; + multipart = false; useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")"; } @@ -184,6 +185,7 @@ struct HTTPFetchOngoing std::ostringstream oss; char *post_fields; struct curl_slist *httpheader; + curl_httppost *post; HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_): pool(pool_), @@ -192,7 +194,8 @@ struct HTTPFetchOngoing request(request_), result(request_), oss(std::ios::binary), - httpheader(NULL) + httpheader(NULL), + post(NULL) { curl = pool->alloc(); if (curl != NULL) { @@ -239,18 +242,52 @@ struct HTTPFetchOngoing httpfetch_writefunction); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss); } + // Set POST (or GET) data if (request.post_fields.empty()) { curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); - } - else { - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, - request.post_fields.size()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, - request.post_fields.c_str()); + } else if (request.multipart) { + curl_httppost *last = NULL; + for (std::map::iterator it = + request.post_fields.begin(); + it != request.post_fields.end(); + ++it) { + curl_formadd(&post, &last, + CURLFORM_NAMELENGTH, it->first.size(), + CURLFORM_PTRNAME, it->first.c_str(), + CURLFORM_CONTENTSLENGTH, it->second.size(), + CURLFORM_PTRCONTENTS, it->second.c_str(), + CURLFORM_END); + } + curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); // request.post_fields must now *never* be - // modified until CURLOPT_POSTFIELDS is cleared + // modified until CURLOPT_HTTPPOST is cleared + } else { + curl_easy_setopt(curl, CURLOPT_POST, 1); + if (request.post_data.empty()) { + std::string str; + for (std::map::iterator it = + request.post_fields.begin(); + it != request.post_fields.end(); + ++it) { + if (str != "") + str += "&"; + str += urlencode(it->first); + str += "="; + str += urlencode(it->second); + } + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + str.size()); + curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, + str.c_str()); + } else { + 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 + } } // Set additional HTTP headers for (size_t i = 0; i < request.extra_headers.size(); ++i) { @@ -333,6 +370,10 @@ struct HTTPFetchOngoing curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); curl_slist_free_all(httpheader); } + if (post != NULL) { + curl_easy_setopt(curl, CURLOPT_HTTPPOST, NULL); + curl_formfree(post); + } // Store the cURL handle for reuse pool->free(curl); diff --git a/src/httpfetch.h b/src/httpfetch.h index e02e92b4a..aba7482c2 100644 --- a/src/httpfetch.h +++ b/src/httpfetch.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include "config.h" // Can be used in place of "caller" in asynchronous transfers to discard result @@ -47,10 +48,16 @@ struct HTTPFetchRequest // Timeout for the connection phase, in milliseconds long connect_timeout; - // POST data (should be application/x-www-form-urlencoded - // unless a Content-Type header is specified in extra_headers) + // Indicates if this is multipart/form-data or + // application/x-www-form-urlencoded. POST-only. + bool multipart; + + // POST fields. Fields are escaped properly. // If this is empty a GET request is done instead. - std::string post_fields; + std::map post_fields; + + // Raw POST data, overrides post_fields. + std::string post_data; // If not empty, should contain entries such as "Accept: text/html" std::vector extra_headers; diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 8a85b33b3..315e23688 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -230,11 +230,8 @@ void sendAnnounce(std::string action, const std::vector & clients_n Json::FastWriter writer; HTTPFetchRequest fetchrequest; fetchrequest.url = g_settings->get("serverlist_url") + std::string("/announce"); - std::string query = std::string("json=") + urlencode(writer.write(server)); - if (query.size() < 1000) - fetchrequest.url += "?" + query; - else - fetchrequest.post_fields = query; + fetchrequest.post_fields["json"] = writer.write(server); + fetchrequest.multipart = true; httpfetch_async(fetchrequest); } #endif