diff options
Diffstat (limited to 'indra/llmessage')
| -rw-r--r-- | indra/llmessage/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | indra/llmessage/tests/llhttpclient_test.cpp | 376 | ||||
| -rw-r--r-- | indra/llmessage/tests/llsdmessage_test.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/tests/test_llsdmessage_peer.py | 19 | 
4 files changed, 398 insertions, 7 deletions
| diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0f40a670fa..d98781e9e6 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -254,6 +254,14 @@ if (LL_TESTS)      "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"      ) +  LL_ADD_INTEGRATION_TEST( +    llhttpclient +    "llhttpclient.cpp" +    "${test_libs}" +    ${PYTHON_EXECUTABLE} +    "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" +    ) +    LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp new file mode 100644 index 0000000000..843c3bcc4b --- /dev/null +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -0,0 +1,376 @@ +/**  + * @file llhttpclient_test.cpp + * @brief Testing the HTTP client classes. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +/** + * + * These classes test the HTTP client framework. + * + */ + +#include <tut/tut.hpp> +#include "linden_common.h" + +#include "lltut.h" +#include "llhttpclient.h" +#include "llformat.h" +#include "llpipeutil.h" +#include "llproxy.h" +#include "llpumpio.h" + +#include "llsdhttpserver.h" +#include "lliohttpserver.h" +#include "lliosocket.h" +#include "stringize.h" + +namespace tut +{ +	LLSD storage; +	 +	class LLSDStorageNode : public LLHTTPNode +	{ +	public: +		LLSD simpleGet() const					{ return storage; } +		LLSD simplePut(const LLSD& value) const	{ storage = value; return LLSD(); } +	}; + +	class ErrorNode : public LLHTTPNode +	{ +	public: +		void get(ResponsePtr r, const LLSD& context) const +			{ r->status(599, "Intentional error"); } +		void post(ResponsePtr r, const LLSD& context, const LLSD& input) const +			{ r->status(input["status"], input["reason"]); } +	}; + +	class TimeOutNode : public LLHTTPNode +	{ +	public: +		void get(ResponsePtr r, const LLSD& context) const +		{ +            /* do nothing, the request will eventually time out */  +		} +	}; + +	LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage"); +	LLHTTPRegistration<ErrorNode>		gErrorNode("/test/error"); +	LLHTTPRegistration<TimeOutNode>		gTimeOutNode("/test/timeout"); + +	struct HTTPClientTestData +	{ +	public: +		HTTPClientTestData(): +			local_server(STRINGIZE("http://127.0.0.1:" << getenv("PORT") << "/")) +		{ +			apr_pool_create(&mPool, NULL); +			LLCurl::initClass(false); +			mServerPump = new LLPumpIO(mPool); +			mClientPump = new LLPumpIO(mPool); + +			LLHTTPClient::setPump(*mClientPump); +		} +		 +		~HTTPClientTestData() +		{ +			delete mServerPump; +			delete mClientPump; +			LLProxy::cleanupClass(); +			apr_pool_destroy(mPool); +		} + +		void setupTheServer() +		{ +			LLHTTPNode& root = LLIOHTTPServer::create(mPool, *mServerPump, 8888); + +			LLHTTPStandardServices::useServices(); +			LLHTTPRegistrar::buildAllServices(root); +		} +		 +		void runThePump(float timeout = 100.0f) +		{ +			LLTimer timer; +			timer.setTimerExpirySec(timeout); + +			while(!mSawCompleted && !mSawCompletedHeader && !timer.hasExpired()) +			{ +				if (mServerPump) +				{ +					mServerPump->pump(); +					mServerPump->callback(); +				} +				if (mClientPump) +				{ +					mClientPump->pump(); +					mClientPump->callback(); +				} +			} +		} + +		void killServer() +		{ +			delete mServerPump; +			mServerPump = NULL; +		} + +		const std::string local_server; + +	private: +		apr_pool_t* mPool; +		LLPumpIO* mServerPump; +		LLPumpIO* mClientPump; + +	protected: +		void ensureStatusOK() +		{ +			if (mSawError) +			{ +				std::string msg = +					llformat("error() called when not expected, status %d", +						mStatus);  +				fail(msg); +			} +		} +	 +		void ensureStatusError() +		{ +			if (!mSawError) +			{ +				fail("error() wasn't called"); +			} +		} +		 +		LLSD getResult() +		{ +			return mResult; +		} +		LLSD getHeader() +		{ +			return mHeader; +		} +	 +	protected: +		bool mSawError; +		U32 mStatus; +		std::string mReason; +		bool mSawCompleted; +		bool mSawCompletedHeader; +		LLSD mResult; +		LLSD mHeader; +		bool mResultDeleted; + +		class Result : public LLHTTPClient::Responder +		{ +		protected: +			Result(HTTPClientTestData& client) +				: mClient(client) +			{ +			} +		 +		public: +			static boost::intrusive_ptr<Result> build(HTTPClientTestData& client) +			{ +				return boost::intrusive_ptr<Result>(new Result(client)); +			} +			 +			~Result() +			{ +				mClient.mResultDeleted = true; +			} +			 +			virtual void error(U32 status, const std::string& reason) +			{ +				mClient.mSawError = true; +				mClient.mStatus = status; +				mClient.mReason = reason; +			} + +			virtual void result(const LLSD& content) +			{ +				mClient.mResult = content; +			} + +			virtual void completed( +							U32 status, const std::string& reason, +							const LLSD& content) +			{ +				LLHTTPClient::Responder::completed(status, reason, content); +				 +				mClient.mSawCompleted = true; +			} + +			virtual void completedHeader( +				U32 status, const std::string& reason, +				const LLSD& content) +			{ +				mClient.mHeader = content; +				mClient.mSawCompletedHeader = true; +			} + +		private: +			HTTPClientTestData& mClient; +		}; + +		friend class Result; + +	protected: +		LLHTTPClient::ResponderPtr newResult() +		{ +			mSawError = false; +			mStatus = 0; +			mSawCompleted = false; +			mSawCompletedHeader = false; +			mResult.clear(); +			mHeader.clear(); +			mResultDeleted = false; +			 +			return Result::build(*this); +		} +	}; +	 +	 +	typedef test_group<HTTPClientTestData>	HTTPClientTestGroup; +	typedef HTTPClientTestGroup::object		HTTPClientTestObject; +	HTTPClientTestGroup httpClientTestGroup("http_client"); + +	template<> template<> +	void HTTPClientTestObject::test<1>() +	{ +		LLHTTPClient::get(local_server, newResult()); +		runThePump(); +		ensureStatusOK(); +		ensure("result object wasn't destroyed", mResultDeleted); +	} + +	template<> template<> +	void HTTPClientTestObject::test<2>() +	{ +		// Please nobody listen on this particular port... +		LLHTTPClient::get("http://127.0.0.1:7950", newResult()); +		runThePump(); +		ensureStatusError(); +	} + +	template<> template<> +		void HTTPClientTestObject::test<3>() +	{ +		LLSD sd; + +		sd["list"][0]["one"] = 1; +		sd["list"][0]["two"] = 2; +		sd["list"][1]["three"] = 3; +		sd["list"][1]["four"] = 4; +		 +		setupTheServer(); + +		LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult()); +		runThePump(); +		ensureStatusOK(); +		ensure_equals("echoed result matches", getResult(), sd); +	} + +	template<> template<> +		void HTTPClientTestObject::test<4>() +	{ +		LLSD sd; + +		sd["message"] = "This is my test message."; + +		setupTheServer(); +		LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult()); +		runThePump(); +		ensureStatusOK(); + +		LLHTTPClient::get("http://localhost:8888/test/storage", newResult()); +		runThePump(); +		ensureStatusOK(); +		ensure_equals("echoed result matches", getResult(), sd); +	 +	} + +	template<> template<> +		void HTTPClientTestObject::test<5>() +	{ +		LLSD sd; +		sd["status"] = 543; +		sd["reason"] = "error for testing"; + +		setupTheServer(); + +		LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult()); +		runThePump(); +		ensureStatusError(); +		ensure_contains("reason", mReason, sd["reason"]); +	} + +	template<> template<> +		void HTTPClientTestObject::test<6>() +	{ +		setupTheServer(); + +		LLHTTPClient::get("http://localhost:8888/test/timeout", newResult()); +		runThePump(1.0f); +		killServer(); +		runThePump(); +		ensureStatusError(); +		ensure_equals("reason", mReason, "STATUS_ERROR"); +	} + +	template<> template<> +		void HTTPClientTestObject::test<7>() +	{ +		// Can not use the little mini server.  The blocking request +		// won't ever let it run.  Instead get from a known LLSD +		// source and compare results with the non-blocking get which +		// is tested against the mini server earlier. +		LLHTTPClient::get(local_server, newResult()); +		runThePump(); +		ensureStatusOK(); +		LLSD expected = getResult(); + +		LLSD result; +		result = LLHTTPClient::blockingGet(local_server); +		LLSD body = result["body"]; +		ensure_equals("echoed result matches", body.size(), expected.size()); +	} +	template<> template<> +		void HTTPClientTestObject::test<8>() +	{ +		// This is testing for the presence of the Header in the returned results +		// from an HTTP::get call. +		LLHTTPClient::get(local_server, newResult()); +		runThePump(); +		ensureStatusOK(); +		LLSD header = getHeader(); +		ensure("got a header", ! header.emptyMap().asBoolean()); +	} +	template<> template<> +	void HTTPClientTestObject::test<9>() +	{ +		LLHTTPClient::head(local_server, newResult()); +		runThePump(); +		ensureStatusOK(); +		ensure("result object wasn't destroyed", mResultDeleted); +	} +} diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp index 6871ac0d52..44b024a83f 100644 --- a/indra/llmessage/tests/llsdmessage_test.cpp +++ b/indra/llmessage/tests/llsdmessage_test.cpp @@ -115,7 +115,7 @@ namespace tut          httpPump.post(request);          ensure("got response", netio.pump());          ensure("success response", success); -        ensure_equals(result.asString(), "success"); +        ensure_equals(result["reply"].asString(), "success");          body["status"] = 499;          body["reason"] = "custom error message"; diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 22edd9dad8..fe4f3a8c01 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -78,25 +78,32 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):  ##         debug("root node tag %s", tree.getroot().tag)  ##         return llsd.to_python(tree.getroot()) -    def do_GET(self): +    def do_HEAD(self): +        self.do_GET(withdata=False) + +    def do_GET(self, withdata=True):          # Of course, don't attempt to read data. -        self.answer(dict(reply="success", status=500, -                         reason="Your GET operation requested failure")) +        data = dict(reply="success", body="avatar", random=17) +        self.answer(data, withdata=withdata)      def do_POST(self):          # Read the provided POST data.          self.answer(self.read_xml()) -    def answer(self, data): +    def answer(self, data, withdata=True):          debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path)          if "fail" not in self.path: -            response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) +            data = data.copy()          # we're going to modify +            # Ensure there's a "reply" key in data, even if there wasn't before +            data["reply"] = data.get("reply", llsd.LLSD("success")) +            response = llsd.format_xml(data)              debug("success: %s", response)              self.send_response(200)              self.send_header("Content-type", "application/llsd+xml")              self.send_header("Content-Length", str(len(response)))              self.end_headers() -            self.wfile.write(response) +            if withdata: +                self.wfile.write(response)          else:                           # fail requested              status = data.get("status", 500)              # self.responses maps an int status to a (short, long) pair of | 
