PageRenderTime 36ms CodeModel.GetById 14ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/lleventpoll.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 289 lines | 194 code | 46 blank | 49 comment | 16 complexity | 226600469ab1ee8353e244cfd92f4ba9 MD5 | raw file
  1/**
  2 * @file lleventpoll.cpp
  3 * @brief Implementation of the LLEventPoll class.
  4 *
  5 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "lleventpoll.h"
 30#include "llappviewer.h"
 31#include "llagent.h"
 32
 33#include "llhttpclient.h"
 34#include "llhttpstatuscodes.h"
 35#include "llsdserialize.h"
 36#include "lleventtimer.h"
 37#include "llviewerregion.h"
 38#include "message.h"
 39#include "lltrans.h"
 40
 41namespace
 42{
 43	// We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
 44	// This means we attempt to recover relatively quickly but back off giving more time to recover
 45	// until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
 46	const F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
 47	const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
 48	const S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
 49
 50	class LLEventPollResponder : public LLHTTPClient::Responder
 51	{
 52	public:
 53		
 54		static LLHTTPClient::ResponderPtr start(const std::string& pollURL, const LLHost& sender);
 55		void stop();
 56		
 57		void makeRequest();
 58
 59	private:
 60		LLEventPollResponder(const std::string&	pollURL, const LLHost& sender);
 61		~LLEventPollResponder();
 62
 63		
 64		void handleMessage(const LLSD& content);
 65		virtual	void error(U32 status, const std::string& reason);
 66		virtual	void result(const LLSD&	content);
 67
 68		virtual void completedRaw(U32 status,
 69									const std::string& reason,
 70									const LLChannelDescriptors& channels,
 71									const LLIOPipe::buffer_ptr_t& buffer);
 72	private:
 73
 74		bool	mDone;
 75
 76		std::string			mPollURL;
 77		std::string			mSender;
 78		
 79		LLSD	mAcknowledge;
 80		
 81		// these are only here for debugging so	we can see which poller	is which
 82		static int sCount;
 83		int	mCount;
 84		S32 mErrorCount;
 85	};
 86
 87	class LLEventPollEventTimer : public LLEventTimer
 88	{
 89		typedef boost::intrusive_ptr<LLEventPollResponder> EventPollResponderPtr;
 90
 91	public:
 92		LLEventPollEventTimer(F32 period, EventPollResponderPtr responder)
 93			: LLEventTimer(period), mResponder(responder)
 94		{ }
 95
 96		virtual BOOL tick()
 97		{
 98			mResponder->makeRequest();
 99			return TRUE;	// Causes this instance to be deleted.
100		}
101
102	private:
103		
104		EventPollResponderPtr mResponder;
105	};
106
107	//static
108	LLHTTPClient::ResponderPtr LLEventPollResponder::start(
109		const std::string& pollURL, const LLHost& sender)
110	{
111		LLHTTPClient::ResponderPtr result = new LLEventPollResponder(pollURL, sender);
112		llinfos	<< "LLEventPollResponder::start <" << sCount << "> "
113				<< pollURL << llendl;
114		return result;
115	}
116
117	void LLEventPollResponder::stop()
118	{
119		llinfos	<< "LLEventPollResponder::stop	<" << mCount <<	"> "
120				<< mPollURL	<< llendl;
121		// there should	be a way to	stop a LLHTTPClient	request	in progress
122		mDone =	true;
123	}
124
125	int	LLEventPollResponder::sCount =	0;
126
127	LLEventPollResponder::LLEventPollResponder(const std::string& pollURL, const LLHost& sender)
128		: mDone(false),
129		  mPollURL(pollURL),
130		  mCount(++sCount),
131		  mErrorCount(0)
132	{
133		//extract host and port of simulator to set as sender
134		LLViewerRegion *regionp = gAgent.getRegion();
135		if (!regionp)
136		{
137			llerrs << "LLEventPoll initialized before region is added." << llendl;
138		}
139		mSender = sender.getIPandPort();
140		llinfos << "LLEventPoll initialized with sender " << mSender << llendl;
141		makeRequest();
142	}
143
144	LLEventPollResponder::~LLEventPollResponder()
145	{
146		stop();
147		lldebugs <<	"LLEventPollResponder::~Impl <" <<	mCount << "> "
148				 <<	mPollURL <<	llendl;
149	}
150
151	// virtual 
152	void LLEventPollResponder::completedRaw(U32 status,
153									const std::string& reason,
154									const LLChannelDescriptors& channels,
155									const LLIOPipe::buffer_ptr_t& buffer)
156	{
157		if (status == HTTP_BAD_GATEWAY)
158		{
159			// These errors are not parsable as LLSD, 
160			// which LLHTTPClient::Responder::completedRaw will try to do.
161			completed(status, reason, LLSD());
162		}
163		else
164		{
165			LLHTTPClient::Responder::completedRaw(status,reason,channels,buffer);
166		}
167	}
168
169	void LLEventPollResponder::makeRequest()
170	{
171		LLSD request;
172		request["ack"] = mAcknowledge;
173		request["done"]	= mDone;
174		
175		lldebugs <<	"LLEventPollResponder::makeRequest	<" << mCount <<	"> ack = "
176				 <<	LLSDXMLStreamer(mAcknowledge) << llendl;
177		LLHTTPClient::post(mPollURL, request, this);
178	}
179
180	void LLEventPollResponder::handleMessage(const	LLSD& content)
181	{
182		std::string	msg_name	= content["message"];
183		LLSD message;
184		message["sender"] = mSender;
185		message["body"] = content["body"];
186		LLMessageSystem::dispatch(msg_name, message);
187	}
188
189	//virtual
190	void LLEventPollResponder::error(U32 status, const	std::string& reason)
191	{
192		if (mDone) return;
193
194		// A HTTP_BAD_GATEWAY (502) error is our standard timeout response
195		// we get this when there are no events.
196		if ( status == HTTP_BAD_GATEWAY )	
197		{
198			mErrorCount = 0;
199			makeRequest();
200		}
201		else if (mErrorCount < MAX_EVENT_POLL_HTTP_ERRORS)
202		{
203			++mErrorCount;
204			
205			// The 'tick' will return TRUE causing the timer to delete this.
206			new LLEventPollEventTimer(EVENT_POLL_ERROR_RETRY_SECONDS
207										+ mErrorCount * EVENT_POLL_ERROR_RETRY_SECONDS_INC
208									, this);
209
210			llwarns << "Unexpected HTTP error.  status: " << status << ", reason: " << reason << llendl;
211		}
212		else
213		{
214			llwarns <<	"LLEventPollResponder::error: <" << mCount << "> got "
215					<<	status << ": " << reason
216					<<	(mDone ? " -- done"	: "") << llendl;
217			stop();
218
219			// At this point we have given up and the viewer will not receive HTTP messages from the simulator.
220			// IMs, teleports, about land, selecing land, region crossing and more will all fail.
221			// They are essentially disconnected from the region even though some things may still work.
222			// Since things won't get better until they relog we force a disconnect now.
223
224			// *NOTE:Mani - The following condition check to see if this failing event poll
225			// is attached to the Agent's main region. If so we disconnect the viewer.
226			// Else... its a child region and we just leave the dead event poll stopped and 
227			// continue running.
228			if(gAgent.getRegion() && gAgent.getRegion()->getHost().getIPandPort() == mSender)
229			{
230				llwarns << "Forcing disconnect due to stalled main region event poll."  << llendl;
231				LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection"));
232			}
233		}
234	}
235
236	//virtual
237	void LLEventPollResponder::result(const LLSD& content)
238	{
239		lldebugs <<	"LLEventPollResponder::result <" << mCount	<< ">"
240				 <<	(mDone ? " -- done"	: "") << llendl;
241		
242		if (mDone) return;
243
244		mErrorCount = 0;
245
246		if (!content.get("events") ||
247			!content.get("id"))
248		{
249			llwarns << "received event poll with no events or id key" << llendl;
250			makeRequest();
251			return;
252		}
253		
254		mAcknowledge = content["id"];
255		LLSD events	= content["events"];
256
257		if(mAcknowledge.isUndefined())
258		{
259			llwarns << "LLEventPollResponder: id undefined" << llendl;
260		}
261		
262		// was llinfos but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG
263		lldebugs  << "LLEventPollResponder::completed <" <<	mCount << "> " << events.size() << "events (id "
264				 <<	LLSDXMLStreamer(mAcknowledge) << ")" << llendl;
265		
266		LLSD::array_const_iterator i = events.beginArray();
267		LLSD::array_const_iterator end = events.endArray();
268		for	(; i !=	end; ++i)
269		{
270			if (i->has("message"))
271			{
272				handleMessage(*i);
273			}
274		}
275		
276		makeRequest();
277	}	
278}
279
280LLEventPoll::LLEventPoll(const std::string&	poll_url, const LLHost& sender)
281	: mImpl(LLEventPollResponder::start(poll_url, sender))
282	{ }
283
284LLEventPoll::~LLEventPoll()
285{
286	LLHTTPClient::Responder* responderp = mImpl.get();
287	LLEventPollResponder* event_poll_responder = dynamic_cast<LLEventPollResponder*>(responderp);
288	if (event_poll_responder) event_poll_responder->stop();
289}