Upload UI
[lsnes.git] / src / library / httpreq.cpp
blob9107ce44abb20c3f370167f040119420a5d5f114
1 #include <cstdio>
2 #include "httpreq.hpp"
3 #include "httpauth.hpp"
4 #include "string.hpp"
5 #include "minmax.hpp"
6 #include "threadtypes.hpp"
7 #include "streamcompress.hpp"
8 #include <curl/curl.h>
9 #include <cstring>
10 #include <fstream>
11 #include <boost/iostreams/categories.hpp>
12 #include <boost/iostreams/copy.hpp>
13 #include <boost/iostreams/stream.hpp>
14 #include <boost/iostreams/stream_buffer.hpp>
15 #include <boost/iostreams/filter/symmetric.hpp>
16 #include <boost/iostreams/filter/zlib.hpp>
17 #include <boost/iostreams/filtering_stream.hpp>
18 #include <boost/iostreams/device/back_inserter.hpp>
20 http_request::input_handler::~input_handler()
24 size_t http_request::input_handler::read_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
26 if(reinterpret_cast<http_request::input_handler*>(userdata)->canceled) return CURL_READFUNC_ABORT;
27 try {
28 return reinterpret_cast<http_request::input_handler*>(userdata)->read(ptr, size * nmemb);
29 } catch(...) {
30 return CURL_READFUNC_ABORT;
34 http_request::null_input_handler::~null_input_handler()
38 uint64_t http_request::null_input_handler::get_length()
40 return 0;
43 size_t http_request::null_input_handler::read(char* target, size_t maxread)
45 return 0;
48 http_request::output_handler::~output_handler()
52 size_t http_request::output_handler::write_fn(char* ptr, size_t size, size_t nmemb, void* userdata)
54 if(reinterpret_cast<http_request::output_handler*>(userdata)->canceled) return 0;
55 try {
56 reinterpret_cast<http_request::output_handler*>(userdata)->write(ptr, size * nmemb);
57 return size * nmemb;
58 } catch(...) {
59 return 0;
63 size_t http_request::output_handler::header_fn(void* _ptr, size_t size, size_t nmemb, void* userdata)
65 size_t hsize = size * nmemb;
66 char* ptr = (char*)_ptr;
67 while(hsize > 0 && (ptr[hsize - 1] == '\r' || ptr[hsize - 1] == '\n')) hsize--;
68 char* split = strchr((char*)ptr, ':');
69 char* firstns = split;
70 if(firstns) {
71 firstns++;
72 while(*firstns && (*firstns == '\t' || *firstns == ' ')) firstns++;
74 char* end = (char*)ptr + hsize;
75 if(split == NULL)
76 reinterpret_cast<http_request::output_handler*>(userdata)->header("", std::string((char*)ptr, hsize));
77 else
78 reinterpret_cast<http_request::output_handler*>(userdata)->header(std::string((char*)ptr,
79 split - (char*)ptr), std::string(firstns, end - firstns));
80 return size * nmemb;
83 http_request::www_authenticate_extractor::www_authenticate_extractor(
84 std::function<void(const std::string& value)> _callback)
86 callback = _callback;
89 http_request::www_authenticate_extractor::~www_authenticate_extractor()
93 std::string http_strlower(const std::string& name)
95 std::string name2 = name;
96 for(size_t i = 0; i < name2.length(); i++)
97 if(name2[i] >= 65 && name2[i] <= 90) name2[i] = name2[i] + 32;
98 return name2;
101 void http_request::www_authenticate_extractor::header(const std::string& name, const std::string& content)
103 if(http_strlower(name) == "www-authenticate") callback(content);
106 void http_request::www_authenticate_extractor::write(const char* source, size_t srcsize)
108 //Do nothing.
112 http_request::~http_request()
114 if(handle)
115 curl_easy_cleanup((CURL*)handle);
118 http_request::http_request(const std::string& verb, const std::string& url)
120 dlnow = dltotal = ulnow = ultotal = 0;
121 handle = curl_easy_init();
122 if(!handle)
123 throw std::runtime_error("Can't initialize HTTP transfer");
124 has_body = false;
125 if(verb == "GET") {
126 } else if(verb == "HEAD") {
127 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOBODY, 1);
128 if(err) throw std::runtime_error(curl_easy_strerror(err));
129 } else if(verb == "POST") {
130 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_POST, 1);
131 if(err) throw std::runtime_error(curl_easy_strerror(err));
132 has_body = true;
133 } else if(verb == "PUT") {
134 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_PUT, 1);
135 if(err) throw std::runtime_error(curl_easy_strerror(err));
136 has_body = true;
137 } else
138 throw std::runtime_error("Unknown HTTP verb");
139 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_URL, url.c_str());
140 if(err) throw std::runtime_error(curl_easy_strerror(err));
143 void http_request::do_transfer(input_handler* inhandler, output_handler* outhandler)
145 auto err = curl_easy_setopt((CURL*)handle, CURLOPT_NOPROGRESS, 0);
146 if(err) throw std::runtime_error(curl_easy_strerror(err));
147 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEFUNCTION, http_request::output_handler::write_fn);
148 if(err) throw std::runtime_error(curl_easy_strerror(err));
149 err = curl_easy_setopt((CURL*)handle, CURLOPT_WRITEDATA, (void*)outhandler);
150 if(err) throw std::runtime_error(curl_easy_strerror(err));
151 if(has_body) {
152 err = curl_easy_setopt((CURL*)handle, CURLOPT_READFUNCTION,
153 http_request::input_handler::read_fn);
154 if(err) throw std::runtime_error(curl_easy_strerror(err));
155 err = curl_easy_setopt((CURL*)handle, CURLOPT_READDATA, (void*)inhandler);
156 if(err) throw std::runtime_error(curl_easy_strerror(err));
157 err = curl_easy_setopt((CURL*)handle, CURLOPT_INFILESIZE_LARGE,
158 (curl_off_t)inhandler->get_length());
159 if(err) throw std::runtime_error(curl_easy_strerror(err));
161 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSFUNCTION, &http_request::progress);
162 if(err) throw std::runtime_error(curl_easy_strerror(err));
163 err = curl_easy_setopt((CURL*)handle, CURLOPT_PROGRESSDATA, (void*)this);
164 if(err) throw std::runtime_error(curl_easy_strerror(err));
165 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERFUNCTION, &http_request::output_handler::header_fn);
166 if(err) throw std::runtime_error(curl_easy_strerror(err));
167 err = curl_easy_setopt((CURL*)handle, CURLOPT_HEADERDATA, (void*)outhandler);
168 if(err) throw std::runtime_error(curl_easy_strerror(err));
170 struct curl_slist* list = NULL;
171 if(authorization != "") {
172 std::string foo = "Authorization: " + authorization;
173 list = curl_slist_append(list, foo.c_str());
175 if(list) {
176 curl_easy_setopt((CURL*)handle, CURLOPT_HTTPHEADER, list);
179 err = curl_easy_perform((CURL*)handle);
180 if(err) throw std::runtime_error(curl_easy_strerror(err));
182 if(list)
183 curl_slist_free_all(list);
186 void http_request::global_init()
188 curl_global_init(CURL_GLOBAL_ALL);
191 int http_request::progress(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow)
193 return reinterpret_cast<http_request*>(userdata)->_progress(dltotal, dlnow, ultotal, ulnow);
196 int http_request::_progress(double _dltotal, double _dlnow, double _ultotal, double _ulnow)
198 dltotal = _dltotal;
199 dlnow = _dlnow;
200 ultotal = _ultotal;
201 ulnow = _ulnow;
204 void http_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
206 dnow = dlnow;
207 dtotal = dltotal;
208 unow = ulnow;
209 utotal = ultotal;
212 long http_request::get_http_code()
214 long ret = 0;
215 curl_easy_getinfo((CURL*)handle, CURLINFO_RESPONSE_CODE, &ret);
216 return ret;
219 http_async_request::http_async_request()
221 ihandler = NULL;
222 ohandler = NULL;
223 final_dl = 0;
224 final_ul = 0;
225 finished = false;
226 req = NULL;
229 void http_async_request::get_xfer_status(int64_t& dnow, int64_t& dtotal, int64_t& unow, int64_t& utotal)
231 umutex_class h(m);
232 if(req) {
233 req->get_xfer_status(dnow, dtotal, unow, utotal);
234 } else {
235 dnow = dtotal = final_dl;
236 unow = utotal = final_ul;
240 namespace
242 void async_http_trampoline(http_async_request* r)
244 try {
245 r->req->do_transfer(r->ihandler, r->ohandler);
246 } catch(std::exception& e) {
247 umutex_class h(r->m);
248 r->finished_cond.notify_all();
249 r->finished = true;
250 delete r->req;
251 r->req = NULL;
252 r->errormsg = e.what();
253 return;
255 int64_t tmp1, tmp2;
256 umutex_class h(r->m);
257 r->http_code = r->req->get_http_code();
258 r->req->get_xfer_status(r->final_dl, tmp1, r->final_ul, tmp2);
259 r->finished_cond.notify_all();
260 r->finished = true;
261 delete r->req;
262 r->req = NULL;
266 void http_async_request::lauch_async()
268 try {
270 umutex_class h(m);
271 req = new http_request(verb, url);
272 if(authorization != "") req->set_authorization(authorization);
274 (new thread_class(async_http_trampoline, this))->detach();
275 } catch(std::exception& e) {
276 umutex_class h(m);
277 finished_cond.notify_all();
278 finished = true;
279 delete req;
280 req = NULL;
281 errormsg = e.what();
285 void http_async_request::cancel()
287 if(ihandler) ihandler->cancel();
288 if(ohandler) ohandler->cancel();
291 property_upload_request::property_upload_request()
293 state = 0;
294 sent = 0;
297 property_upload_request::~property_upload_request()
301 uint64_t property_upload_request::get_length()
303 uint64_t tmp = 0;
304 for(auto j : data) {
305 std::string X = (stringfmt() << "," << j.second.length() << ":").str();
306 tmp = tmp + X.length() + j.first.length() + j.second.length();
308 return tmp;
311 void property_upload_request::rewind()
313 state = 0;
314 sent = 0;
317 void property_upload_request::str_helper(const std::string& str, char*& target, size_t& maxread, size_t& x,
318 unsigned next)
320 size_t y = min((uint64_t)maxread, (uint64_t)(str.length() - sent));
321 if(y == 0) {
322 state = next;
323 sent = 0;
324 return;
326 std::copy(str.begin() + sent, str.begin() + sent + y, target);
327 target += y;
328 maxread -= y;
329 x += y;
330 sent += y;
333 void property_upload_request::chr_helper(char ch, char*& target, size_t& maxread, size_t& x, unsigned next)
335 *(target++) = ch;
336 maxread--;
337 x++;
338 state = next;
341 void property_upload_request::len_helper(size_t len, char*& target, size_t& maxread, size_t& x, unsigned next)
343 std::string tmp = (stringfmt() << len).str();
344 str_helper(tmp, target, maxread, x, next);
347 size_t property_upload_request::read(char* target, size_t maxread)
349 size_t x = 0;
350 size_t y;
351 while(maxread > 0) {
352 switch(state) {
353 case 0:
354 state = 1;
355 itr = data.begin();
356 break;
357 case 1:
358 if(itr == data.end()) {
359 state = 7;
360 } else
361 str_helper(itr->first, target, maxread, x, 2);
362 break;
363 case 2:
364 chr_helper('=', target, maxread, x, 3);
365 break;
366 case 3: //Length of value.
367 len_helper(itr->second.length(), target, maxread, x, 4);
368 break;
369 case 4: //The separator of value.
370 chr_helper(':', target, maxread, x, 5);
371 break;
372 case 5: //Value.
373 str_helper(itr->second, target, maxread, x, 6);
374 break;
375 case 6: //End of entry.
376 itr++;
377 state = 1;
378 break;
379 case 7:
380 return x;
383 return x;