aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llhttpassetstorage.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmessage/llhttpassetstorage.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llmessage/llhttpassetstorage.cpp')
-rw-r--r--linden/indra/llmessage/llhttpassetstorage.cpp1019
1 files changed, 1019 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llhttpassetstorage.cpp b/linden/indra/llmessage/llhttpassetstorage.cpp
new file mode 100644
index 0000000..2b64385
--- /dev/null
+++ b/linden/indra/llmessage/llhttpassetstorage.cpp
@@ -0,0 +1,1019 @@
1/**
2 * @file llhttpassetstorage.cpp
3 * @brief Subclass capable of loading asset data to/from an external
4 * source. Currently, a web server accessed via curl
5 *
6 * Copyright (c) 2003-2007, Linden Research, Inc.
7 *
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "linden_common.h"
30
31#include "llhttpassetstorage.h"
32
33#include "indra_constants.h"
34#include "llvfile.h"
35#include "llvfs.h"
36
37#include "zlib/zlib.h"
38
39const F32 MAX_PROCESSING_TIME = 0.005f;
40const S32 CURL_XFER_BUFFER_SIZE = 65536;
41// Try for 30 minutes for now.
42const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f;
43
44const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096;
45
46const S32 HTTP_OK = 200;
47const S32 HTTP_PUT_OK = 201;
48const S32 HTTP_NO_CONTENT = 204;
49const S32 HTTP_MISSING = 404;
50const S32 HTTP_SERVER_BAD_GATEWAY = 502;
51const S32 HTTP_SERVER_TEMP_UNAVAILABLE = 503;
52
53/////////////////////////////////////////////////////////////////////////////////
54// LLTempAssetData
55// An asset not stored on central asset store, but on a simulator node somewhere.
56/////////////////////////////////////////////////////////////////////////////////
57struct LLTempAssetData
58{
59 LLUUID mAssetID;
60 LLUUID mAgentID;
61 std::string mHostName;
62};
63
64/////////////////////////////////////////////////////////////////////////////////
65// LLHTTPAssetRequest
66/////////////////////////////////////////////////////////////////////////////////
67
68class LLHTTPAssetRequest : public LLAssetRequest
69{
70public:
71 LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi);
72 virtual ~LLHTTPAssetRequest();
73
74 void setupCurlHandle();
75
76 void prepareCompressedUpload();
77 void finishCompressedUpload();
78 size_t readCompressedData(void* data, size_t size);
79
80 static size_t curlCompressedUploadCallback(
81 void *data, size_t size, size_t nmemb, void *user_data);
82
83public:
84 LLHTTPAssetStorage *mAssetStoragep;
85
86 CURL *mCurlHandle;
87 CURLM *mCurlMultiHandle;
88 char *mURLBuffer;
89 struct curl_slist *mHTTPHeaders;
90 LLVFile *mVFile;
91 LLUUID mTmpUUID;
92 BOOL mIsUpload;
93 BOOL mIsLocalUpload;
94 BOOL mIsDownload;
95
96 bool mZInitialized;
97 z_stream mZStream;
98 char* mZInputBuffer;
99 bool mZInputExhausted;
100
101 FILE *mFP;
102};
103
104
105LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi)
106 : LLAssetRequest(uuid, type),
107 mZInitialized(false)
108{
109 mAssetStoragep = asp;
110 mCurlHandle = NULL;
111 mCurlMultiHandle = curl_multi;
112 mVFile = NULL;
113 mIsUpload = FALSE;
114 mIsLocalUpload = FALSE;
115 mIsDownload = FALSE;
116 mHTTPHeaders = NULL;
117
118 mURLBuffer = new char[strlen(url) + 1]; /*Flawfinder: ignore*/
119 if (mURLBuffer)
120 {
121 strcpy(mURLBuffer, url);
122 }
123}
124
125LLHTTPAssetRequest::~LLHTTPAssetRequest()
126{
127 // Cleanup/cancel the request
128 if (mCurlHandle)
129 {
130 curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle);
131 curl_easy_cleanup(mCurlHandle);
132 if (mAssetStoragep)
133 {
134 // Terminating a request. Thus upload or download is no longer pending.
135 if (mIsUpload)
136 {
137 mAssetStoragep->clearPendingUpload();
138 }
139 else if (mIsLocalUpload)
140 {
141 mAssetStoragep->clearPendingLocalUpload();
142 }
143 else if (mIsDownload)
144 {
145 mAssetStoragep->clearPendingDownload();
146 }
147 else
148 {
149 llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - Destroyed request is not upload OR download, this is bad!" << llendl;
150 }
151 }
152 else
153 {
154 llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << llendl;
155 }
156 }
157 if (mHTTPHeaders)
158 {
159 curl_slist_free_all(mHTTPHeaders);
160 }
161 delete[] mURLBuffer;
162 delete mVFile;
163 finishCompressedUpload();
164}
165
166void LLHTTPAssetRequest::setupCurlHandle()
167{
168 mCurlHandle = curl_easy_init();
169 curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
170 curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
171 curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer);
172 curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
173 if (mIsDownload)
174 {
175 curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
176 // only do this on downloads, as uploads
177 // to some apache configs (like our test grids)
178 // mistakenly claim the response is gzip'd if the resource
179 // name ends in .gz, even though in a PUT, the response is
180 // just plain HTML saying "created"
181 }
182 /* Remove the Pragma: no-cache header that libcurl inserts by default;
183 we want the cached version, if possible. */
184 if (mZInitialized)
185 {
186 curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
187 // disable use of proxy, which can't handle chunked transfers
188 }
189 mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
190 // resist the temptation to explicitly add the Transfer-Encoding: chunked
191 // header here - invokes a libCURL bug
192 curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
193 if (mAssetStoragep)
194 {
195 // Set the appropriate pending upload or download flag
196 if (mIsUpload)
197 {
198 mAssetStoragep->setPendingUpload();
199 }
200 else if (mIsLocalUpload)
201 {
202 mAssetStoragep->setPendingLocalUpload();
203 }
204 else if (mIsDownload)
205 {
206 mAssetStoragep->setPendingDownload();
207 }
208 else
209 {
210 llerrs << "LLHTTPAssetRequest::setupCurlHandle - Request is not upload OR download, this is bad!" << llendl;
211 }
212 }
213 else
214 {
215 llerrs << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << llendl;
216 }
217}
218
219void LLHTTPAssetRequest::prepareCompressedUpload()
220{
221 mZStream.next_in = Z_NULL;
222 mZStream.avail_in = 0;
223 mZStream.zalloc = Z_NULL;
224 mZStream.zfree = Z_NULL;
225 mZStream.opaque = Z_NULL;
226
227 int r = deflateInit2(&mZStream,
228 1, // compression level
229 Z_DEFLATED, // the only method defined
230 15 + 16, // the default windowBits + gzip header flag
231 8, // the default memLevel
232 Z_DEFAULT_STRATEGY);
233
234 if (r != Z_OK)
235 {
236 llerrs << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << llendl;
237 }
238
239 mZInitialized = true;
240 mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE];
241 mZInputExhausted = false;
242
243 mVFile = new LLVFile(gAssetStorage->mVFS,
244 getUUID(), getType(), LLVFile::READ);
245}
246
247void LLHTTPAssetRequest::finishCompressedUpload()
248{
249 if (mZInitialized)
250 {
251 llinfos << "LLHTTPAssetRequest::finishCompressedUpload: "
252 << "read " << mZStream.total_in << " byte asset file, "
253 << "uploaded " << mZStream.total_out << " byte compressed asset"
254 << llendl;
255
256 deflateEnd(&mZStream);
257 delete[] mZInputBuffer;
258 }
259}
260
261size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size)
262{
263 mZStream.next_out = (Bytef*)data;
264 mZStream.avail_out = size;
265
266 while (mZStream.avail_out > 0)
267 {
268 if (mZStream.avail_in == 0 && !mZInputExhausted)
269 {
270 S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
271 (S32)(mVFile->getSize() - mVFile->tell()));
272
273 mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/
274
275 mZStream.next_in = (Bytef*)mZInputBuffer;
276 mZStream.avail_in = mVFile->getLastBytesRead();
277
278 mZInputExhausted = mZStream.avail_in == 0;
279 }
280
281 int r = deflate(&mZStream,
282 mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
283
284 if (r == Z_STREAM_END)
285 {
286 break;
287 }
288 }
289
290 return size - mZStream.avail_out;
291}
292
293//static
294size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
295 void *data, size_t size, size_t nmemb, void *user_data)
296{
297 if (!gAssetStorage)
298 {
299 return 0;
300 }
301 CURL *curl_handle = (CURL *)user_data;
302 LLHTTPAssetRequest *req = NULL;
303 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
304
305 return req->readCompressedData(data, size * nmemb);
306}
307
308/////////////////////////////////////////////////////////////////////////////////
309// LLHTTPAssetStorage
310/////////////////////////////////////////////////////////////////////////////////
311
312
313LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
314 LLVFS *vfs, const LLHost &upstream_host,
315 const char *web_host,
316 const char *local_web_host,
317 const char *host_name)
318 : LLAssetStorage(msg, xfer, vfs, upstream_host)
319{
320 _init(web_host, local_web_host, host_name);
321}
322
323LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
324 LLVFS *vfs,
325 const char *web_host,
326 const char *local_web_host,
327 const char *host_name)
328 : LLAssetStorage(msg, xfer, vfs)
329{
330 _init(web_host, local_web_host, host_name);
331}
332
333void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, const char* host_name)
334{
335 mBaseURL = web_host;
336 mLocalBaseURL = local_web_host;
337 mHostName = host_name;
338
339 // Do not change this "unless you are familiar with and mean to control
340 // internal operations of libcurl"
341 // - http://curl.haxx.se/libcurl/c/curl_global_init.html
342 curl_global_init(CURL_GLOBAL_ALL);
343
344 mCurlMultiHandle = curl_multi_init();
345
346 mPendingDownload = FALSE;
347 mPendingUpload = FALSE;
348 mPendingLocalUpload = FALSE;
349}
350
351LLHTTPAssetStorage::~LLHTTPAssetStorage()
352{
353 curl_multi_cleanup(mCurlMultiHandle);
354 mCurlMultiHandle = NULL;
355
356 curl_global_cleanup();
357}
358
359// storing data is simpler than getting it, so we just overload the whole method
360void LLHTTPAssetStorage::storeAssetData(
361 const LLUUID& uuid,
362 LLAssetType::EType type,
363 LLAssetStorage::LLStoreAssetCallback callback,
364 void* user_data,
365 bool temp_file,
366 bool is_priority,
367 bool store_local,
368 const LLUUID& requesting_agent_id)
369{
370 if (mVFS->getExists(uuid, type))
371 {
372 LLAssetRequest *req = new LLAssetRequest(uuid, type);
373 req->mUpCallback = callback;
374 req->mUserData = user_data;
375 req->mRequestingAgentID = requesting_agent_id;
376
377 // this will get picked up and transmitted in checkForTimeouts
378 if(store_local)
379 {
380 mPendingLocalUploads.push_back(req);
381 }
382 else if(is_priority)
383 {
384 mPendingUploads.push_front(req);
385 }
386 else
387 {
388 mPendingUploads.push_back(req);
389 }
390 }
391 else
392 {
393 llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
394 if (callback)
395 {
396 callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE );
397 }
398 }
399}
400
401// virtual
402void LLHTTPAssetStorage::storeAssetData(
403 const char* filename,
404 const LLUUID& asset_id,
405 LLAssetType::EType asset_type,
406 LLStoreAssetCallback callback,
407 void* user_data,
408 bool temp_file,
409 bool is_priority)
410{
411 llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
412
413 LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
414
415 legacy->mUpCallback = callback;
416 legacy->mUserData = user_data;
417
418 FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
419 if (fp)
420 {
421 LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
422
423 fseek(fp, 0, SEEK_END);
424 S32 size = ftell(fp);
425 fseek(fp, 0, SEEK_SET);
426
427 file.setMaxSize(size);
428
429 const S32 buf_size = 65536;
430 U8 copy_buf[buf_size];
431 while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
432 {
433 file.write(copy_buf, size);
434 }
435 fclose(fp);
436
437 // if this upload fails, the caller needs to setup a new tempfile for us
438 if (temp_file)
439 {
440 LLFile::remove(filename);
441 }
442
443 storeAssetData(
444 asset_id,
445 asset_type,
446 legacyStoreDataCallback,
447 (void**)legacy,
448 temp_file,
449 is_priority);
450 }
451 else
452 {
453 if (callback)
454 {
455 callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE);
456 }
457 }
458}
459
460// internal requester, used by getAssetData in superclass
461void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
462 void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
463 void *user_data, BOOL duplicate,
464 BOOL is_priority)
465{
466 // stash the callback info so we can find it after we get the response message
467 LLAssetRequest *req = new LLAssetRequest(uuid, type);
468 req->mDownCallback = callback;
469 req->mUserData = user_data;
470 req->mIsPriority = is_priority;
471
472 // this will get picked up and downloaded in checkForTimeouts
473
474 //
475 // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK! Asset requests were taking too long and timing out.
476 // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add
477 // non-texture requests to the front, and add texture requests to the back. The theory is
478 // that we always want them first, even if they're out of order.
479 //
480
481 if (req->getType() == LLAssetType::AT_TEXTURE)
482 {
483 mPendingDownloads.push_back(req);
484 }
485 else
486 {
487 mPendingDownloads.push_front(req);
488 }
489}
490
491// overloaded to additionally move data to/from the webserver
492void LLHTTPAssetStorage::checkForTimeouts()
493{
494 LLAssetRequest *req = NULL;
495 if (mPendingDownloads.size() > 0 && !mPendingDownload)
496 {
497 req = mPendingDownloads.front();
498 // Setup this curl download request
499 // We need to generate a new request here
500 // since the one in the list could go away
501 char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
502 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
503 req->getUUID().toString(uuid_str);
504 std::string base_url = getBaseURL(req->getUUID(), req->getType());
505 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
506
507 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
508 new_req->mTmpUUID.generate();
509 new_req->mIsDownload = TRUE;
510
511 // Sets pending download flag internally
512 new_req->setupCurlHandle();
513 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
514 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
515 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
516
517 curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
518 llinfos << "Requesting " << new_req->mURLBuffer << llendl;
519
520 }
521
522
523 if (mPendingUploads.size() > 0 && !mPendingUpload)
524 {
525 req = mPendingUploads.front();
526 // setup this curl upload request
527
528 bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
529
530 char tmp_url[MAX_STRING];/*Flawfinder: ignore*/
531 char uuid_str[UUID_STR_LENGTH];/*Flawfinder: ignore*/
532 req->getUUID().toString(uuid_str);
533 snprintf(tmp_url, sizeof(tmp_url), /*Flawfinder: ignore*/
534 do_compress ? "%s/%s.%s.gz" : "%s/%s.%s",
535 mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));
536
537 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
538 new_req->mIsUpload = TRUE;
539 if (do_compress)
540 {
541 new_req->prepareCompressedUpload();
542 }
543
544 // Sets pending upload flag internally
545 new_req->setupCurlHandle();
546 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
547 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
548
549 if (do_compress)
550 {
551 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
552 &LLHTTPAssetRequest::curlCompressedUploadCallback);
553 }
554 else
555 {
556 LLVFile file(mVFS, req->getUUID(), req->getType());
557 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
558 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
559 &curlUpCallback);
560 }
561 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
562
563 curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
564 llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl;
565 // Pending upload will have been flagged by the request
566 }
567
568
569 if (mPendingLocalUploads.size() > 0 && !mPendingLocalUpload)
570 {
571 req = mPendingLocalUploads.front();
572 // setup this curl upload request
573 LLVFile file(mVFS, req->getUUID(), req->getType());
574
575 char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
576 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
577 req->getUUID().toString(uuid_str);
578
579 // KLW - All temporary uploads are saved locally "http://localhost:12041/asset"
580 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
581
582 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
583 new_req->mIsLocalUpload = TRUE;
584 new_req->mRequestingAgentID = req->mRequestingAgentID;
585
586 // Sets pending upload flag internally
587 new_req->setupCurlHandle();
588 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
589 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
590 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
591 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
592 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
593
594 curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
595 llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
596 << " Requesting PUT " << new_req->mURLBuffer << llendl;
597 // Pending upload will have been flagged by the request
598 }
599 S32 count = 0;
600 CURLMcode mcode;
601 int queue_length;
602 do
603 {
604 mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
605 count++;
606 } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
607
608 CURLMsg *curl_msg;
609 do
610 {
611 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
612 if (curl_msg && curl_msg->msg == CURLMSG_DONE)
613 {
614 long curl_result = 0;
615 S32 xfer_result = 0;
616
617 LLHTTPAssetRequest *req = NULL;
618 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
619
620 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
621 if (req->mIsUpload || req->mIsLocalUpload)
622 {
623 if (curl_msg->data.result == CURLE_OK && (curl_result == HTTP_OK || curl_result == HTTP_PUT_OK || curl_result == HTTP_NO_CONTENT))
624 {
625 llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
626 if (req->mIsLocalUpload)
627 {
628 addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
629 }
630 }
631 else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
632 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
633 curl_result == HTTP_SERVER_BAD_GATEWAY ||
634 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
635 {
636 llwarns << "Re-requesting upload for " << req->getUUID() << ". Received upload error to " << req->mURLBuffer <<
637 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
638 }
639 else
640 {
641 llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
642 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
643
644 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
645 }
646
647 if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
648 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
649 curl_result == HTTP_SERVER_BAD_GATEWAY ||
650 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
651 {
652 // shared upload finished callback
653 // in the base class, this is called from processUploadComplete
654 _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0));
655 // Pending upload flag will get cleared when the request is deleted
656 }
657 }
658 else if (req->mIsDownload)
659 {
660 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
661 {
662 if (req->mVFile && req->mVFile->getSize() > 0)
663 {
664 llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
665
666 req->mVFile->rename(req->getUUID(), req->getType());
667 }
668 else
669 {
670 // TODO: if this actually indicates a bad asset on the server
671 // (not certain at this point), then delete it
672 llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
673 xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
674 }
675 }
676 else
677 {
678 // KLW - TAT See if an avatar owns this texture, and if so request re-upload.
679 llwarns << "Failure downloading " << req->mURLBuffer <<
680 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
681
682 xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
683
684 if (req->mVFile)
685 {
686 req->mVFile->remove();
687 }
688 }
689
690 // call the static callback for transfer completion
691 // this will cleanup all requests for this asset, including ours
692 downloadCompleteCallback(
693 xfer_result,
694 req->getUUID(),
695 req->getType(),
696 (void *)req);
697 // Pending download flag will get cleared when the request is deleted
698 }
699 else
700 {
701 // nothing, just axe this request
702 // currently this can only mean an asset delete
703 }
704
705 // Deleting clears the pending upload/download flag if it's set and the request is transferring
706 delete req;
707 req = NULL;
708 }
709
710 } while (curl_msg && queue_length > 0);
711
712
713 LLAssetStorage::checkForTimeouts();
714}
715
716// static
717size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
718{
719 if (!gAssetStorage)
720 {
721 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
722 return 0;
723 }
724 S32 bytes = (S32)(size * nmemb);
725 CURL *curl_handle = (CURL *)user_data;
726 LLHTTPAssetRequest *req = NULL;
727 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
728
729 if (! req->mVFile)
730 {
731 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
732 }
733
734 double content_length = 0.0;
735 curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
736
737 // sanitize content_length, reconcile w/ actual data
738 S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
739
740 req->mVFile->setMaxSize(file_length);
741 req->mVFile->write((U8*)data, bytes);
742
743 return nmemb;
744}
745
746// static
747size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
748{
749 if (!gAssetStorage)
750 {
751 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
752 return 0;
753 }
754 CURL *curl_handle = (CURL *)user_data;
755 LLHTTPAssetRequest *req = NULL;
756 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
757
758 if (! req->mVFile)
759 {
760 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
761 }
762
763 S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
764
765 req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/
766
767 return req->mVFile->getLastBytesRead();
768}
769
770// static
771size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
772{
773 // do nothing, this is here to soak up script output so it doesn't end up on stdout
774
775 return nmemb;
776}
777
778
779
780// blocking asset fetch which bypasses the VFS
781// this is a very limited function for use by the simstate loader and other one-offs
782S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata)
783{
784 // *NOTE: There is no guarantee that the uuid and the asset_type match
785 // - not that it matters. - Doug
786 lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
787
788 FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
789 if (! fp)
790 {
791 llwarns << "Failed to open " << filename << " for writing" << llendl;
792 return LL_ERR_ASSET_REQUEST_FAILED;
793 }
794
795 // make sure we use the normal curl setup, even though we don't really need a request object
796 LLHTTPAssetRequest req(this, uuid, asset_type, url.c_str(), mCurlMultiHandle);
797 req.mFP = fp;
798 req.mIsDownload = TRUE;
799
800 req.setupCurlHandle();
801 curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
802 curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
803 curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
804
805 curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
806 llinfos << "Requesting as file " << req.mURLBuffer << llendl;
807
808 // braindead curl loop
809 int queue_length;
810 CURLMsg *curl_msg;
811 LLTimer timeout;
812 timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
813 bool success = false;
814 S32 xfer_result = 0;
815 do
816 {
817 curl_multi_perform(mCurlMultiHandle, &queue_length);
818 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
819
820 if (callback)
821 {
822 callback(userdata);
823 }
824
825 if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
826 {
827 success = true;
828 }
829 else if (timeout.hasExpired())
830 {
831 llwarns << "Request for " << url << " has timed out." << llendl;
832 success = false;
833 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
834 break;
835 }
836 } while (!success);
837
838 if (success)
839 {
840 long curl_result = 0;
841 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
842
843 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
844 {
845 S32 size = ftell(req.mFP);
846 if (size > 0)
847 {
848 // everything seems to be in order
849 llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
850 }
851 else
852 {
853 llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
854 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
855 }
856 }
857 else
858 {
859 xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
860 llinfos << "Failure downloading " << req.mURLBuffer <<
861 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
862 }
863 }
864
865 fclose(fp);
866 if (xfer_result)
867 {
868 LLFile::remove(filename);
869 }
870 return xfer_result;
871}
872
873
874// static
875size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
876{
877 CURL *curl_handle = (CURL *)user_data;
878 LLHTTPAssetRequest *req = NULL;
879 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
880
881 if (! req->mFP)
882 {
883 llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
884 return 0;
885 }
886
887 return fwrite(data, size, nmemb, req->mFP);
888}
889
890// virtual
891void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
892{
893 if (agent_id.isNull() || asset_id.isNull())
894 {
895 llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << llendl;
896 return;
897 }
898
899 LLTempAssetData temp_asset_data;
900 temp_asset_data.mAssetID = asset_id;
901 temp_asset_data.mAgentID = agent_id;
902 temp_asset_data.mHostName = host_name;
903
904 mTempAssets[asset_id] = temp_asset_data;
905}
906
907// virtual
908BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
909{
910 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
911 BOOL found = (citer != mTempAssets.end());
912 return found;
913}
914
915// virtual
916std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
917{
918 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
919 if (citer != mTempAssets.end())
920 {
921 return citer->second.mHostName;
922 }
923 else
924 {
925 return std::string();
926 }
927}
928
929// virtual
930LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
931{
932 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
933 if (citer != mTempAssets.end())
934 {
935 return citer->second.mAgentID;
936 }
937 else
938 {
939 return LLUUID::null;
940 }
941}
942
943// virtual
944void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
945{
946 mTempAssets.erase(asset_id);
947}
948
949// virtual
950void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
951{
952 uuid_tempdata_map::iterator it = mTempAssets.begin();
953 uuid_tempdata_map::iterator end = mTempAssets.end();
954
955 while (it != end)
956 {
957 const LLTempAssetData& asset_data = it->second;
958 if (asset_data.mAgentID == agent_id)
959 {
960 mTempAssets.erase(it++);
961 }
962 else
963 {
964 ++it;
965 }
966 }
967}
968
969std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
970{
971 if (LLAssetType::AT_TEXTURE == asset_type)
972 {
973 uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
974 if (citer != mTempAssets.end())
975 {
976 const std::string& host_name = citer->second.mHostName;
977 std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
978 return url;
979 }
980 }
981
982 return mBaseURL;
983}
984
985void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
986{
987 uuid_tempdata_map::const_iterator it = mTempAssets.begin();
988 uuid_tempdata_map::const_iterator end = mTempAssets.end();
989 S32 count = 0;
990 for ( ; it != end; ++it)
991 {
992 const LLTempAssetData& temp_asset_data = it->second;
993 if (avatar_id.isNull()
994 || avatar_id == temp_asset_data.mAgentID)
995 {
996 llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
997 << " texture " << temp_asset_data.mAssetID
998 << " host " << temp_asset_data.mHostName
999 << llendl;
1000 count++;
1001 }
1002 }
1003
1004 if (avatar_id.isNull())
1005 {
1006 llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
1007 }
1008 else
1009 {
1010 llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
1011 }
1012}
1013
1014void LLHTTPAssetStorage::clearTempAssetData()
1015{
1016 llinfos << "TAT: Clearing temp asset data map" << llendl;
1017 mTempAssets.clear();
1018}
1019