PageRenderTime 34ms CodeModel.GetById 19ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1
  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. #include "llviewerprecompiledheaders.h"
  27. #include "lleventpoll.h"
  28. #include "llappviewer.h"
  29. #include "llagent.h"
  30. #include "llhttpclient.h"
  31. #include "llhttpstatuscodes.h"
  32. #include "llsdserialize.h"
  33. #include "lleventtimer.h"
  34. #include "llviewerregion.h"
  35. #include "message.h"
  36. #include "lltrans.h"
  37. namespace
  38. {
  39. // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
  40. // This means we attempt to recover relatively quickly but back off giving more time to recover
  41. // until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
  42. const F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
  43. const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
  44. const S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
  45. class LLEventPollResponder : public LLHTTPClient::Responder
  46. {
  47. public:
  48. static LLHTTPClient::ResponderPtr start(const std::string& pollURL, const LLHost& sender);
  49. void stop();
  50. void makeRequest();
  51. private:
  52. LLEventPollResponder(const std::string& pollURL, const LLHost& sender);
  53. ~LLEventPollResponder();
  54. void handleMessage(const LLSD& content);
  55. virtual void error(U32 status, const std::string& reason);
  56. virtual void result(const LLSD& content);
  57. virtual void completedRaw(U32 status,
  58. const std::string& reason,
  59. const LLChannelDescriptors& channels,
  60. const LLIOPipe::buffer_ptr_t& buffer);
  61. private:
  62. bool mDone;
  63. std::string mPollURL;
  64. std::string mSender;
  65. LLSD mAcknowledge;
  66. // these are only here for debugging so we can see which poller is which
  67. static int sCount;
  68. int mCount;
  69. S32 mErrorCount;
  70. };
  71. class LLEventPollEventTimer : public LLEventTimer
  72. {
  73. typedef boost::intrusive_ptr<LLEventPollResponder> EventPollResponderPtr;
  74. public:
  75. LLEventPollEventTimer(F32 period, EventPollResponderPtr responder)
  76. : LLEventTimer(period), mResponder(responder)
  77. { }
  78. virtual BOOL tick()
  79. {
  80. mResponder->makeRequest();
  81. return TRUE; // Causes this instance to be deleted.
  82. }
  83. private:
  84. EventPollResponderPtr mResponder;
  85. };
  86. //static
  87. LLHTTPClient::ResponderPtr LLEventPollResponder::start(
  88. const std::string& pollURL, const LLHost& sender)
  89. {
  90. LLHTTPClient::ResponderPtr result = new LLEventPollResponder(pollURL, sender);
  91. llinfos << "LLEventPollResponder::start <" << sCount << "> "
  92. << pollURL << llendl;
  93. return result;
  94. }
  95. void LLEventPollResponder::stop()
  96. {
  97. llinfos << "LLEventPollResponder::stop <" << mCount << "> "
  98. << mPollURL << llendl;
  99. // there should be a way to stop a LLHTTPClient request in progress
  100. mDone = true;
  101. }
  102. int LLEventPollResponder::sCount = 0;
  103. LLEventPollResponder::LLEventPollResponder(const std::string& pollURL, const LLHost& sender)
  104. : mDone(false),
  105. mPollURL(pollURL),
  106. mCount(++sCount),
  107. mErrorCount(0)
  108. {
  109. //extract host and port of simulator to set as sender
  110. LLViewerRegion *regionp = gAgent.getRegion();
  111. if (!regionp)
  112. {
  113. llerrs << "LLEventPoll initialized before region is added." << llendl;
  114. }
  115. mSender = sender.getIPandPort();
  116. llinfos << "LLEventPoll initialized with sender " << mSender << llendl;
  117. makeRequest();
  118. }
  119. LLEventPollResponder::~LLEventPollResponder()
  120. {
  121. stop();
  122. lldebugs << "LLEventPollResponder::~Impl <" << mCount << "> "
  123. << mPollURL << llendl;
  124. }
  125. // virtual
  126. void LLEventPollResponder::completedRaw(U32 status,
  127. const std::string& reason,
  128. const LLChannelDescriptors& channels,
  129. const LLIOPipe::buffer_ptr_t& buffer)
  130. {
  131. if (status == HTTP_BAD_GATEWAY)
  132. {
  133. // These errors are not parsable as LLSD,
  134. // which LLHTTPClient::Responder::completedRaw will try to do.
  135. completed(status, reason, LLSD());
  136. }
  137. else
  138. {
  139. LLHTTPClient::Responder::completedRaw(status,reason,channels,buffer);
  140. }
  141. }
  142. void LLEventPollResponder::makeRequest()
  143. {
  144. LLSD request;
  145. request["ack"] = mAcknowledge;
  146. request["done"] = mDone;
  147. lldebugs << "LLEventPollResponder::makeRequest <" << mCount << "> ack = "
  148. << LLSDXMLStreamer(mAcknowledge) << llendl;
  149. LLHTTPClient::post(mPollURL, request, this);
  150. }
  151. void LLEventPollResponder::handleMessage(const LLSD& content)
  152. {
  153. std::string msg_name = content["message"];
  154. LLSD message;
  155. message["sender"] = mSender;
  156. message["body"] = content["body"];
  157. LLMessageSystem::dispatch(msg_name, message);
  158. }
  159. //virtual
  160. void LLEventPollResponder::error(U32 status, const std::string& reason)
  161. {
  162. if (mDone) return;
  163. // A HTTP_BAD_GATEWAY (502) error is our standard timeout response
  164. // we get this when there are no events.
  165. if ( status == HTTP_BAD_GATEWAY )
  166. {
  167. mErrorCount = 0;
  168. makeRequest();
  169. }
  170. else if (mErrorCount < MAX_EVENT_POLL_HTTP_ERRORS)
  171. {
  172. ++mErrorCount;
  173. // The 'tick' will return TRUE causing the timer to delete this.
  174. new LLEventPollEventTimer(EVENT_POLL_ERROR_RETRY_SECONDS
  175. + mErrorCount * EVENT_POLL_ERROR_RETRY_SECONDS_INC
  176. , this);
  177. llwarns << "Unexpected HTTP error. status: " << status << ", reason: " << reason << llendl;
  178. }
  179. else
  180. {
  181. llwarns << "LLEventPollResponder::error: <" << mCount << "> got "
  182. << status << ": " << reason
  183. << (mDone ? " -- done" : "") << llendl;
  184. stop();
  185. // At this point we have given up and the viewer will not receive HTTP messages from the simulator.
  186. // IMs, teleports, about land, selecing land, region crossing and more will all fail.
  187. // They are essentially disconnected from the region even though some things may still work.
  188. // Since things won't get better until they relog we force a disconnect now.
  189. // *NOTE:Mani - The following condition check to see if this failing event poll
  190. // is attached to the Agent's main region. If so we disconnect the viewer.
  191. // Else... its a child region and we just leave the dead event poll stopped and
  192. // continue running.
  193. if(gAgent.getRegion() && gAgent.getRegion()->getHost().getIPandPort() == mSender)
  194. {
  195. llwarns << "Forcing disconnect due to stalled main region event poll." << llendl;
  196. LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection"));
  197. }
  198. }
  199. }
  200. //virtual
  201. void LLEventPollResponder::result(const LLSD& content)
  202. {
  203. lldebugs << "LLEventPollResponder::result <" << mCount << ">"
  204. << (mDone ? " -- done" : "") << llendl;
  205. if (mDone) return;
  206. mErrorCount = 0;
  207. if (!content.get("events") ||
  208. !content.get("id"))
  209. {
  210. llwarns << "received event poll with no events or id key" << llendl;
  211. makeRequest();
  212. return;
  213. }
  214. mAcknowledge = content["id"];
  215. LLSD events = content["events"];
  216. if(mAcknowledge.isUndefined())
  217. {
  218. llwarns << "LLEventPollResponder: id undefined" << llendl;
  219. }
  220. // was llinfos but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG
  221. lldebugs << "LLEventPollResponder::completed <" << mCount << "> " << events.size() << "events (id "
  222. << LLSDXMLStreamer(mAcknowledge) << ")" << llendl;
  223. LLSD::array_const_iterator i = events.beginArray();
  224. LLSD::array_const_iterator end = events.endArray();
  225. for (; i != end; ++i)
  226. {
  227. if (i->has("message"))
  228. {
  229. handleMessage(*i);
  230. }
  231. }
  232. makeRequest();
  233. }
  234. }
  235. LLEventPoll::LLEventPoll(const std::string& poll_url, const LLHost& sender)
  236. : mImpl(LLEventPollResponder::start(poll_url, sender))
  237. { }
  238. LLEventPoll::~LLEventPoll()
  239. {
  240. LLHTTPClient::Responder* responderp = mImpl.get();
  241. LLEventPollResponder* event_poll_responder = dynamic_cast<LLEventPollResponder*>(responderp);
  242. if (event_poll_responder) event_poll_responder->stop();
  243. }