llhttpclient.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:17k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llhttpclient.cpp
  3.  * @brief Implementation of classes for making HTTP requests.
  4.  *
  5.  * $LicenseInfo:firstyear=2006&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2006-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "linden_common.h"
  33. #include "llhttpclient.h"
  34. #include "llassetstorage.h"
  35. #include "lliopipe.h"
  36. #include "llurlrequest.h"
  37. #include "llbufferstream.h"
  38. #include "llsdserialize.h"
  39. #include "llvfile.h"
  40. #include "llvfs.h"
  41. #include "lluri.h"
  42. #include "message.h"
  43. #include <curl/curl.h>
  44. const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
  45. ////////////////////////////////////////////////////////////////////////////
  46. // Responder class moved to LLCurl
  47. namespace
  48. {
  49. class LLHTTPClientURLAdaptor : public LLURLRequestComplete
  50. {
  51. public:
  52. LLHTTPClientURLAdaptor(LLCurl::ResponderPtr responder)
  53. : LLURLRequestComplete(), mResponder(responder), mStatus(499),
  54.   mReason("LLURLRequest complete w/no status")
  55. {
  56. }
  57. ~LLHTTPClientURLAdaptor()
  58. {
  59. }
  60. virtual void httpStatus(U32 status, const std::string& reason)
  61. {
  62. LLURLRequestComplete::httpStatus(status,reason);
  63. mStatus = status;
  64. mReason = reason;
  65. }
  66. virtual void complete(const LLChannelDescriptors& channels,
  67.   const buffer_ptr_t& buffer)
  68. {
  69. if (mResponder.get())
  70. {
  71. mResponder->completedRaw(mStatus, mReason, channels, buffer);
  72. mResponder->completedHeader(mStatus, mReason, mHeaderOutput);
  73. }
  74. }
  75. virtual void header(const std::string& header, const std::string& value)
  76. {
  77. mHeaderOutput[header] = value;
  78. }
  79. private:
  80. LLCurl::ResponderPtr mResponder;
  81. U32 mStatus;
  82. std::string mReason;
  83. LLSD mHeaderOutput;
  84. };
  85. class Injector : public LLIOPipe
  86. {
  87. public:
  88. virtual const char* contentType() = 0;
  89. };
  90. class LLSDInjector : public Injector
  91. {
  92. public:
  93. LLSDInjector(const LLSD& sd) : mSD(sd) {}
  94. virtual ~LLSDInjector() {}
  95. const char* contentType() { return "application/llsd+xml"; }
  96. virtual EStatus process_impl(const LLChannelDescriptors& channels,
  97. buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
  98. {
  99. LLBufferStream ostream(channels, buffer.get());
  100. LLSDSerialize::toXML(mSD, ostream);
  101. eos = true;
  102. return STATUS_DONE;
  103. }
  104. const LLSD mSD;
  105. };
  106. class RawInjector : public Injector
  107. {
  108. public:
  109. RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {}
  110. virtual ~RawInjector() {delete mData;}
  111. const char* contentType() { return "application/octet-stream"; }
  112. virtual EStatus process_impl(const LLChannelDescriptors& channels,
  113. buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
  114. {
  115. LLBufferStream ostream(channels, buffer.get());
  116. ostream.write((const char *)mData, mSize);  // hopefully chars are always U8s
  117. eos = true;
  118. return STATUS_DONE;
  119. }
  120. const U8* mData;
  121. S32 mSize;
  122. };
  123. class FileInjector : public Injector
  124. {
  125. public:
  126. FileInjector(const std::string& filename) : mFilename(filename) {}
  127. virtual ~FileInjector() {}
  128. const char* contentType() { return "application/octet-stream"; }
  129. virtual EStatus process_impl(const LLChannelDescriptors& channels,
  130. buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
  131. {
  132. LLBufferStream ostream(channels, buffer.get());
  133. llifstream fstream(mFilename, std::iostream::binary | std::iostream::out);
  134. if(fstream.is_open())
  135. {
  136. fstream.seekg(0, std::ios::end);
  137. U32 fileSize = fstream.tellg();
  138. fstream.seekg(0, std::ios::beg);
  139. std::vector<char> fileBuffer(fileSize);
  140. fstream.read(&fileBuffer[0], fileSize);
  141. ostream.write(&fileBuffer[0], fileSize);
  142. fstream.close();
  143. eos = true;
  144. return STATUS_DONE;
  145. }
  146. return STATUS_ERROR;
  147. }
  148. const std::string mFilename;
  149. };
  150. class VFileInjector : public Injector
  151. {
  152. public:
  153. VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {}
  154. virtual ~VFileInjector() {}
  155. const char* contentType() { return "application/octet-stream"; }
  156. virtual EStatus process_impl(const LLChannelDescriptors& channels,
  157. buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
  158. {
  159. LLBufferStream ostream(channels, buffer.get());
  160. LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ);
  161. S32 fileSize = vfile.getSize();
  162. U8* fileBuffer;
  163. fileBuffer = new U8 [fileSize];
  164.             vfile.read(fileBuffer, fileSize);
  165.             ostream.write((char*)fileBuffer, fileSize);
  166. eos = true;
  167. return STATUS_DONE;
  168. }
  169. const LLUUID mUUID;
  170. LLAssetType::EType mAssetType;
  171. };
  172. LLPumpIO* theClientPump = NULL;
  173. }
  174. static void request(
  175. const std::string& url,
  176. LLURLRequest::ERequestAction method,
  177. Injector* body_injector,
  178. LLCurl::ResponderPtr responder,
  179. const F32 timeout = HTTP_REQUEST_EXPIRY_SECS,
  180. const LLSD& headers = LLSD())
  181. {
  182. if (!LLHTTPClient::hasPump())
  183. {
  184. responder->completed(U32_MAX, "No pump", LLSD());
  185. return;
  186. }
  187. LLPumpIO::chain_t chain;
  188. LLURLRequest* req = new LLURLRequest(method, url);
  189. req->checkRootCertificate(LLCurl::getSSLVerify());
  190. lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " "
  191. << headers << llendl;
  192.     // Insert custom headers is the caller sent any
  193.     if (headers.isMap())
  194.     {
  195.         LLSD::map_const_iterator iter = headers.beginMap();
  196.         LLSD::map_const_iterator end  = headers.endMap();
  197.         for (; iter != end; ++iter)
  198.         {
  199.             std::ostringstream header;
  200.             //if the header is "Pragma" with no value
  201.             //the caller intends to force libcurl to drop
  202.             //the Pragma header it so gratuitously inserts
  203.             //Before inserting the header, force libcurl
  204.             //to not use the proxy (read: llurlrequest.cpp)
  205. static const std::string PRAGMA("Pragma");
  206. if ((iter->first == PRAGMA) && (iter->second.asString().empty()))
  207.             {
  208.                 req->useProxy(false);
  209.             }
  210.             header << iter->first << ": " << iter->second.asString() ;
  211.             lldebugs << "header = " << header.str() << llendl;
  212.             req->addHeader(header.str().c_str());
  213.         }
  214.     }
  215. // Check to see if we have already set Accept or not. If no one
  216. // set it, set it to application/llsd+xml since that's what we
  217. // almost always want.
  218. if( method != LLURLRequest::HTTP_PUT && method != LLURLRequest::HTTP_POST )
  219. {
  220. static const std::string ACCEPT("Accept");
  221. if(!headers.has(ACCEPT))
  222. {
  223. req->addHeader("Accept: application/llsd+xml");
  224. }
  225. }
  226. if (responder)
  227. {
  228. responder->setURL(url);
  229. }
  230. req->setCallback(new LLHTTPClientURLAdaptor(responder));
  231. if (method == LLURLRequest::HTTP_POST  &&  gMessageSystem)
  232. {
  233. req->addHeader(llformat("X-SecondLife-UDP-Listen-Port: %d",
  234. gMessageSystem->mPort).c_str());
  235.     }
  236. if (method == LLURLRequest::HTTP_PUT || method == LLURLRequest::HTTP_POST)
  237. {
  238. static const std::string CONTENT_TYPE("Content-Type");
  239. if(!headers.has(CONTENT_TYPE))
  240. {
  241. // If the Content-Type header was passed in, it has
  242. // already been added as a header through req->addHeader
  243. // in the loop above. We defer to the caller's wisdom, but
  244. // if they did not specify a Content-Type, then ask the
  245. // injector.
  246. req->addHeader(
  247. llformat(
  248. "Content-Type: %s",
  249. body_injector->contentType()).c_str());
  250. }
  251.     chain.push_back(LLIOPipe::ptr_t(body_injector));
  252. }
  253. chain.push_back(LLIOPipe::ptr_t(req));
  254. theClientPump->addChain(chain, timeout);
  255. }
  256. void LLHTTPClient::getByteRange(
  257. const std::string& url,
  258. S32 offset,
  259. S32 bytes,
  260. ResponderPtr responder,
  261. const LLSD& hdrs,
  262. const F32 timeout)
  263. {
  264. LLSD headers = hdrs;
  265. if(offset > 0 || bytes > 0)
  266. {
  267. std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1);
  268. headers["Range"] = range;
  269. }
  270.     request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
  271. }
  272. void LLHTTPClient::head(
  273. const std::string& url,
  274. ResponderPtr responder,
  275. const LLSD& headers,
  276. const F32 timeout)
  277. {
  278. request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers);
  279. }
  280. void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
  281. {
  282. request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
  283. }
  284. void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
  285. {
  286. request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers);
  287. }
  288. void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout)
  289. {
  290. getHeaderOnly(url, responder, LLSD(), timeout);
  291. }
  292. void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout)
  293. {
  294. LLURI uri;
  295. uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query);
  296. get(uri.asString(), responder, headers, timeout);
  297. }
  298. // A simple class for managing data returned from a curl http request.
  299. class LLHTTPBuffer
  300. {
  301. public:
  302. LLHTTPBuffer() { }
  303. static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
  304. {
  305. LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
  306. size_t bytes = (size * nmemb);
  307. self->mBuffer.append((char*)ptr,bytes);
  308. return nmemb;
  309. }
  310. LLSD asLLSD()
  311. {
  312. LLSD content;
  313. if (mBuffer.empty()) return content;
  314. std::istringstream istr(mBuffer);
  315. LLSDSerialize::fromXML(content, istr);
  316. return content;
  317. }
  318. std::string asString()
  319. {
  320. return mBuffer;
  321. }
  322. private:
  323. std::string mBuffer;
  324. };
  325. // These calls are blocking! This is usually bad, unless you're a dataserver. Then it's awesome.
  326. /**
  327. @brief does a blocking request on the url, returning the data or bad status.
  328. @param url URI to verb on.
  329. @param method the verb to hit the URI with.
  330. @param body the body of the call (if needed - for instance not used for GET and DELETE, but is for POST and PUT)
  331. @param headers HTTP headers to use for the request.
  332. @param timeout Curl timeout to use. Defaults to 5. Rationale:
  333. Without this timeout, blockingGet() calls have been observed to take
  334. up to 90 seconds to complete.  Users of blockingGet() already must 
  335. check the HTTP return code for validity, so this will not introduce
  336. new errors.  A 5 second timeout will succeed > 95% of the time (and 
  337. probably > 99% of the time) based on my statistics. JC
  338. @returns an LLSD map: {status: integer, body: map}
  339.   */
  340. static LLSD blocking_request(
  341. const std::string& url,
  342. LLURLRequest::ERequestAction method,
  343. const LLSD& body,
  344. const LLSD& headers = LLSD(),
  345. const F32 timeout = 5
  346. )
  347. {
  348. lldebugs << "blockingRequest of " << url << llendl;
  349. char curl_error_buffer[CURL_ERROR_SIZE] = "";
  350. CURL* curlp = curl_easy_init();
  351. LLHTTPBuffer http_buffer;
  352. std::string body_str;
  353. // other request method checks root cert first, we skip?
  354. //req->checkRootCertificate(true);
  355. // * Set curl handle options
  356. curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
  357. curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function.
  358. curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
  359. curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
  360. curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
  361. curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
  362. // * Setup headers (don't forget to free them after the call!)
  363. curl_slist* headers_list = NULL;
  364. if (headers.isMap())
  365. {
  366. LLSD::map_const_iterator iter = headers.beginMap();
  367. LLSD::map_const_iterator end  = headers.endMap();
  368. for (; iter != end; ++iter)
  369. {
  370. std::ostringstream header;
  371. header << iter->first << ": " << iter->second.asString() ;
  372. lldebugs << "header = " << header.str() << llendl;
  373. headers_list = curl_slist_append(headers_list, header.str().c_str());
  374. }
  375. }
  376. // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
  377. if (method == LLURLRequest::HTTP_GET)
  378. {
  379. curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1);
  380. }
  381. else if (method == LLURLRequest::HTTP_POST)
  382. {
  383. curl_easy_setopt(curlp, CURLOPT_POST, 1);
  384. //serialize to ostr then copy to str - need to because ostr ptr is unstable :(
  385. std::ostringstream ostr;
  386. LLSDSerialize::toXML(body, ostr);
  387. body_str = ostr.str();
  388. curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str());
  389. //copied from PHP libs, correct?
  390. headers_list = curl_slist_append(headers_list, "Content-Type: application/llsd+xml");
  391. // copied from llurlrequest.cpp
  392. // it appears that apache2.2.3 or django in etch is busted. If
  393. // we do not clear the expect header, we get a 500. May be
  394. // limited to django/mod_wsgi.
  395. headers_list = curl_slist_append(headers_list, "Expect:");
  396. }
  397. // * Do the action using curl, handle results
  398. lldebugs << "HTTP body: " << body_str << llendl;
  399. headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml");
  400. CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list);
  401. if ( curl_result != CURLE_OK )
  402. {
  403. llinfos << "Curl is hosed - can't add headers" << llendl;
  404. }
  405. LLSD response = LLSD::emptyMap();
  406. S32 curl_success = curl_easy_perform(curlp);
  407. S32 http_status = 499;
  408. curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status);
  409. response["status"] = http_status;
  410. // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
  411. if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
  412. {
  413. // We expect 404s, don't spam for them.
  414. llwarns << "CURL REQ URL: " << url << llendl;
  415. llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
  416. llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
  417. llwarns << "CURL REQ BODY: " << body_str << llendl;
  418. llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
  419. llwarns << "CURL ERROR: " << curl_error_buffer << llendl;
  420. llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
  421. response["body"] = http_buffer.asString();
  422. }
  423. else
  424. {
  425. response["body"] = http_buffer.asLLSD();
  426. lldebugs << "CURL response: " << http_buffer.asString() << llendl;
  427. }
  428. if(headers_list)
  429. { // free the header list  
  430. curl_slist_free_all(headers_list); 
  431. }
  432. // * Cleanup
  433. curl_easy_cleanup(curlp);
  434. return response;
  435. }
  436. LLSD LLHTTPClient::blockingGet(const std::string& url)
  437. {
  438. return blocking_request(url, LLURLRequest::HTTP_GET, LLSD());
  439. }
  440. LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body)
  441. {
  442. return blocking_request(url, LLURLRequest::HTTP_POST, body);
  443. }
  444. void LLHTTPClient::put(
  445. const std::string& url,
  446. const LLSD& body,
  447. ResponderPtr responder,
  448. const LLSD& headers,
  449. const F32 timeout)
  450. {
  451. request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder, timeout, headers);
  452. }
  453. void LLHTTPClient::post(
  454. const std::string& url,
  455. const LLSD& body,
  456. ResponderPtr responder,
  457. const LLSD& headers,
  458. const F32 timeout)
  459. {
  460. request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder, timeout, headers);
  461. }
  462. void LLHTTPClient::postRaw(
  463. const std::string& url,
  464. const U8* data,
  465. S32 size,
  466. ResponderPtr responder,
  467. const LLSD& headers,
  468. const F32 timeout)
  469. {
  470. request(url, LLURLRequest::HTTP_POST, new RawInjector(data, size), responder, timeout, headers);
  471. }
  472. void LLHTTPClient::postFile(
  473. const std::string& url,
  474. const std::string& filename,
  475. ResponderPtr responder,
  476. const LLSD& headers,
  477. const F32 timeout)
  478. {
  479. request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder, timeout, headers);
  480. }
  481. void LLHTTPClient::postFile(
  482. const std::string& url,
  483. const LLUUID& uuid,
  484. LLAssetType::EType asset_type,
  485. ResponderPtr responder,
  486. const LLSD& headers,
  487. const F32 timeout)
  488. {
  489. request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder, timeout, headers);
  490. }
  491. // static
  492. void LLHTTPClient::del(
  493. const std::string& url,
  494. ResponderPtr responder,
  495. const LLSD& headers,
  496. const F32 timeout)
  497. {
  498. request(url, LLURLRequest::HTTP_DELETE, NULL, responder, timeout, headers);
  499. }
  500. // static
  501. void LLHTTPClient::move(
  502. const std::string& url,
  503. const std::string& destination,
  504. ResponderPtr responder,
  505. const LLSD& hdrs,
  506. const F32 timeout)
  507. {
  508. LLSD headers = hdrs;
  509. headers["Destination"] = destination;
  510. request(url, LLURLRequest::HTTP_MOVE, NULL, responder, timeout, headers);
  511. }
  512. void LLHTTPClient::setPump(LLPumpIO& pump)
  513. {
  514. theClientPump = &pump;
  515. }
  516. bool LLHTTPClient::hasPump()
  517. {
  518. return theClientPump != NULL;
  519. }