framework2

Форк
0
416 строк · 10.3 Кб
1
#include "ofURLFileLoader.h"
2
#include "ofAppRunner.h"
3
#include "ofUtils.h"
4

5
using std::move;
6
using std::set;
7
using std::string;
8
using std::map;
9

10
#if !defined(TARGET_IMPLEMENTS_URL_LOADER)
11
	#include <curl/curl.h>
12
	#include "ofThreadChannel.h"
13
	#include "ofThread.h"
14
	static bool curlInited = false;
15
#endif
16

17
int	ofHttpRequest::nextID = 0;
18

19
ofEvent<ofHttpResponse> & ofURLResponseEvent(){
20
	static ofEvent<ofHttpResponse> * event = new ofEvent<ofHttpResponse>;
21
	return *event;
22
}
23

24

25

26

27
#if !defined(TARGET_IMPLEMENTS_URL_LOADER)
28
class ofURLFileLoaderImpl: public ofThread, public ofBaseURLFileLoader{
29
public:
30
	ofURLFileLoaderImpl();
31
	~ofURLFileLoaderImpl();
32
	ofHttpResponse get(const string& url);
33
	int getAsync(const string& url, const string& name=""); // returns id
34
	ofHttpResponse saveTo(const string& url, const of::filesystem::path& path);
35
	int saveAsync(const string& url, const of::filesystem::path& path);
36
	void remove(int id);
37
	void clear();
38
	void stop();
39
	ofHttpResponse handleRequest(const ofHttpRequest & request);
40
	int handleRequestAsync(const ofHttpRequest& request); // returns id
41

42
protected:
43
	// threading -----------------------------------------------
44
	void threadedFunction();
45
	void start();
46
	void update(ofEventArgs & args);  // notify in update so the notification is thread safe
47

48
private:
49
	// perform the requests on the thread
50

51
	ofThreadChannel<ofHttpRequest> requests;
52
	ofThreadChannel<ofHttpResponse> responses;
53
	ofThreadChannel<int> cancelRequestQueue;
54
	set<int> cancelledRequests;
55
};
56

57
ofURLFileLoaderImpl::ofURLFileLoaderImpl() {
58
	if(!curlInited){
59
		 curl_global_init(CURL_GLOBAL_ALL);
60
	}
61
}
62

63
ofURLFileLoaderImpl::~ofURLFileLoaderImpl(){
64
	clear();
65
	stop();
66
}
67

68
ofHttpResponse ofURLFileLoaderImpl::get(const string& url) {
69
	ofHttpRequest request(url,url);
70
	return handleRequest(request);
71
}
72

73

74
int ofURLFileLoaderImpl::getAsync(const string& url, const string& name){
75
	ofHttpRequest request(url, name.empty() ? url : name);
76
	requests.send(request);
77
	start();
78
	return request.getId();
79
}
80

81

82
ofHttpResponse ofURLFileLoaderImpl::saveTo(const string& url, const of::filesystem::path& path){
83
	ofHttpRequest request(url,path.string(),true);
84
	return handleRequest(request);
85
}
86

87
int ofURLFileLoaderImpl::saveAsync(const string& url, const of::filesystem::path& path){
88
	ofHttpRequest request(url,path.string(),true);
89
	requests.send(request);
90
	start();
91
	return request.getId();
92
}
93

94
void ofURLFileLoaderImpl::remove(int id){
95
	cancelRequestQueue.send(id);
96
}
97

98
void ofURLFileLoaderImpl::clear(){
99
	ofHttpResponse resp;
100
	ofHttpRequest req;
101
	while(requests.tryReceive(req)){}
102
	while(responses.tryReceive(resp)){}
103
}
104

105
void ofURLFileLoaderImpl::start() {
106
	 if (!isThreadRunning()){
107
		ofAddListener(ofEvents().update,this,&ofURLFileLoaderImpl::update);
108
		startThread();
109
	}
110
}
111

112
void ofURLFileLoaderImpl::stop() {
113
	stopThread();
114
	requests.close();
115
	responses.close();
116
	waitForThread();
117
}
118

119
void ofURLFileLoaderImpl::threadedFunction() {
120
	setThreadName("ofURLFileLoader " + ofToString(getThreadId()));
121
	while( isThreadRunning() ){
122
		int cancelled=0;
123
		while(cancelRequestQueue.tryReceive(cancelled)){
124
			cancelledRequests.insert(cancelled);
125
		}
126
		ofHttpRequest request;
127
		if(requests.receive(request)){
128
			if(cancelledRequests.find(request.getId())==cancelledRequests.end()){
129
				ofHttpResponse response(handleRequest(request));
130
				int status = response.status;
131
				if(!responses.send(move(response))){
132
					break;
133
				}
134
				if(status==-1){
135
					// retry
136
					requests.send(request);
137
				}
138
			}else{
139
				cancelledRequests.erase(cancelled);
140
			}
141
		}else{
142
			break;
143
		}
144
	}
145
}
146

147
namespace{
148
	size_t saveToFile_cb(void *buffer, size_t size, size_t nmemb, void *userdata){
149
		auto saveTo = (ofFile*)userdata;
150
		saveTo->write((const char*)buffer, size * nmemb);
151
		return size * nmemb;
152
	}
153

154
	size_t saveToMemory_cb(void *buffer, size_t size, size_t nmemb, void *userdata){
155
		auto response = (ofHttpResponse*)userdata;
156
		response->data.append((const char*)buffer, size * nmemb);
157
		return size * nmemb;
158
	}
159

160
    size_t readBody_cb(void *ptr, size_t size, size_t nmemb, void *userdata){
161
        auto body = (std::string*)userdata;
162

163
        if(size*nmemb < 1){
164
            return 0;
165
        }
166

167
        if(!body->empty()) {
168
            auto sent = std::min(size * nmemb, body->size());
169
            memcpy(ptr, body->c_str(), sent);
170
            *body = body->substr(sent);
171
            return sent;
172
        }
173

174
        return 0;                          /* no more data left to deliver */
175
    }
176
}
177

178
ofHttpResponse ofURLFileLoaderImpl::handleRequest(const ofHttpRequest & request) {
179
	std::unique_ptr<CURL, void(*)(CURL*)> curl =
180
		std::unique_ptr<CURL, void(*)(CURL*)>(curl_easy_init(), curl_easy_cleanup);
181
	curl_slist *headers = nullptr;
182
	curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, true);
183
	curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 2);
184
	curl_easy_setopt(curl.get(), CURLOPT_URL, request.url.c_str());
185

186
	// always follow redirections
187
	curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
188

189
	// Set content type and any other header
190
	if(request.contentType!=""){
191
		headers = curl_slist_append(headers, ("Content-Type: " + request.contentType).c_str());
192
	}
193
	for(map<string,string>::const_iterator it = request.headers.cbegin(); it!=request.headers.cend(); it++){
194
		headers = curl_slist_append(headers, (it->first + ": " +it->second).c_str());
195
	}
196

197
	curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers);
198

199
    std::string body = request.body;
200

201
	// set body if there's any
202
	if(request.body!=""){
203
//		curl_easy_setopt(curl.get(), CURLOPT_UPLOAD, 1L); // Tis does PUT instead of POST
204
		curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, request.body.size());
205
		curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, nullptr);
206
		//curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, request.body.c_str());
207
		curl_easy_setopt(curl.get(), CURLOPT_READFUNCTION, readBody_cb);
208
		curl_easy_setopt(curl.get(), CURLOPT_READDATA, &body);
209
	}else{
210
//		curl_easy_setopt(curl.get(), CURLOPT_UPLOAD, 0L);
211
		curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, 0);
212
        //curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, nullptr);
213
        curl_easy_setopt(curl.get(), CURLOPT_READFUNCTION, nullptr);
214
        curl_easy_setopt(curl.get(), CURLOPT_READDATA, nullptr);
215
	}
216
	if(request.method == ofHttpRequest::GET){
217
		curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1);
218
		curl_easy_setopt(curl.get(), CURLOPT_POST, 0);
219
	}else{
220
		curl_easy_setopt(curl.get(), CURLOPT_POST, 1);
221
		curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 0);
222
	}
223

224
    if(request.timeoutSeconds>0){
225
        curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, request.timeoutSeconds);
226
    }
227

228
	// start request and receive response
229
	ofHttpResponse response(request, 0, "");
230
	CURLcode err = CURLE_OK;
231
	if(request.saveTo){
232
		ofFile saveTo(request.name, ofFile::WriteOnly, true);
233
		curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &saveTo);
234
		curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, saveToFile_cb);
235
		err = curl_easy_perform(curl.get());
236
	}else{
237
		curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);
238
		curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, saveToMemory_cb);
239
		err = curl_easy_perform(curl.get());
240
	}
241
	if(err==CURLE_OK){
242
		long http_code = 0;
243
		curl_easy_getinfo (curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
244
		response.status = http_code;
245
	}else{
246
		response.error = curl_easy_strerror(err);
247
		response.status = -1;
248
	}
249

250
	if(headers){
251
		curl_slist_free_all(headers);
252
	}
253

254
	return response;
255
}
256

257

258
int ofURLFileLoaderImpl::handleRequestAsync(const ofHttpRequest& request){
259
	requests.send(request);
260
	start();
261
	return request.getId();
262
}
263

264
void ofURLFileLoaderImpl::update(ofEventArgs & args){
265
	ofHttpResponse response;
266
	while(responses.tryReceive(response)){
267
		try{
268
			response.request.done(response);
269
		}catch(...){
270

271
		}
272

273
		ofNotifyEvent(ofURLResponseEvent(),response);
274
	}
275

276
}
277

278
ofURLFileLoader::ofURLFileLoader()
279
:impl(new ofURLFileLoaderImpl){}
280
#endif
281

282
#ifdef TARGET_EMSCRIPTEN
283
#include "ofxEmscriptenURLFileLoader.h"
284
ofURLFileLoader::ofURLFileLoader()
285
:impl(new ofxEmscriptenURLFileLoader){}
286
#endif
287

288
ofHttpResponse ofURLFileLoader::get(const string& url){
289
	return impl->get(url);
290
}
291

292
int ofURLFileLoader::getAsync(const string& url, const string& name){
293
	return impl->getAsync(url,name);
294
}
295

296
ofHttpResponse ofURLFileLoader::saveTo(const string& url, const of::filesystem::path & path){
297
	return impl->saveTo(url,path);
298
}
299

300
int ofURLFileLoader::saveAsync(const string& url, const of::filesystem::path & path){
301
	return impl->saveAsync(url,path);
302
}
303

304
void ofURLFileLoader::remove(int id){
305
	impl->remove(id);
306
}
307

308
void ofURLFileLoader::clear(){
309
	impl->clear();
310
}
311

312
void ofURLFileLoader::stop(){
313
	impl->stop();
314
}
315

316
ofHttpResponse ofURLFileLoader::handleRequest(const ofHttpRequest & request){
317
	return impl->handleRequest(request);
318
}
319

320
int ofURLFileLoader::handleRequestAsync(const ofHttpRequest& request){
321
	return impl->handleRequestAsync(request);
322
}
323

324
static bool initialized = false;
325
static ofURLFileLoader & getFileLoader(){
326
	static ofURLFileLoader * fileLoader = new ofURLFileLoader;
327
	initialized = true;
328
	return *fileLoader;
329
}
330

331

332
ofHttpRequest::ofHttpRequest()
333
:saveTo(false)
334
,method(GET)
335
,id(nextID++)
336
{
337
}
338

339
ofHttpRequest::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

348
int ofHttpRequest::getId() const {
349
	return id;
350
}
351

352
int ofHttpRequest::getID(){
353
	return id;
354
}
355

356

357
ofHttpResponse::ofHttpResponse()
358
:status(0)
359
{
360
}
361

362
ofHttpResponse::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

370
ofHttpResponse::ofHttpResponse(const ofHttpRequest& request, int status, const string& error)
371
:request(request)
372
,status(status)
373
,error(error)
374
{
375
}
376

377
ofHttpResponse::operator ofBuffer&(){
378
	return data;
379
}
380

381

382

383
ofHttpResponse ofLoadURL(const string& url){
384
	return getFileLoader().get(url);
385
}
386

387
int ofLoadURLAsync(const string&  url, const string&  name){
388
	return getFileLoader().getAsync(url,name);
389
}
390

391
ofHttpResponse ofSaveURLTo(const string& url, const of::filesystem::path& path){
392
	return getFileLoader().saveTo(url,path);
393
}
394

395
int ofSaveURLAsync(const string& url, const of::filesystem::path& path){
396
	return getFileLoader().saveAsync(url,path);
397
}
398

399
void ofRemoveURLRequest(int id){
400
	getFileLoader().remove(id);
401
}
402

403
void ofRemoveAllURLRequests(){
404
	getFileLoader().clear();
405
}
406

407
void ofStopURLLoader(){
408
	getFileLoader().stop();
409
}
410

411
void ofURLFileLoaderShutdown(){
412
	if(initialized){
413
		ofRemoveAllURLRequests();
414
		ofStopURLLoader();
415
	}
416
}
417

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.