Implicitly redirect cmdhelp includes to corresponding JSON files
[lsnes.git] / src / library / httpreq.cpp
blob9b5623358ec4d5c6d15b85eae2cd1cf743e11ed1
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 <cstring>
13 #include <fstream>
14 #include <boost/iostreams/categories.hpp>
15 #include <boost/iostreams/copy.hpp>
16 #include <boost/iostreams/stream.hpp>
17 #include <boost/iostreams/stream_buffer.hpp>
18 #include <boost/iostreams/filter/symmetric.hpp>
19 #include <boost/iostreams/filter/zlib.hpp>
20 #include <boost/iostreams/filtering_stream.hpp>
21 #include <boost/iostreams/device/back_inserter.hpp>
23 http_request::input_handler::~input_handler()
27 size_t http_request::input_handler::read_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
29 if(reinterpret_cast<http_request::input_handler*>(userdata)->canceled) return CURL_READFUNC_ABORT;
30 try {
31 return reinterpret_cast<http_request::input_handler*>(userdata)->read(ptr, size * nmemb);
32 } catch(...) {
33 return CURL_READFUNC_ABORT;
37 http_request::null_input_handler::~null_input_handler()
41 uint64_t http_request::null_input_handler::get_length()
43 return 0;
46 size_t http_request::null_input_handler::read(char* target, size_t maxread)
48 return 0;
51 http_request::output_handler::~output_handler()
55 size_t http_request::output_handler::write_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
57 if(reinterpret_cast<http_request::output_handler*>(userdata)->canceled) return 0;
58 try {
59 reinterpret_cast<http_request::output_handler*>(userdata)->write(ptr, size * nmemb);
60 return size * nmemb;
61 } catch(...) {
62 return 0;
66 size_t http_request::output_handler::header_fn(void* _ptr, size_t size, size_t nmemb, void* userdata)
68 size_t hsize = size * nmemb;
69 char* ptr = (char*)_ptr;
70 while(hsize > 0 && (ptr[hsize - 1] == '\r' || ptr[hsize - 1] == '\n')) hsize--;
71 char* split = strchr((char*)ptr, ':');
72 char* firstns = split;
73 if(firstns) {
74 firstns++;
75 while(*firstns && (*firstns == '\t' || *firstns == ' ')) firstns++;
77 char* end = (char*)ptr + hsize;
78 if(split == NULL)
79 reinterpret_cast<http_request::output_handler*>(userdata)->header("", std::string((char*)ptr, hsize));
80 else
81 reinterpret_cast<http_request::output_handler*>(userdata)->header(std::string((char*)ptr,
82 split - (char*)ptr), std::string(firstns, end - firstns));
83 return size * nmemb;
86 http_request::www_authenticate_extractor::www_authenticate_extractor(
87 std::function<void(const std::string& value)> _callback)
89 callback = _callback;
92 http_request::www_authenticate_extractor::~www_authenticate_extractor()
96 std::string http_strlower(const std::string& name)
98 std::string name2 = name;
99 for(size_t i = 0; i < name2.length(); i++)
100 if(name2[i] >= 65 && name2[i] <= 90) name2[i] = name2[i] + 32;
101 return name2;
104 void http_request::www_authenticate_extractor::header(const std::string& name, const std::string& content)
106 if(http_strlower(name) == "www-authenticate") callback(content);
109 void http_request::www_authenticate_extractor::write(const char* source, size_t srcsize)
111 //Do nothing.
115 http_request::~http_request()
117 if(handle)
118 curl_easy_cleanup((CURL*)handle);
121 http_request::http_request(const std::string& verb, const std::string& url)
123 dlnow = dltotal = ulnow = ultotal = 0;
124 handle = curl_easy_init();
125 if(!handle)
126 throw std::runtime_error("Can't initialize HTTP transfer");
127 has_body = false;
128 if(verb == "GET") {
129 } else if(verb == "HEAD") {
130 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOBODY, 1);
131 if(err) throw std::runtime_error(curl_easy_strerror(err));
132 } else if(verb == "POST") {
133 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_POST, 1);
134 if(err) throw std::runtime_error(curl_easy_strerror(err));
135 has_body = true;
136 } else if(verb == "PUT") {
137 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_PUT, 1);
138 if(err) throw std::runtime_error(curl_easy_strerror(err));
139 has_body = true;
140 } else
141 throw std::runtime_error("Unknown HTTP verb");
142 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_URL, url.c_str());
143 if(err) throw std::runtime_error(curl_easy_strerror(err));
146 void http_request::do_transfer(input_handler* inhandler, output_handler* outhandler)
148 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOPROGRESS, 0);
149 if(err) throw std::runtime_error(curl_easy_strerror(err));
150 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEFUNCTION, http_request::output_handler::write_fn);
151 if(err) throw std::runtime_error(curl_easy_strerror(err));
152 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEDATA, (void*)outhandler);
153 if(err) throw std::runtime_error(curl_easy_strerror(err));
154 if(has_body) {
155 err = curl_easy_setopt((CURL*)handle, CURLOPT_READFUNCTION,
156 http_request::input_handler::read_fn);
157 if(err) throw std::runtime_error(curl_easy_strerror(err));
158 err = curl_easy_setopt((CURL*)handle, CURLOPT_READDATA, (void*)inhandler);
159 if(err) throw std::runtime_error(curl_easy_strerror(err));
160 err = curl_easy_setopt((CURL*)handle, CURLOPT_INFILESIZE_LARGE,
161 (curl_off_t)inhandler->get_length());
162 if(err) throw std::runtime_error(curl_easy_strerror(err));
164 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSFUNCTION, &http_request::progress);
165 if(err) throw std::runtime_error(curl_easy_strerror(err));
166 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSDATA, (void*)this);
167 if(err) throw std::runtime_error(curl_easy_strerror(err));
168 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERFUNCTION, &http_request::output_handler::header_fn);
169 if(err) throw std::runtime_error(curl_easy_strerror(err));
170 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERDATA, (void*)outhandler);
171 if(err) throw std::runtime_error(curl_easy_strerror(err));
173 struct curl_slist* list = NULL;
174 if(authorization != "") {
175 std::string foo = "Authorization: " + authorization;
176 list = curl_slist_append(list, foo.c_str());
178 if(list) {
179 curl_easy_setopt((CURL*)handle, CURLOPT_HTTPHEADER, list);
182 err = curl_easy_perform((CURL*)handle);
183 if(err) throw std::runtime_error(curl_easy_strerror(err));
185 if(list)
186 curl_slist_free_all(list);
189 void http_request::global_init()
191 curl_global_init(CURL_GLOBAL_ALL);
194 int http_request::progress(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow)
196 return reinterpret_cast<http_request*>(userdata)->_progress(dltotal, dlnow, ultotal, ulnow);
199 int http_request::_progress(double _dltotal, double _dlnow, double _ultotal, double _ulnow)
201 dltotal = _dltotal;
202 dlnow = _dlnow;
203 ultotal = _ultotal;
204 ulnow = _ulnow;
205 return 0;
208 void http_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
210 dnow = dlnow;
211 dtotal = dltotal;
212 unow = ulnow;
213 utotal = ultotal;
216 long http_request::get_http_code()
218 long ret = 0;
219 curl_easy_getinfo((CURL*)handle, CURLINFO_RESPONSE_CODE, &ret);
220 return ret;
223 http_async_request::http_async_request()
225 ihandler = NULL;
226 ohandler = NULL;
227 final_dl = 0;
228 final_ul = 0;
229 finished = false;
230 req = NULL;
233 void http_async_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
235 threads::alock h(m);
236 if(req) {
237 req->get_xfer_status(dnow, dtotal, unow, utotal);
238 } else {
239 dnow = dtotal = final_dl;
240 unow = utotal = final_ul;
244 namespace
246 void async_http_trampoline(http_async_request* r)
248 try {
249 r->req->do_transfer(r->ihandler, r->ohandler);
250 } catch(std::exception& e) {
251 threads::alock h(r->m);
252 r->finished_cond.notify_all();
253 r->finished = true;
254 delete r->req;
255 r->req = NULL;
256 r->errormsg = e.what();
257 return;
259 int64_t tmp1, tmp2;
260 threads::alock h(r->m);
261 r->http_code = r->req->get_http_code();
262 r->req->get_xfer_status(r->final_dl, tmp1, r->final_ul, tmp2);
263 r->finished_cond.notify_all();
264 r->finished = true;
265 delete r->req;
266 r->req = NULL;
270 void http_async_request::lauch_async()
272 try {
274 threads::alock h(m);
275 req = new http_request(verb, url);
276 if(authorization != "") req->set_authorization(authorization);
278 (new threads::thread(async_http_trampoline, this))->detach();
279 } catch(std::exception& e) {
280 threads::alock h(m);
281 finished_cond.notify_all();
282 finished = true;
283 delete req;
284 req = NULL;
285 errormsg = e.what();
289 void http_async_request::cancel()
291 if(ihandler) ihandler->cancel();
292 if(ohandler) ohandler->cancel();
295 property_upload_request::property_upload_request()
297 state = 0;
298 sent = 0;
301 property_upload_request::~property_upload_request()
305 uint64_t property_upload_request::get_length()
307 uint64_t tmp = 0;
308 for(auto j : data) {
309 std::string X = (stringfmt() << "," << j.second.length() << ":").str();
310 tmp = tmp + X.length() + j.first.length() + j.second.length();
312 return tmp;
315 void property_upload_request::rewind()
317 state = 0;
318 sent = 0;
321 void property_upload_request::str_helper(const std::string& str, char*& target, size_t& maxread, size_t& x,
322 unsigned next)
324 size_t y = min((uint64_t)maxread, (uint64_t)(str.length() - sent));
325 if(y == 0) {
326 state = next;
327 sent = 0;
328 return;
330 std::copy(str.begin() + sent, str.begin() + sent + y, target);
331 target += y;
332 maxread -= y;
333 x += y;
334 sent += y;
337 void property_upload_request::chr_helper(char ch, char*& target, size_t& maxread, size_t& x, unsigned next)
339 *(target++) = ch;
340 maxread--;
341 x++;
342 state = next;
345 void property_upload_request::len_helper(size_t len, char*& target, size_t& maxread, size_t& x, unsigned next)
347 std::string tmp = (stringfmt() << len).str();
348 str_helper(tmp, target, maxread, x, next);
351 size_t property_upload_request::read(char* target, size_t maxread)
353 size_t x = 0;
354 while(maxread > 0) {
355 switch(state) {
356 case 0:
357 state = 1;
358 itr = data.begin();
359 break;
360 case 1:
361 if(itr == data.end()) {
362 state = 7;
363 } else
364 str_helper(itr->first, target, maxread, x, 2);
365 break;
366 case 2:
367 chr_helper('=', target, maxread, x, 3);
368 break;
369 case 3: //Length of value.
370 len_helper(itr->second.length(), target, maxread, x, 4);
371 break;
372 case 4: //The separator of value.
373 chr_helper(':', target, maxread, x, 5);
374 break;
375 case 5: //Value.
376 str_helper(itr->second, target, maxread, x, 6);
377 break;
378 case 6: //End of entry.
379 itr++;
380 state = 1;
381 break;
382 case 7:
383 return x;
386 return x;