00001
00032 #include "linden_common.h"
00033
00034 #include "llhttpclient.h"
00035
00036 #include "llassetstorage.h"
00037 #include "lliopipe.h"
00038 #include "llurlrequest.h"
00039 #include "llbufferstream.h"
00040 #include "llsdserialize.h"
00041 #include "llvfile.h"
00042 #include "llvfs.h"
00043 #include "lluri.h"
00044
00045 #include "message.h"
00046 #include <curl/curl.h>
00047
00048 const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
00049 static std::string gCABundle;
00050
00051
00052 LLHTTPClient::Responder::Responder()
00053 : mReferenceCount(0)
00054 {
00055 }
00056
00057 LLHTTPClient::Responder::~Responder()
00058 {
00059 }
00060
00061
00062 void LLHTTPClient::Responder::error(U32 status, const std::string& reason)
00063 {
00064 llinfos << "LLHTTPClient::Responder::error "
00065 << status << ": " << reason << llendl;
00066 }
00067
00068
00069 void LLHTTPClient::Responder::result(const LLSD& content)
00070 {
00071 }
00072
00073
00074 void LLHTTPClient::Responder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels,
00075 const LLIOPipe::buffer_ptr_t& buffer)
00076 {
00077 LLBufferStream istr(channels, buffer.get());
00078 LLSD content;
00079
00080 if (isGoodStatus(status))
00081 {
00082 LLSDSerialize::fromXML(content, istr);
00083
00084
00085
00086
00087
00088
00089
00090
00091 }
00092
00093 completed(status, reason, content);
00094 }
00095
00096
00097 void LLHTTPClient::Responder::completed(U32 status, const std::string& reason, const LLSD& content)
00098 {
00099 if(isGoodStatus(status))
00100 {
00101 result(content);
00102 }
00103 else
00104 {
00105 error(status, reason);
00106 }
00107 }
00108
00109
00110 namespace
00111 {
00112 class LLHTTPClientURLAdaptor : public LLURLRequestComplete
00113 {
00114 public:
00115 LLHTTPClientURLAdaptor(LLHTTPClient::ResponderPtr responder)
00116 : mResponder(responder),
00117 mStatus(499), mReason("LLURLRequest complete w/no status")
00118 {
00119 }
00120
00121 ~LLHTTPClientURLAdaptor()
00122 {
00123 }
00124
00125 virtual void httpStatus(U32 status, const std::string& reason)
00126 {
00127 mStatus = status;
00128 mReason = reason;
00129 }
00130
00131 virtual void complete(const LLChannelDescriptors& channels,
00132 const buffer_ptr_t& buffer)
00133 {
00134 if (mResponder.get())
00135 {
00136 mResponder->completedRaw(mStatus, mReason, channels, buffer);
00137 }
00138 }
00139
00140 private:
00141 LLHTTPClient::ResponderPtr mResponder;
00142 U32 mStatus;
00143 std::string mReason;
00144 };
00145
00146 class Injector : public LLIOPipe
00147 {
00148 public:
00149 virtual const char* contentType() = 0;
00150 };
00151
00152 class LLSDInjector : public Injector
00153 {
00154 public:
00155 LLSDInjector(const LLSD& sd) : mSD(sd) {}
00156 virtual ~LLSDInjector() {}
00157
00158 const char* contentType() { return "application/xml"; }
00159
00160 virtual EStatus process_impl(const LLChannelDescriptors& channels,
00161 buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
00162 {
00163 LLBufferStream ostream(channels, buffer.get());
00164 LLSDSerialize::toXML(mSD, ostream);
00165 eos = true;
00166 return STATUS_DONE;
00167 }
00168
00169 const LLSD mSD;
00170 };
00171
00172 class RawInjector : public Injector
00173 {
00174 public:
00175 RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {}
00176 virtual ~RawInjector() {}
00177
00178 const char* contentType() { return "application/octet-stream"; }
00179
00180 virtual EStatus process_impl(const LLChannelDescriptors& channels,
00181 buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
00182 {
00183 LLBufferStream ostream(channels, buffer.get());
00184 ostream.write((const char *)mData, mSize);
00185 eos = true;
00186 return STATUS_DONE;
00187 }
00188
00189 const U8* mData;
00190 S32 mSize;
00191 };
00192
00193 class FileInjector : public Injector
00194 {
00195 public:
00196 FileInjector(const std::string& filename) : mFilename(filename) {}
00197 virtual ~FileInjector() {}
00198
00199 const char* contentType() { return "application/octet-stream"; }
00200
00201 virtual EStatus process_impl(const LLChannelDescriptors& channels,
00202 buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
00203 {
00204 LLBufferStream ostream(channels, buffer.get());
00205
00206 llifstream fstream(mFilename.c_str(), std::iostream::binary | std::iostream::out);
00207 fstream.seekg(0, std::ios::end);
00208 U32 fileSize = fstream.tellg();
00209 fstream.seekg(0, std::ios::beg);
00210 char* fileBuffer;
00211 fileBuffer = new char [fileSize];
00212 fstream.read(fileBuffer, fileSize);
00213 ostream.write(fileBuffer, fileSize);
00214 fstream.close();
00215 eos = true;
00216 return STATUS_DONE;
00217 }
00218
00219 const std::string mFilename;
00220 };
00221
00222 class VFileInjector : public Injector
00223 {
00224 public:
00225 VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {}
00226 virtual ~VFileInjector() {}
00227
00228 const char* contentType() { return "application/octet-stream"; }
00229
00230 virtual EStatus process_impl(const LLChannelDescriptors& channels,
00231 buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
00232 {
00233 LLBufferStream ostream(channels, buffer.get());
00234
00235 LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ);
00236 S32 fileSize = vfile.getSize();
00237 U8* fileBuffer;
00238 fileBuffer = new U8 [fileSize];
00239 vfile.read(fileBuffer, fileSize);
00240 ostream.write((char*)fileBuffer, fileSize);
00241 eos = true;
00242 return STATUS_DONE;
00243 }
00244
00245 const LLUUID mUUID;
00246 LLAssetType::EType mAssetType;
00247 };
00248
00249
00250 LLPumpIO* theClientPump = NULL;
00251 }
00252
00253 static void request(
00254 const std::string& url,
00255 LLURLRequest::ERequestAction method,
00256 Injector* body_injector,
00257 LLHTTPClient::ResponderPtr responder,
00258 const LLSD& headers,
00259 const F32 timeout=HTTP_REQUEST_EXPIRY_SECS)
00260 {
00261 if (!LLHTTPClient::hasPump())
00262 {
00263 responder->completed(U32_MAX, "No pump", LLSD());
00264 return;
00265 }
00266 LLPumpIO::chain_t chain;
00267
00268 LLURLRequest *req = new LLURLRequest(method, url);
00269 req->requestEncoding("");
00270
00271
00272 if (headers.isMap())
00273 {
00274 LLSD::map_const_iterator iter = headers.beginMap();
00275 LLSD::map_const_iterator end = headers.endMap();
00276
00277 for (; iter != end; ++iter)
00278 {
00279 std::ostringstream header;
00280
00281
00282
00283
00284
00285 if ((iter->first == "Pragma") && (iter->second.asString() == ""))
00286 {
00287 req->useProxy(FALSE);
00288 }
00289 header << iter->first << ": " << iter->second.asString() ;
00290 llinfos << "header = " << header.str() << llendl;
00291 req->addHeader(header.str().c_str());
00292 }
00293 }
00294 if (!gCABundle.empty())
00295 {
00296 req->checkRootCertificate(true, gCABundle.c_str());
00297 }
00298 req->setCallback(new LLHTTPClientURLAdaptor(responder));
00299
00300 if (method == LLURLRequest::HTTP_POST && gMessageSystem)
00301 {
00302 req->addHeader(llformat("X-SecondLife-UDP-Listen-Port: %d",
00303 gMessageSystem->mPort).c_str());
00304 }
00305
00306 if (method == LLURLRequest::HTTP_PUT || method == LLURLRequest::HTTP_POST)
00307 {
00308 req->addHeader(llformat("Content-Type: %s",
00309 body_injector->contentType()).c_str());
00310
00311 chain.push_back(LLIOPipe::ptr_t(body_injector));
00312 }
00313 chain.push_back(LLIOPipe::ptr_t(req));
00314
00315 theClientPump->addChain(chain, timeout);
00316 }
00317
00318 static void request(
00319 const std::string& url,
00320 LLURLRequest::ERequestAction method,
00321 Injector* body_injector,
00322 LLHTTPClient::ResponderPtr responder,
00323 const F32 timeout=HTTP_REQUEST_EXPIRY_SECS)
00324 {
00325 request(url, method, body_injector, responder, LLSD(), timeout);
00326 }
00327
00328 void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
00329 {
00330 request(url, LLURLRequest::HTTP_GET, NULL, responder, headers, timeout);
00331 }
00332
00333 void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const F32 timeout)
00334 {
00335 get(url, responder, LLSD(), timeout);
00336 }
00337
00338 void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout)
00339 {
00340 LLURI uri;
00341
00342 uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query);
00343 get(uri.asString(), responder, headers, timeout);
00344 }
00345
00346 void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const F32 timeout)
00347 {
00348 get(url, query, responder, LLSD(), timeout);
00349 }
00350
00351
00352 class LLHTTPBuffer
00353 {
00354 public:
00355 LLHTTPBuffer() { }
00356
00357 static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
00358 {
00359 LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
00360
00361 size_t bytes = (size * nmemb);
00362 self->mBuffer.append((char*)ptr,bytes);
00363 return nmemb;
00364 }
00365
00366 LLSD asLLSD()
00367 {
00368 LLSD content;
00369
00370 if (mBuffer.empty()) return content;
00371
00372 std::istringstream istr(mBuffer);
00373 LLSDSerialize::fromXML(content, istr);
00374 return content;
00375 }
00376
00377 std::string asString()
00378 {
00379 return mBuffer;
00380 }
00381
00382 private:
00383 std::string mBuffer;
00384 };
00385
00386
00387 LLSD LLHTTPClient::blockingGet(const std::string& url)
00388 {
00389 llinfos << "blockingGet of " << url << llendl;
00390
00391
00392 char curl_error_buffer[CURL_ERROR_SIZE];
00393 CURL* curlp = curl_easy_init();
00394
00395 LLHTTPBuffer http_buffer;
00396
00397
00398
00399
00400
00401
00402 curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1);
00403 curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5);
00404
00405 curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
00406 curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
00407 curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
00408 curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
00409 curl_easy_setopt(curlp, CURLOPT_FAILONERROR, 1);
00410
00411 LLSD response = LLSD::emptyMap();
00412
00413 S32 curl_success = curl_easy_perform(curlp);
00414
00415 S32 http_status = 499;
00416 curl_easy_getinfo(curlp,CURLINFO_RESPONSE_CODE, &http_status);
00417
00418 response["status"] = http_status;
00419
00420 if (curl_success != 0
00421 && http_status != 404)
00422 {
00423 llwarns << "CURL ERROR: " << curl_error_buffer << llendl;
00424
00425 response["body"] = http_buffer.asString();
00426 }
00427 else
00428 {
00429 response["body"] = http_buffer.asLLSD();
00430 }
00431
00432 curl_easy_cleanup(curlp);
00433
00434 return response;
00435 }
00436
00437 void LLHTTPClient::put(const std::string& url, const LLSD& body, ResponderPtr responder, const F32 timeout)
00438 {
00439 request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder, timeout);
00440 }
00441
00442 void LLHTTPClient::post(const std::string& url, const LLSD& body, ResponderPtr responder, const F32 timeout)
00443 {
00444 request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder, timeout);
00445 }
00446
00447 void LLHTTPClient::post(const std::string& url, const U8* data, S32 size, ResponderPtr responder, const F32 timeout)
00448 {
00449 request(url, LLURLRequest::HTTP_POST, new RawInjector(data, size), responder, timeout);
00450 }
00451
00452 void LLHTTPClient::del(const std::string& url, ResponderPtr responder, const F32 timeout)
00453 {
00454 request(url, LLURLRequest::HTTP_DELETE, NULL, responder, timeout);
00455 }
00456
00457 #if 1
00458 void LLHTTPClient::postFile(const std::string& url, const std::string& filename, ResponderPtr responder, const F32 timeout)
00459 {
00460 request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder, timeout);
00461 }
00462
00463 void LLHTTPClient::postFile(const std::string& url, const LLUUID& uuid,
00464 LLAssetType::EType asset_type, ResponderPtr responder, const F32 timeout)
00465 {
00466 request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder, timeout);
00467 }
00468 #endif
00469
00470 void LLHTTPClient::setPump(LLPumpIO& pump)
00471 {
00472 theClientPump = &pump;
00473 }
00474
00475 bool LLHTTPClient::hasPump()
00476 {
00477 return theClientPump != NULL;
00478 }
00479
00480 void LLHTTPClient::setCABundle(const std::string& caBundle)
00481 {
00482 gCABundle = caBundle;
00483 }
00484
00485 namespace boost
00486 {
00487 void intrusive_ptr_add_ref(LLHTTPClient::Responder* p)
00488 {
00489 ++p->mReferenceCount;
00490 }
00491
00492 void intrusive_ptr_release(LLHTTPClient::Responder* p)
00493 {
00494 if(p && 0 == --p->mReferenceCount)
00495 {
00496 delete p;
00497 }
00498 }
00499 };