00001
00038 #include <tut/tut.h>
00039 #include "linden_common.h"
00040
00041
00042 #if !LL_WINDOWS
00043
00044 #include "lltut.h"
00045 #include "llhttpclient.h"
00046 #include "llformat.h"
00047 #include "llpipeutil.h"
00048 #include "llpumpio.h"
00049
00050 #include "llsdhttpserver.h"
00051 #include "lliohttpserver.h"
00052 #include "lliosocket.h"
00053
00054 namespace tut
00055 {
00056 LLSD storage;
00057
00058 class LLSDStorageNode : public LLHTTPNode
00059 {
00060 public:
00061 LLSD get() const { return storage; }
00062 LLSD put(const LLSD& value) const { storage = value; return LLSD(); }
00063 };
00064
00065 class ErrorNode : public LLHTTPNode
00066 {
00067 public:
00068 void get(ResponsePtr r, const LLSD& context) const
00069 { r->status(599, "Intentional error"); }
00070 void post(ResponsePtr r, const LLSD& context, const LLSD& input) const
00071 { r->status(input["status"], input["reason"]); }
00072 };
00073
00074 class TimeOutNode : public LLHTTPNode
00075 {
00076 public:
00077 void get(ResponsePtr r, const LLSD& context) const
00078 {
00079
00080 }
00081 };
00082
00083 LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage");
00084 LLHTTPRegistration<ErrorNode> gErrorNode("/test/error");
00085 LLHTTPRegistration<TimeOutNode> gTimeOutNode("/test/timeout");
00086
00087 struct HTTPClientTestData
00088 {
00089 public:
00090 HTTPClientTestData()
00091 {
00092 apr_pool_create(&mPool, NULL);
00093 mServerPump = new LLPumpIO(mPool);
00094 mClientPump = new LLPumpIO(mPool);
00095
00096 LLHTTPClient::setPump(*mClientPump);
00097 }
00098
00099 ~HTTPClientTestData()
00100 {
00101 delete mServerPump;
00102 delete mClientPump;
00103 apr_pool_destroy(mPool);
00104 }
00105
00106 void setupTheServer()
00107 {
00108 LLHTTPNode& root = LLIOHTTPServer::create(mPool, *mServerPump, 8888);
00109
00110 LLHTTPStandardServices::useServices();
00111 LLHTTPRegistrar::buildAllServices(root);
00112 }
00113
00114 void runThePump(float timeout = 100.0f)
00115 {
00116 LLTimer timer;
00117 timer.setTimerExpirySec(timeout);
00118
00119 while(!mSawCompleted && !mSawCompletedHeader && !timer.hasExpired())
00120 {
00121 if (mServerPump)
00122 {
00123 mServerPump->pump();
00124 mServerPump->callback();
00125 }
00126 if (mClientPump)
00127 {
00128 mClientPump->pump();
00129 mClientPump->callback();
00130 }
00131 }
00132 }
00133
00134 void killServer()
00135 {
00136 delete mServerPump;
00137 mServerPump = NULL;
00138 }
00139
00140 private:
00141 apr_pool_t* mPool;
00142 LLPumpIO* mServerPump;
00143 LLPumpIO* mClientPump;
00144
00145
00146 protected:
00147 void ensureStatusOK()
00148 {
00149 if (mSawError)
00150 {
00151 std::string msg =
00152 llformat("error() called when not expected, status %d",
00153 mStatus);
00154 fail(msg);
00155 }
00156 }
00157
00158 void ensureStatusError()
00159 {
00160 if (!mSawError)
00161 {
00162 fail("error() wasn't called");
00163 }
00164 }
00165
00166 LLSD getResult()
00167 {
00168 return mResult;
00169 }
00170 LLSD getHeader()
00171 {
00172 return mHeader;
00173 }
00174
00175 protected:
00176 bool mSawError;
00177 U32 mStatus;
00178 std::string mReason;
00179 bool mSawCompleted;
00180 bool mSawCompletedHeader;
00181 LLSD mResult;
00182 LLSD mHeader;
00183 bool mResultDeleted;
00184
00185 class Result : public LLHTTPClient::Responder
00186 {
00187 protected:
00188 Result(HTTPClientTestData& client)
00189 : mClient(client)
00190 {
00191 }
00192
00193 public:
00194 static boost::intrusive_ptr<Result> build(HTTPClientTestData& client)
00195 {
00196 return boost::intrusive_ptr<Result>(new Result(client));
00197 }
00198
00199 ~Result()
00200 {
00201 mClient.mResultDeleted = true;
00202 }
00203
00204 virtual void error(U32 status, const std::string& reason)
00205 {
00206 mClient.mSawError = true;
00207 mClient.mStatus = status;
00208 mClient.mReason = reason;
00209 }
00210
00211 virtual void result(const LLSD& content)
00212 {
00213 mClient.mResult = content;
00214 }
00215
00216 virtual void completed(
00217 U32 status, const std::string& reason,
00218 const LLSD& content)
00219 {
00220 LLHTTPClient::Responder::completed(status, reason, content);
00221
00222 mClient.mSawCompleted = true;
00223 }
00224
00225 virtual void completedHeader(
00226 U32 status, const std::string& reason,
00227 const LLSD& content)
00228 {
00229 mClient.mHeader = content;
00230 mClient.mSawCompletedHeader = true;
00231 }
00232
00233 private:
00234 HTTPClientTestData& mClient;
00235 };
00236
00237 friend class Result;
00238
00239 protected:
00240 LLHTTPClient::ResponderPtr newResult()
00241 {
00242 mSawError = false;
00243 mStatus = 0;
00244 mSawCompleted = false;
00245 mSawCompletedHeader = false;
00246 mResult.clear();
00247 mHeader.clear();
00248 mResultDeleted = false;
00249
00250 return Result::build(*this);
00251 }
00252 };
00253
00254
00255 typedef test_group<HTTPClientTestData> HTTPClientTestGroup;
00256 typedef HTTPClientTestGroup::object HTTPClientTestObject;
00257 HTTPClientTestGroup httpClientTestGroup("http_client");
00258
00259 template<> template<>
00260 void HTTPClientTestObject::test<1>()
00261 {
00262 LLHTTPClient::get("http://www.secondlife.com/", newResult());
00263 runThePump();
00264 ensureStatusOK();
00265 ensure("result object wasn't destroyed", mResultDeleted);
00266 }
00267
00268 template<> template<>
00269 void HTTPClientTestObject::test<2>()
00270 {
00271 LLHTTPClient::get("http://www.invalid", newResult());
00272 runThePump();
00273 ensureStatusError();
00274 }
00275
00276 template<> template<>
00277 void HTTPClientTestObject::test<3>()
00278 {
00279 LLSD sd;
00280
00281 sd["list"][0]["one"] = 1;
00282 sd["list"][0]["two"] = 2;
00283 sd["list"][1]["three"] = 3;
00284 sd["list"][1]["four"] = 4;
00285
00286 setupTheServer();
00287
00288 LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult());
00289 runThePump();
00290 ensureStatusOK();
00291 ensure_equals("echoed result matches", getResult(), sd);
00292 }
00293
00294 template<> template<>
00295 void HTTPClientTestObject::test<4>()
00296 {
00297 LLSD sd;
00298
00299 sd["message"] = "This is my test message.";
00300
00301 setupTheServer();
00302 LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult());
00303 runThePump();
00304 ensureStatusOK();
00305
00306 LLHTTPClient::get("http://localhost:8888/test/storage", newResult());
00307 runThePump();
00308 ensureStatusOK();
00309 ensure_equals("echoed result matches", getResult(), sd);
00310
00311 }
00312
00313 template<> template<>
00314 void HTTPClientTestObject::test<5>()
00315 {
00316 LLSD sd;
00317 sd["status"] = 543;
00318 sd["reason"] = "error for testing";
00319
00320 setupTheServer();
00321
00322 LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult());
00323 runThePump();
00324 ensureStatusError();
00325 ensure_contains("reason", mReason, sd["reason"]);
00326 }
00327
00328 template<> template<>
00329 void HTTPClientTestObject::test<6>()
00330 {
00331 setupTheServer();
00332
00333 LLHTTPClient::get("http://localhost:8888/test/timeout", newResult());
00334 runThePump(1.0f);
00335 killServer();
00336 runThePump();
00337 ensureStatusError();
00338 ensure_equals("reason", mReason, "STATUS_ERROR");
00339 }
00340
00341 template<> template<>
00342 void HTTPClientTestObject::test<7>()
00343 {
00344
00345
00346
00347
00348 skip_fail("secondlife.com is not reliable enough for unit tests.");
00349
00350
00351 LLSD expected;
00352
00353 LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult());
00354 runThePump();
00355 ensureStatusOK();
00356 expected = getResult();
00357
00358 LLSD result;
00359 result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php");
00360 LLSD body = result["body"];
00361 ensure_equals("echoed result matches", body.size(), expected.size());
00362 }
00363 template<> template<>
00364 void HTTPClientTestObject::test<8>()
00365 {
00366
00367
00368 LLHTTPClient::get("http://www.secondlife.com/", newResult());
00369 runThePump();
00370 ensureStatusOK();
00371 LLSD header = getHeader();
00372 ensure_equals("got a header", header.emptyMap().asBoolean(), FALSE);
00373 }
00374 template<> template<>
00375 void HTTPClientTestObject::test<9>()
00376 {
00377 LLHTTPClient::head("http://www.secondlife.com/", newResult());
00378 runThePump();
00379 ensureStatusOK();
00380 ensure("result object wasn't destroyed", mResultDeleted);
00381 }
00382 }
00383
00384 #endif // !LL_WINDOWS