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 && !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
00171 protected:
00172 bool mSawError;
00173 U32 mStatus;
00174 std::string mReason;
00175 bool mSawCompleted;
00176 LLSD mResult;
00177 bool mResultDeleted;
00178
00179 class Result : public LLHTTPClient::Responder
00180 {
00181 protected:
00182 Result(HTTPClientTestData& client)
00183 : mClient(client)
00184 {
00185 }
00186
00187 public:
00188 static boost::intrusive_ptr<Result> build(HTTPClientTestData& client)
00189 {
00190 return boost::intrusive_ptr<Result>(new Result(client));
00191 }
00192
00193 ~Result()
00194 {
00195 mClient.mResultDeleted = true;
00196 }
00197
00198 virtual void error(U32 status, const std::string& reason)
00199 {
00200 mClient.mSawError = true;
00201 mClient.mStatus = status;
00202 mClient.mReason = reason;
00203 }
00204
00205 virtual void result(const LLSD& content)
00206 {
00207 mClient.mResult = content;
00208 }
00209
00210 virtual void completed(
00211 U32 status, const std::string& reason,
00212 const LLSD& content)
00213 {
00214 LLHTTPClient::Responder::completed(status, reason, content);
00215
00216 mClient.mSawCompleted = true;
00217 }
00218
00219 private:
00220 HTTPClientTestData& mClient;
00221 };
00222
00223 friend class Result;
00224
00225 protected:
00226 LLHTTPClient::ResponderPtr newResult()
00227 {
00228 mSawError = false;
00229 mStatus = 0;
00230 mSawCompleted = false;
00231 mResult.clear();
00232 mResultDeleted = false;
00233
00234 return Result::build(*this);
00235 }
00236 };
00237
00238
00239 typedef test_group<HTTPClientTestData> HTTPClientTestGroup;
00240 typedef HTTPClientTestGroup::object HTTPClientTestObject;
00241 HTTPClientTestGroup httpClientTestGroup("http_client");
00242
00243 template<> template<>
00244 void HTTPClientTestObject::test<1>()
00245 {
00246 LLHTTPClient::get("http://www.secondlife.com/", newResult());
00247 runThePump();
00248 ensureStatusOK();
00249 ensure("result object wasn't destroyed", mResultDeleted);
00250 }
00251
00252 template<> template<>
00253 void HTTPClientTestObject::test<2>()
00254 {
00255 LLHTTPClient::get("http://www.invalid", newResult());
00256 runThePump();
00257 ensureStatusError();
00258 }
00259
00260 template<> template<>
00261 void HTTPClientTestObject::test<3>()
00262 {
00263 LLSD sd;
00264
00265 sd["list"][0]["one"] = 1;
00266 sd["list"][0]["two"] = 2;
00267 sd["list"][1]["three"] = 3;
00268 sd["list"][1]["four"] = 4;
00269
00270 setupTheServer();
00271
00272 LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult());
00273 runThePump();
00274 ensureStatusOK();
00275 ensure_equals("echoed result matches", getResult(), sd);
00276 }
00277
00278 template<> template<>
00279 void HTTPClientTestObject::test<4>()
00280 {
00281 LLSD sd;
00282
00283 sd["message"] = "This is my test message.";
00284
00285 setupTheServer();
00286 LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult());
00287 runThePump();
00288 ensureStatusOK();
00289
00290 LLHTTPClient::get("http://localhost:8888/test/storage", newResult());
00291 runThePump();
00292 ensureStatusOK();
00293 ensure_equals("echoed result matches", getResult(), sd);
00294
00295 }
00296
00297 template<> template<>
00298 void HTTPClientTestObject::test<5>()
00299 {
00300 LLSD sd;
00301 sd["status"] = 543;
00302 sd["reason"] = "error for testing";
00303
00304 setupTheServer();
00305
00306 LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult());
00307 runThePump();
00308 ensureStatusError();
00309 ensure_contains("reason", mReason, sd["reason"]);
00310 }
00311
00312 template<> template<>
00313 void HTTPClientTestObject::test<6>()
00314 {
00315 setupTheServer();
00316
00317 LLHTTPClient::get("http://localhost:8888/test/timeout", newResult());
00318 runThePump(1.0f);
00319 killServer();
00320 runThePump();
00321 ensureStatusError();
00322 ensure_equals("reason", mReason, "STATUS_ERROR");
00323 }
00324
00325 template<> template<>
00326 void HTTPClientTestObject::test<7>()
00327 {
00328
00329
00330
00331 LLSD expected;
00332
00333 LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult());
00334 runThePump();
00335 ensureStatusOK();
00336 expected = getResult();
00337
00338 LLSD result;
00339 result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php");
00340 LLSD body = result["body"];
00341 ensure_equals("echoed result matches", body.size(), expected.size());
00342 }
00343
00344 }
00345
00346 #endif // !LL_WINDOWS