Add <functional> to files that use std::function
[lsnes.git] / src / library / httpreq.cpp
blobf1746969cf0c53f4b3662bf7e1bc6efd4f68adda
1 #ifndef CURL_STATICLIB
2 #define CURL_STATICLIB
3 #endif
4 #include <cstdio>
5 #include "httpreq.hpp"
6 #include "httpauth.hpp"
7 #include "string.hpp"
8 #include "minmax.hpp"
9 #include "threads.hpp"
10 #include "streamcompress.hpp"
11 #include <curl/curl.h>
12 #include <functional>
13 #include <cstring>
14 #include <fstream>
15 #include <boost/iostreams/categories.hpp>
16 #include <boost/iostreams/copy.hpp>
17 #include <boost/iostreams/stream.hpp>
18 #include <boost/iostreams/stream_buffer.hpp>
19 #include <boost/iostreams/filter/symmetric.hpp>
20 #include <boost/iostreams/filter/zlib.hpp>
21 #include <boost/iostreams/filtering_stream.hpp>
22 #include <boost/iostreams/device/back_inserter.hpp>
24 http_request::input_handler::~input_handler()
28 size_t http_request::input_handler::read_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
30 if(reinterpret_cast<http_request::input_handler*>(userdata)->canceled) return CURL_READFUNC_ABORT;
31 try {
32 return reinterpret_cast<http_request::input_handler*>(userdata)->read(ptr, size * nmemb);
33 } catch(...) {
34 return CURL_READFUNC_ABORT;
38 http_request::null_input_handler::~null_input_handler()
42 uint64_t http_request::null_input_handler::get_length()
44 return 0;
47 size_t http_request::null_input_handler::read(char* target, size_t maxread)
49 return 0;
52 http_request::output_handler::~output_handler()
56 size_t http_request::output_handler::write_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
58 if(reinterpret_cast<http_request::output_handler*>(userdata)->canceled) return 0;
59 try {
60 reinterpret_cast<http_request::output_handler*>(userdata)->write(ptr, size * nmemb);
61 return size * nmemb;
62 } catch(...) {
63 return 0;
67 size_t http_request::output_handler::header_fn(void* _ptr, size_t size, size_t nmemb, void* userdata)
69 size_t hsize = size * nmemb;
70 char* ptr = (char*)_ptr;
71 while(hsize > 0 && (ptr[hsize - 1] == '\r' || ptr[hsize - 1] == '\n')) hsize--;
72 char* split = strchr((char*)ptr, ':');
73 char* firstns = split;
74 if(firstns) {
75 firstns++;
76 while(*firstns && (*firstns == '\t' || *firstns == ' ')) firstns++;
78 char* end = (char*)ptr + hsize;
79 if(split == NULL)
80 reinterpret_cast<http_request::output_handler*>(userdata)->header("", std::string((char*)ptr, hsize));
81 else
82 reinterpret_cast<http_request::output_handler*>(userdata)->header(std::string((char*)ptr,
83 split - (char*)ptr), std::string(firstns, end - firstns));
84 return size * nmemb;
87 http_request::www_authenticate_extractor::www_authenticate_extractor(
88 std::function<void(const std::string& value)> _callback)
90 callback = _callback;
93 http_request::www_authenticate_extractor::~www_authenticate_extractor()
97 std::string http_strlower(const std::string& name)
99 std::string name2 = name;
100 for(size_t i = 0; i < name2.length(); i++)
101 if(name2[i] >= 65 && name2[i] <= 90) name2[i] = name2[i] + 32;
102 return name2;
105 void http_request::www_authenticate_extractor::header(const std::string& name, const std::string& content)
107 if(http_strlower(name) == "www-authenticate") callback(content);
110 void http_request::www_authenticate_extractor::write(const char* source, size_t srcsize)
112 //Do nothing.
116 http_request::~http_request()
118 if(handle)
119 curl_easy_cleanup((CURL*)handle);
122 http_request::http_request(const std::string& verb, const std::string& url)
124 dlnow = dltotal = ulnow = ultotal = 0;
125 handle = curl_easy_init();
126 if(!handle)
127 throw std::runtime_error("Can't initialize HTTP transfer");
128 has_body = false;
129 if(verb == "GET") {
130 } else if(verb == "HEAD") {
131 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOBODY, 1);
132 if(err) throw std::runtime_error(curl_easy_strerror(err));
133 } else if(verb == "POST") {
134 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_POST, 1);
135 if(err) throw std::runtime_error(curl_easy_strerror(err));
136 has_body = true;
137 } else if(verb == "PUT") {
138 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_PUT, 1);
139 if(err) throw std::runtime_error(curl_easy_strerror(err));
140 has_body = true;
141 } else
142 throw std::runtime_error("Unknown HTTP verb");
143 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_URL, url.c_str());
144 if(err) throw std::runtime_error(curl_easy_strerror(err));
147 void http_request::do_transfer(input_handler* inhandler, output_handler* outhandler)
149 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOPROGRESS, 0);
150 if(err) throw std::runtime_error(curl_easy_strerror(err));
151 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEFUNCTION, http_request::output_handler::write_fn);
152 if(err) throw std::runtime_error(curl_easy_strerror(err));
153 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEDATA, (void*)outhandler);
154 if(err) throw std::runtime_error(curl_easy_strerror(err));
155 if(has_body) {
156 err = curl_easy_setopt((CURL*)handle, CURLOPT_READFUNCTION,
157 http_request::input_handler::read_fn);
158 if(err) throw std::runtime_error(curl_easy_strerror(err));
159 err = curl_easy_setopt((CURL*)handle, CURLOPT_READDATA, (void*)inhandler);
160 if(err) throw std::runtime_error(curl_easy_strerror(err));
161 err = curl_easy_setopt((CURL*)handle, CURLOPT_INFILESIZE_LARGE,
162 (curl_off_t)inhandler->get_length());
163 if(err) throw std::runtime_error(curl_easy_strerror(err));
165 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSFUNCTION, &http_request::progress);
166 if(err) throw std::runtime_error(curl_easy_strerror(err));
167 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSDATA, (void*)this);
168 if(err) throw std::runtime_error(curl_easy_strerror(err));
169 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERFUNCTION, &http_request::output_handler::header_fn);
170 if(err) throw std::runtime_error(curl_easy_strerror(err));
171 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERDATA, (void*)outhandler);
172 if(err) throw std::runtime_error(curl_easy_strerror(err));
174 struct curl_slist* list = NULL;
175 if(authorization != "") {
176 std::string foo = "Authorization: " + authorization;
177 list = curl_slist_append(list, foo.c_str());
179 if(list) {
180 curl_easy_setopt((CURL*)handle, CURLOPT_HTTPHEADER, list);
183 err = curl_easy_perform((CURL*)handle);
184 if(err) throw std::runtime_error(curl_easy_strerror(err));
186 if(list)
187 curl_slist_free_all(list);
190 void http_request::global_init()
192 curl_global_init(CURL_GLOBAL_ALL);
195 int http_request::progress(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow)
197 return reinterpret_cast<http_request*>(userdata)->_progress(dltotal, dlnow, ultotal, ulnow);
200 int http_request::_progress(double _dltotal, double _dlnow, double _ultotal, double _ulnow)
202 dltotal = _dltotal;
203 dlnow = _dlnow;
204 ultotal = _ultotal;
205 ulnow = _ulnow;
206 return 0;
209 void http_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
211 dnow = dlnow;
212 dtotal = dltotal;
213 unow = ulnow;
214 utotal = ultotal;
217 uint32_t http_request::get_http_code()
219 long ret = 0;
220 curl_easy_getinfo((CURL*)handle, CURLINFO_RESPONSE_CODE, &ret);
221 return ret;
224 http_async_request::http_async_request()
226 ihandler = NULL;
227 ohandler = NULL;
228 final_dl = 0;
229 final_ul = 0;
230 finished = false;
231 req = NULL;
234 void http_async_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
236 threads::alock h(m);
237 if(req) {
238 req->get_xfer_status(dnow, dtotal, unow, utotal);
239 } else {
240 dnow = dtotal = final_dl;
241 unow = utotal = final_ul;
245 namespace
247 void async_http_trampoline(http_async_request* r)
249 try {
250 r->req->do_transfer(r->ihandler, r->ohandler);
251 } catch(std::exception& e) {
252 threads::alock h(r->m);
253 r->finished_cond.notify_all();
254 r->finished = true;
255 delete r->req;
256 r->req = NULL;
257 r->errormsg = e.what();
258 return;
260 int64_t tmp1, tmp2;
261 threads::alock h(r->m);
262 r->http_code = r->req->get_http_code();
263 r->req->get_xfer_status(r->final_dl, tmp1, r->final_ul, tmp2);
264 r->finished_cond.notify_all();
265 r->finished = true;
266 delete r->req;
267 r->req = NULL;
271 void http_async_request::lauch_async()
273 try {
275 threads::alock h(m);
276 req = new http_request(verb, url);
277 if(authorization != "") req->set_authorization(authorization);
279 (new threads::thread(async_http_trampoline, this))->detach();
280 } catch(std::exception& e) {
281 threads::alock h(m);
282 finished_cond.notify_all();
283 finished = true;
284 delete req;
285 req = NULL;
286 errormsg = e.what();
290 void http_async_request::cancel()
292 if(ihandler) ihandler->cancel();
293 if(ohandler) ohandler->cancel();
296 property_upload_request::property_upload_request()
298 state = 0;
299 sent = 0;
302 property_upload_request::~property_upload_request()
306 uint64_t property_upload_request::get_length()
308 uint64_t tmp = 0;
309 for(auto j : data) {
310 std::string X = (stringfmt() << "," << j.second.length() << ":").str();
311 tmp = tmp + X.length() + j.first.length() + j.second.length();
313 return tmp;
316 void property_upload_request::rewind()
318 state = 0;
319 sent = 0;
322 void property_upload_request::str_helper(const std::string& str, char*& target, size_t& maxread, size_t& x,
323 unsigned next)
325 size_t y = min((uint64_t)maxread, (uint64_t)(str.length() - sent));
326 if(y == 0) {
327 state = next;
328 sent = 0;
329 return;
331 std::copy(str.begin() + sent, str.begin() + sent + y, target);
332 target += y;
333 maxread -= y;
334 x += y;
335 sent += y;
338 void property_upload_request::chr_helper(char ch, char*& target, size_t& maxread, size_t& x, unsigned next)
340 *(target++) = ch;
341 maxread--;
342 x++;
343 state = next;
346 void property_upload_request::len_helper(size_t len, char*& target, size_t& maxread, size_t& x, unsigned next)
348 std::string tmp = (stringfmt() << len).str();
349 str_helper(tmp, target, maxread, x, next);
352 size_t property_upload_request::read(char* target, size_t maxread)
354 size_t x = 0;
355 while(maxread > 0) {
356 switch(state) {
357 case 0:
358 state = 1;
359 itr = data.begin();
360 break;
361 case 1:
362 if(itr == data.end()) {
363 state = 7;
364 } else
365 str_helper(itr->first, target, maxread, x, 2);
366 break;
367 case 2:
368 chr_helper('=', target, maxread, x, 3);
369 break;
370 case 3: //Length of value.
371 len_helper(itr->second.length(), target, maxread, x, 4);
372 break;
373 case 4: //The separator of value.
374 chr_helper(':', target, maxread, x, 5);
375 break;
376 case 5: //Value.
377 str_helper(itr->second, target, maxread, x, 6);
378 break;
379 case 6: //End of entry.
380 itr++;
381 state = 1;
382 break;
383 case 7:
384 return x;
387 return x;