diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
index 5ecba4420d9f7448030cc3fe0c93fa6980305e75..cf06fe3f40b9525a447bc778e9ec4706e007f5e4 100644
--- a/indra/llcommon/lluri.cpp
+++ b/indra/llcommon/lluri.cpp
@@ -466,6 +466,25 @@ LLURI LLURI::buildAgentSessionURI(const LLUUID& agent_id, LLApp* app)
 	return buildHTTP(host, path);
 }
 
+// static
+LLURI LLURI::buildInventoryHostURI(const LLUUID& agent_id, LLApp* app)
+{
+	std::string host = "localhost:12040";
+
+	if (app)
+	{
+		host = app->getOption("backbone-host-port").asString();
+	}
+
+	LLSD path = LLSD::emptyArray();
+	path.append("agent");
+	path.append(agent_id);
+	path.append("inventory");
+	path.append("host");
+
+	return buildHTTP(host, path);
+}
+
 // static
 LLURI LLURI::buildAgentLoginInfoURI(const LLUUID& agent_id, const std::string& dataserver)
 {
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
index b1ac0bca2b95ddcd12e251e9a4be3f2362dbd3b8..514628237d0ebee064111292a81aa698bc4ace06 100644
--- a/indra/llcommon/lluri.h
+++ b/indra/llcommon/lluri.h
@@ -68,6 +68,8 @@ class LLURI
 	static LLURI buildBulkAgentPresenceURI(LLApp* app);
 	static LLURI buildAgentSessionURI(const LLUUID& agent_id, LLApp* app);
 	static LLURI buildAgentLoginInfoURI(const LLUUID& agent_id, const std::string& dataserver);
+	static LLURI buildInventoryHostURI(const LLUUID& agent_id, LLApp* app);
+	
 private:
 	std::string mScheme;
 	std::string mEscapedOpaque;
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
index 7d51b6c722492151becf05b790af47f8ac636c77..d83308d082d63ddc70a48a13eaae633a55b8a507 100644
--- a/indra/llmessage/llhttpclient.cpp
+++ b/indra/llmessage/llhttpclient.cpp
@@ -18,6 +18,8 @@
 #include "llvfile.h"
 #include "llvfs.h"
 
+#include <curl/curl.h>
+
 static const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
 
 static std::string gCABundle;
@@ -224,6 +226,71 @@ void LLHTTPClient::get(const std::string& url, ResponderPtr responder)
 	request(url, LLURLRequest::HTTP_GET, NULL, responder);
 }
 
+// A simple class for managing data returned from a curl http request.
+class LLHTTPBuffer
+{
+public:
+	LLHTTPBuffer() { }
+
+	static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
+	{
+		LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
+		
+		size_t bytes = (size * nmemb);
+		self->mBuffer.append((char*)ptr,bytes);
+		return nmemb;
+	}
+
+	LLSD asLLSD()
+	{
+		LLSD content;
+		std::istringstream istr(mBuffer);
+		LLSDSerialize::fromXML(content, istr);
+		return content;
+	}
+
+private:
+	std::string mBuffer;
+};
+
+// This call is blocking! This is probably usually bad. :(
+LLSD LLHTTPClient::blockingGet(const std::string& url)
+{
+	llinfos << "blockingGet of " << url << llendl;
+
+	// Returns an LLSD map: {status: integer, body: map}
+	char curl_error_buffer[CURL_ERROR_SIZE];
+	CURL* curlp = curl_easy_init();
+
+	LLHTTPBuffer http_buffer;
+
+	curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
+	curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
+	curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
+	curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
+	curl_easy_setopt(curlp, CURLOPT_FAILONERROR, 1);
+
+	LLSD response = LLSD::emptyMap();
+
+	S32 curl_success = curl_easy_perform(curlp);
+
+	S32 http_status = 499;
+	curl_easy_getinfo(curlp,CURLINFO_RESPONSE_CODE, &http_status);
+
+	if (curl_success != 0 
+		&& http_status != 404)  // We expect 404s, don't spam for them.
+	{
+		llwarns << "CURL ERROR: " << curl_error_buffer << llendl;
+	}
+	
+	response["status"] = http_status;
+	response["body"] = http_buffer.asLLSD();
+
+	curl_easy_cleanup(curlp);
+
+	return response;
+}
+
 void LLHTTPClient::put(const std::string& url, const LLSD& body, ResponderPtr responder)
 {
 	request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder);
diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h
index 41ccb1fad9c523aa4303d5a3b4aef1da0b563bbc..d64662e41dc4262ed01b2bf070760b3c9cf8c49c 100644
--- a/indra/llmessage/llhttpclient.h
+++ b/indra/llmessage/llhttpclient.h
@@ -58,6 +58,9 @@ class LLHTTPClient
 	static void postFile(const std::string& url, const LLUUID& uuid,
 		LLAssetType::EType asset_type, ResponderPtr responder);
 
+	// Blocking HTTP get that returns an LLSD map of status and body.
+	static LLSD blockingGet(const std::string& url);
+
 	static void del(const std::string& url, ResponderPtr);
 		///< sends a DELETE method, but we can't call it delete in c++
 	
diff --git a/indra/test/llhttpclient_tut.cpp b/indra/test/llhttpclient_tut.cpp
index 43ef6f443860fa71efc8cad4671f0419e77e7c98..865af98761b8bfebef2ebf3dcdde9fca46364171 100644
--- a/indra/test/llhttpclient_tut.cpp
+++ b/indra/test/llhttpclient_tut.cpp
@@ -294,4 +294,23 @@ namespace tut
 		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.
+		LLSD expected;
+
+		LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult());
+		runThePump();
+		ensureStatusOK();
+		expected = getResult();
+
+		LLSD result;
+		result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php");
+		LLSD body = result["body"];
+		ensure_equals("echoed result matches", body.size(), expected.size());
+	}
 }