diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index e09b511a6e475ace147d16ea9e3e6343a0e42cf9..24c671437eba8e6cfd14889dd1c979e40ac7274d 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -160,7 +160,7 @@ void LLPluginClassMedia::idle(void)
 		mPlugin->idle();
 	}
 	
-	if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL))
+	if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()))
 	{
 		// Can't process a size change at this time
 	}
@@ -437,6 +437,11 @@ void LLPluginClassMedia::mouseEvent(EMouseEventType type, int button, int x, int
 {
 	if(type == MOUSE_EVENT_MOVE)
 	{
+		if(!mPlugin || !mPlugin->isRunning() || mPlugin->isBlocked())
+		{
+			// Don't queue up mouse move events that can't be delivered.
+		}
+
 		if((x == mLastMouseX) && (y == mLastMouseY))
 		{
 			// Don't spam unnecessary mouse move events.
diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp
index 1d7ddc55924c02f1728a8694a2db6cdbf9b8ba19..e524c88cf832d2db02a114f35e65c51509e90ba1 100644
--- a/indra/llplugin/llpluginmessagepipe.cpp
+++ b/indra/llplugin/llpluginmessagepipe.cpp
@@ -299,26 +299,23 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 void LLPluginMessagePipe::processInput(void)
 {
 	// Look for input delimiter(s) in the input buffer.
-	int start = 0;
 	int delim;
-	while((delim = mInput.find(MESSAGE_DELIMITER, start)) != std::string::npos)
+	while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
 	{	
 		// Let the owner process this message
 		if (mOwner)
 		{
-			mOwner->receiveMessageRaw(mInput.substr(start, delim - start));
+			// Pull the message out of the input buffer before calling receiveMessageRaw.
+			// It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request)
+			// and this guarantees that the messages will get dequeued correctly.
+			std::string message(mInput, 0, delim);
+			mInput.erase(0, delim + 1);
+			mOwner->receiveMessageRaw(message);
 		}
 		else
 		{
 			LL_WARNS("Plugin") << "!mOwner" << LL_ENDL;
 		}
-		
-		start = delim + 1;
 	}
-	
-	// Remove delivered messages from the input buffer.
-	if(start != 0)
-		mInput = mInput.substr(start);
-	
 }
 
diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp
index ccaf95b36ded8c072865cb1a954041650126dce1..2d078cd6ed71d4c150c4d4b5a6eda4f0d7708291 100644
--- a/indra/llplugin/llpluginprocesschild.cpp
+++ b/indra/llplugin/llpluginprocesschild.cpp
@@ -48,6 +48,8 @@ LLPluginProcessChild::LLPluginProcessChild()
 	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
 	mSleepTime = PLUGIN_IDLE_SECONDS;	// default: send idle messages at 100Hz
 	mCPUElapsed = 0.0f;
+	mBlockingRequest = false;
+	mBlockingResponseReceived = false;
 }
 
 LLPluginProcessChild::~LLPluginProcessChild()
@@ -226,6 +228,7 @@ void LLPluginProcessChild::idle(void)
 
 void LLPluginProcessChild::sleep(F64 seconds)
 {
+	deliverQueuedMessages();
 	if(mMessagePipe)
 	{
 		mMessagePipe->pump(seconds);
@@ -238,6 +241,7 @@ void LLPluginProcessChild::sleep(F64 seconds)
 
 void LLPluginProcessChild::pump(void)
 {
+	deliverQueuedMessages();
 	if(mMessagePipe)
 	{
 		mMessagePipe->pump(0.0f);
@@ -309,15 +313,32 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
 
 	LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
 
+	// Decode this message
+	LLPluginMessage parsed;
+	parsed.parse(message);
+
+	if(mBlockingRequest)
+	{
+		// We're blocking the plugin waiting for a response.
+
+		if(parsed.hasValue("blocking_response"))
+		{
+			// This is the message we've been waiting for -- fall through and send it immediately. 
+			mBlockingResponseReceived = true;
+		}
+		else
+		{
+			// Still waiting.  Queue this message and don't process it yet.
+			mMessageQueue.push(message);
+			return;
+		}
+	}
+	
 	bool passMessage = true;
 	
 	// FIXME: how should we handle queueing here?
 	
 	{
-		// Decode this message
-		LLPluginMessage parsed;
-		parsed.parse(message);
-		
 		std::string message_class = parsed.getClass();
 		if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
 		{
@@ -425,7 +446,13 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
 void LLPluginProcessChild::receivePluginMessage(const std::string &message)
 {
 	LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
-
+	
+	if(mBlockingRequest)
+	{
+		// 
+		LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
+	}
+	
 	// Incoming message from the plugin instance
 	bool passMessage = true;
 
@@ -436,6 +463,12 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
 		// Decode this message
 		LLPluginMessage parsed;
 		parsed.parse(message);
+		
+		if(parsed.hasValue("blocking_request"))
+		{
+			mBlockingRequest = true;
+		}
+
 		std::string message_class = parsed.getClass();
 		if(message_class == "base")
 		{
@@ -494,6 +527,19 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
 		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
 		writeMessageRaw(message);
 	}
+	
+	while(mBlockingRequest)
+	{
+		// The plugin wants to block and wait for a response to this message.
+		sleep(mSleepTime);	// this will pump the message pipe and process messages
+
+		if(mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
+		{
+			// Response has been received, or we've hit an error state.  Stop waiting.
+			mBlockingRequest = false;
+			mBlockingResponseReceived = false;
+		}
+	}
 }
 
 
@@ -502,3 +548,15 @@ void LLPluginProcessChild::setState(EState state)
 	LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
 	mState = state; 
 };
+
+void LLPluginProcessChild::deliverQueuedMessages()
+{
+	if(!mBlockingRequest)
+	{
+		while(!mMessageQueue.empty())
+		{
+			receiveMessageRaw(mMessageQueue.front());
+			mMessageQueue.pop();
+		}
+	}
+}
diff --git a/indra/llplugin/llpluginprocesschild.h b/indra/llplugin/llpluginprocesschild.h
index 0e5e85406a671fa00f0b33ac1357e5ca17695e8a..1430ad7a5db5cf6f2e486562a9906d55f6e0977d 100644
--- a/indra/llplugin/llpluginprocesschild.h
+++ b/indra/llplugin/llpluginprocesschild.h
@@ -106,6 +106,11 @@ class LLPluginProcessChild: public LLPluginMessagePipeOwner, public LLPluginInst
 	LLTimer mHeartbeat;
 	F64		mSleepTime;
 	F64		mCPUElapsed;
+	bool	mBlockingRequest;
+	bool	mBlockingResponseReceived;
+	std::queue<std::string> mMessageQueue;
+	
+	void deliverQueuedMessages();
 	
 };
 
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 895c858979026645dfc5632e6707ae2cc7fd773e..e273410a1d5f60ae3c01eaf2d4338dbaa9e56147 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -54,6 +54,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
 	mCPUUsage = 0.0;
 	mDisableTimeout = false;
 	mDebug = false;
+	mBlocked = false;
 
 	mPluginLaunchTimeout = 60.0f;
 	mPluginLockupTimeout = 15.0f;
@@ -479,6 +480,13 @@ void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send)
 
 void LLPluginProcessParent::sendMessage(const LLPluginMessage &message)
 {
+	if(message.hasValue("blocking_response"))
+	{
+		mBlocked = false;
+
+		// reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked.
+		mHeartbeat.setTimerExpirySec(mPluginLockupTimeout);
+	}
 	
 	std::string buffer = message.generate();
 	LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL;	
@@ -501,6 +509,11 @@ void LLPluginProcessParent::receiveMessageRaw(const std::string &message)
 
 void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)
 {
+	if(message.hasValue("blocking_request"))
+	{
+		mBlocked = true;
+	}
+
 	std::string message_class = message.getClass();
 	if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
 	{
@@ -689,18 +702,15 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
 {
 	bool result = false;
 	
-	if(!mDisableTimeout && !mDebug)
+	if(!mProcess.isRunning())
 	{
-		if(!mProcess.isRunning())
-		{
-			LL_WARNS("Plugin") << "child exited" << llendl;
-			result = true;
-		}
-		else if(pluginLockedUp())
-		{
-			LL_WARNS("Plugin") << "timeout" << llendl;
-			result = true;
-		}
+		LL_WARNS("Plugin") << "child exited" << llendl;
+		result = true;
+	}
+	else if(pluginLockedUp())
+	{
+		LL_WARNS("Plugin") << "timeout" << llendl;
+		result = true;
 	}
 	
 	return result;
@@ -708,6 +718,12 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
 
 bool LLPluginProcessParent::pluginLockedUp()
 {
+	if(mDisableTimeout || mDebug || mBlocked)
+	{
+		// Never time out a plugin process in these cases.
+		return false;
+	}
+	
 	// If the timer is running and has expired, the plugin has locked up.
 	return (mHeartbeat.getStarted() && mHeartbeat.hasExpired());
 }
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index cc6c513615747283dfd71c9e504841168697ffb3..31f125bfb313eff2e9a09c2522611c2bee879e21 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -74,6 +74,9 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	// returns true if the process has exited or we've had a fatal error
 	bool isDone(void);	
 	
+	// returns true if the process is currently waiting on a blocking request
+	bool isBlocked(void) { return mBlocked; };
+	
 	void killSockets(void);
 	
 	// Go to the proper error state
@@ -160,6 +163,7 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	
 	bool mDisableTimeout;
 	bool mDebug;
+	bool mBlocked;
 
 	LLProcessLauncher mDebugger;