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