framework2
416 строк · 10.3 Кб
1#include "ofURLFileLoader.h"2#include "ofAppRunner.h"3#include "ofUtils.h"4
5using std::move;6using std::set;7using std::string;8using std::map;9
10#if !defined(TARGET_IMPLEMENTS_URL_LOADER)11#include <curl/curl.h>12#include "ofThreadChannel.h"13#include "ofThread.h"14static bool curlInited = false;15#endif16
17int ofHttpRequest::nextID = 0;18
19ofEvent<ofHttpResponse> & ofURLResponseEvent(){20static ofEvent<ofHttpResponse> * event = new ofEvent<ofHttpResponse>;21return *event;22}
23
24
25
26
27#if !defined(TARGET_IMPLEMENTS_URL_LOADER)28class ofURLFileLoaderImpl: public ofThread, public ofBaseURLFileLoader{29public:30ofURLFileLoaderImpl();31~ofURLFileLoaderImpl();32ofHttpResponse get(const string& url);33int getAsync(const string& url, const string& name=""); // returns id34ofHttpResponse saveTo(const string& url, const of::filesystem::path& path);35int saveAsync(const string& url, const of::filesystem::path& path);36void remove(int id);37void clear();38void stop();39ofHttpResponse handleRequest(const ofHttpRequest & request);40int handleRequestAsync(const ofHttpRequest& request); // returns id41
42protected:43// threading -----------------------------------------------44void threadedFunction();45void start();46void update(ofEventArgs & args); // notify in update so the notification is thread safe47
48private:49// perform the requests on the thread50
51ofThreadChannel<ofHttpRequest> requests;52ofThreadChannel<ofHttpResponse> responses;53ofThreadChannel<int> cancelRequestQueue;54set<int> cancelledRequests;55};56
57ofURLFileLoaderImpl::ofURLFileLoaderImpl() {58if(!curlInited){59curl_global_init(CURL_GLOBAL_ALL);60}61}
62
63ofURLFileLoaderImpl::~ofURLFileLoaderImpl(){64clear();65stop();66}
67
68ofHttpResponse ofURLFileLoaderImpl::get(const string& url) {69ofHttpRequest request(url,url);70return handleRequest(request);71}
72
73
74int ofURLFileLoaderImpl::getAsync(const string& url, const string& name){75ofHttpRequest request(url, name.empty() ? url : name);76requests.send(request);77start();78return request.getId();79}
80
81
82ofHttpResponse ofURLFileLoaderImpl::saveTo(const string& url, const of::filesystem::path& path){83ofHttpRequest request(url,path.string(),true);84return handleRequest(request);85}
86
87int ofURLFileLoaderImpl::saveAsync(const string& url, const of::filesystem::path& path){88ofHttpRequest request(url,path.string(),true);89requests.send(request);90start();91return request.getId();92}
93
94void ofURLFileLoaderImpl::remove(int id){95cancelRequestQueue.send(id);96}
97
98void ofURLFileLoaderImpl::clear(){99ofHttpResponse resp;100ofHttpRequest req;101while(requests.tryReceive(req)){}102while(responses.tryReceive(resp)){}103}
104
105void ofURLFileLoaderImpl::start() {106if (!isThreadRunning()){107ofAddListener(ofEvents().update,this,&ofURLFileLoaderImpl::update);108startThread();109}110}
111
112void ofURLFileLoaderImpl::stop() {113stopThread();114requests.close();115responses.close();116waitForThread();117}
118
119void ofURLFileLoaderImpl::threadedFunction() {120setThreadName("ofURLFileLoader " + ofToString(getThreadId()));121while( isThreadRunning() ){122int cancelled=0;123while(cancelRequestQueue.tryReceive(cancelled)){124cancelledRequests.insert(cancelled);125}126ofHttpRequest request;127if(requests.receive(request)){128if(cancelledRequests.find(request.getId())==cancelledRequests.end()){129ofHttpResponse response(handleRequest(request));130int status = response.status;131if(!responses.send(move(response))){132break;133}134if(status==-1){135// retry136requests.send(request);137}138}else{139cancelledRequests.erase(cancelled);140}141}else{142break;143}144}145}
146
147namespace{148size_t saveToFile_cb(void *buffer, size_t size, size_t nmemb, void *userdata){149auto saveTo = (ofFile*)userdata;150saveTo->write((const char*)buffer, size * nmemb);151return size * nmemb;152}153
154size_t saveToMemory_cb(void *buffer, size_t size, size_t nmemb, void *userdata){155auto response = (ofHttpResponse*)userdata;156response->data.append((const char*)buffer, size * nmemb);157return size * nmemb;158}159
160size_t readBody_cb(void *ptr, size_t size, size_t nmemb, void *userdata){161auto body = (std::string*)userdata;162
163if(size*nmemb < 1){164return 0;165}166
167if(!body->empty()) {168auto sent = std::min(size * nmemb, body->size());169memcpy(ptr, body->c_str(), sent);170*body = body->substr(sent);171return sent;172}173
174return 0; /* no more data left to deliver */175}176}
177
178ofHttpResponse ofURLFileLoaderImpl::handleRequest(const ofHttpRequest & request) {179std::unique_ptr<CURL, void(*)(CURL*)> curl =180std::unique_ptr<CURL, void(*)(CURL*)>(curl_easy_init(), curl_easy_cleanup);181curl_slist *headers = nullptr;182curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, true);183curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 2);184curl_easy_setopt(curl.get(), CURLOPT_URL, request.url.c_str());185
186// always follow redirections187curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);188
189// Set content type and any other header190if(request.contentType!=""){191headers = curl_slist_append(headers, ("Content-Type: " + request.contentType).c_str());192}193for(map<string,string>::const_iterator it = request.headers.cbegin(); it!=request.headers.cend(); it++){194headers = curl_slist_append(headers, (it->first + ": " +it->second).c_str());195}196
197curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers);198
199std::string body = request.body;200
201// set body if there's any202if(request.body!=""){203// curl_easy_setopt(curl.get(), CURLOPT_UPLOAD, 1L); // Tis does PUT instead of POST
204curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, request.body.size());205curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, nullptr);206//curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, request.body.c_str());207curl_easy_setopt(curl.get(), CURLOPT_READFUNCTION, readBody_cb);208curl_easy_setopt(curl.get(), CURLOPT_READDATA, &body);209}else{210// curl_easy_setopt(curl.get(), CURLOPT_UPLOAD, 0L);
211curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, 0);212//curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, nullptr);213curl_easy_setopt(curl.get(), CURLOPT_READFUNCTION, nullptr);214curl_easy_setopt(curl.get(), CURLOPT_READDATA, nullptr);215}216if(request.method == ofHttpRequest::GET){217curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1);218curl_easy_setopt(curl.get(), CURLOPT_POST, 0);219}else{220curl_easy_setopt(curl.get(), CURLOPT_POST, 1);221curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 0);222}223
224if(request.timeoutSeconds>0){225curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, request.timeoutSeconds);226}227
228// start request and receive response229ofHttpResponse response(request, 0, "");230CURLcode err = CURLE_OK;231if(request.saveTo){232ofFile saveTo(request.name, ofFile::WriteOnly, true);233curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &saveTo);234curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, saveToFile_cb);235err = curl_easy_perform(curl.get());236}else{237curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);238curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, saveToMemory_cb);239err = curl_easy_perform(curl.get());240}241if(err==CURLE_OK){242long http_code = 0;243curl_easy_getinfo (curl.get(), CURLINFO_RESPONSE_CODE, &http_code);244response.status = http_code;245}else{246response.error = curl_easy_strerror(err);247response.status = -1;248}249
250if(headers){251curl_slist_free_all(headers);252}253
254return response;255}
256
257
258int ofURLFileLoaderImpl::handleRequestAsync(const ofHttpRequest& request){259requests.send(request);260start();261return request.getId();262}
263
264void ofURLFileLoaderImpl::update(ofEventArgs & args){265ofHttpResponse response;266while(responses.tryReceive(response)){267try{268response.request.done(response);269}catch(...){270
271}272
273ofNotifyEvent(ofURLResponseEvent(),response);274}275
276}
277
278ofURLFileLoader::ofURLFileLoader()279:impl(new ofURLFileLoaderImpl){}280#endif281
282#ifdef TARGET_EMSCRIPTEN283#include "ofxEmscriptenURLFileLoader.h"284ofURLFileLoader::ofURLFileLoader()285:impl(new ofxEmscriptenURLFileLoader){}286#endif287
288ofHttpResponse ofURLFileLoader::get(const string& url){289return impl->get(url);290}
291
292int ofURLFileLoader::getAsync(const string& url, const string& name){293return impl->getAsync(url,name);294}
295
296ofHttpResponse ofURLFileLoader::saveTo(const string& url, const of::filesystem::path & path){297return impl->saveTo(url,path);298}
299
300int ofURLFileLoader::saveAsync(const string& url, const of::filesystem::path & path){301return impl->saveAsync(url,path);302}
303
304void ofURLFileLoader::remove(int id){305impl->remove(id);306}
307
308void ofURLFileLoader::clear(){309impl->clear();310}
311
312void ofURLFileLoader::stop(){313impl->stop();314}
315
316ofHttpResponse ofURLFileLoader::handleRequest(const ofHttpRequest & request){317return impl->handleRequest(request);318}
319
320int ofURLFileLoader::handleRequestAsync(const ofHttpRequest& request){321return impl->handleRequestAsync(request);322}
323
324static bool initialized = false;325static ofURLFileLoader & getFileLoader(){326static ofURLFileLoader * fileLoader = new ofURLFileLoader;327initialized = true;328return *fileLoader;329}
330
331
332ofHttpRequest::ofHttpRequest()333:saveTo(false)334,method(GET)335,id(nextID++)336{
337}
338
339ofHttpRequest::ofHttpRequest(const string& url, const string& name,bool saveTo)340:url(url)341,name(name)342,saveTo(saveTo)343,method(GET)344,id(nextID++)345{
346}
347
348int ofHttpRequest::getId() const {349return id;350}
351
352int ofHttpRequest::getID(){353return id;354}
355
356
357ofHttpResponse::ofHttpResponse()358:status(0)359{
360}
361
362ofHttpResponse::ofHttpResponse(const ofHttpRequest& request, const ofBuffer& data, int status, const string& error)363:request(request)364,data(data)365,status(status)366,error(error)367{
368}
369
370ofHttpResponse::ofHttpResponse(const ofHttpRequest& request, int status, const string& error)371:request(request)372,status(status)373,error(error)374{
375}
376
377ofHttpResponse::operator ofBuffer&(){378return data;379}
380
381
382
383ofHttpResponse ofLoadURL(const string& url){384return getFileLoader().get(url);385}
386
387int ofLoadURLAsync(const string& url, const string& name){388return getFileLoader().getAsync(url,name);389}
390
391ofHttpResponse ofSaveURLTo(const string& url, const of::filesystem::path& path){392return getFileLoader().saveTo(url,path);393}
394
395int ofSaveURLAsync(const string& url, const of::filesystem::path& path){396return getFileLoader().saveAsync(url,path);397}
398
399void ofRemoveURLRequest(int id){400getFileLoader().remove(id);401}
402
403void ofRemoveAllURLRequests(){404getFileLoader().clear();405}
406
407void ofStopURLLoader(){408getFileLoader().stop();409}
410
411void ofURLFileLoaderShutdown(){412if(initialized){413ofRemoveAllURLRequests();414ofStopURLLoader();415}416}
417