Add support for multipart/form-data to HTTPFetch for server announcing

This commit is contained in:
ShadowNinja 2014-06-19 16:00:22 -04:00
parent 1c01ed5f13
commit b2dfde8c8c
4 changed files with 63 additions and 18 deletions

@ -266,7 +266,7 @@ void ClientMediaDownloader::initialStep(Client *client)
fetchrequest.request_id = m_httpfetch_next_id; // == i fetchrequest.request_id = m_httpfetch_next_id; // == i
fetchrequest.timeout = m_httpfetch_timeout; fetchrequest.timeout = m_httpfetch_timeout;
fetchrequest.connect_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( fetchrequest.extra_headers.push_back(
"Content-Type: application/octet-stream"); "Content-Type: application/octet-stream");
httpfetch_async(fetchrequest); httpfetch_async(fetchrequest);

@ -46,6 +46,7 @@ HTTPFetchRequest::HTTPFetchRequest()
request_id = 0; request_id = 0;
timeout = g_settings->getS32("curl_timeout"); timeout = g_settings->getS32("curl_timeout");
connect_timeout = timeout; connect_timeout = timeout;
multipart = false;
useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")"; useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")";
} }
@ -184,6 +185,7 @@ struct HTTPFetchOngoing
std::ostringstream oss; std::ostringstream oss;
char *post_fields; char *post_fields;
struct curl_slist *httpheader; struct curl_slist *httpheader;
curl_httppost *post;
HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_): HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_):
pool(pool_), pool(pool_),
@ -192,7 +194,8 @@ struct HTTPFetchOngoing
request(request_), request(request_),
result(request_), result(request_),
oss(std::ios::binary), oss(std::ios::binary),
httpheader(NULL) httpheader(NULL),
post(NULL)
{ {
curl = pool->alloc(); curl = pool->alloc();
if (curl != NULL) { if (curl != NULL) {
@ -239,19 +242,53 @@ struct HTTPFetchOngoing
httpfetch_writefunction); httpfetch_writefunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
} }
// Set POST (or GET) data // Set POST (or GET) data
if (request.post_fields.empty()) { if (request.post_fields.empty()) {
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (request.multipart) {
curl_httppost *last = NULL;
for (std::map<std::string, std::string>::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);
} }
else { curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
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());
// request.post_fields must now *never* be // request.post_fields must now *never* be
// 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<std::string, std::string>::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 // modified until CURLOPT_POSTFIELDS is cleared
} }
}
// Set additional HTTP headers // Set additional HTTP headers
for (size_t i = 0; i < request.extra_headers.size(); ++i) { for (size_t i = 0; i < request.extra_headers.size(); ++i) {
httpheader = curl_slist_append( httpheader = curl_slist_append(
@ -333,6 +370,10 @@ struct HTTPFetchOngoing
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
curl_slist_free_all(httpheader); curl_slist_free_all(httpheader);
} }
if (post != NULL) {
curl_easy_setopt(curl, CURLOPT_HTTPPOST, NULL);
curl_formfree(post);
}
// Store the cURL handle for reuse // Store the cURL handle for reuse
pool->free(curl); pool->free(curl);

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include "config.h" #include "config.h"
// Can be used in place of "caller" in asynchronous transfers to discard result // 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 // Timeout for the connection phase, in milliseconds
long connect_timeout; long connect_timeout;
// POST data (should be application/x-www-form-urlencoded // Indicates if this is multipart/form-data or
// unless a Content-Type header is specified in extra_headers) // 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. // If this is empty a GET request is done instead.
std::string post_fields; std::map<std::string, std::string> post_fields;
// Raw POST data, overrides post_fields.
std::string post_data;
// If not empty, should contain entries such as "Accept: text/html" // If not empty, should contain entries such as "Accept: text/html"
std::vector<std::string> extra_headers; std::vector<std::string> extra_headers;

@ -230,11 +230,8 @@ void sendAnnounce(std::string action, const std::vector<std::string> & clients_n
Json::FastWriter writer; Json::FastWriter writer;
HTTPFetchRequest fetchrequest; HTTPFetchRequest fetchrequest;
fetchrequest.url = g_settings->get("serverlist_url") + std::string("/announce"); fetchrequest.url = g_settings->get("serverlist_url") + std::string("/announce");
std::string query = std::string("json=") + urlencode(writer.write(server)); fetchrequest.post_fields["json"] = writer.write(server);
if (query.size() < 1000) fetchrequest.multipart = true;
fetchrequest.url += "?" + query;
else
fetchrequest.post_fields = query;
httpfetch_async(fetchrequest); httpfetch_async(fetchrequest);
} }
#endif #endif