PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/libUPnP/Platinum/Source/Core/PltHttpClientTask.cpp

https://github.com/hgmeier/xbmc
C++ | 319 lines | 186 code | 41 blank | 92 comment | 29 complexity | 0d7d696587687a03b5e331ba00e407f9 MD5 | raw file
  1. /*****************************************************************
  2. |
  3. | Platinum - HTTP Client Tasks
  4. |
  5. | Copyright (c) 2004-2008, Plutinosoft, LLC.
  6. | All rights reserved.
  7. | http://www.plutinosoft.com
  8. |
  9. | This program is free software; you can redistribute it and/or
  10. | modify it under the terms of the GNU General Public License
  11. | as published by the Free Software Foundation; either version 2
  12. | of the License, or (at your option) any later version.
  13. |
  14. | OEMs, ISVs, VARs and other distributors that combine and
  15. | distribute commercially licensed software with Platinum software
  16. | and do not wish to distribute the source code for the commercially
  17. | licensed software under version 2, or (at your option) any later
  18. | version, of the GNU General Public License (the "GPL") must enter
  19. | into a commercial license agreement with Plutinosoft, LLC.
  20. |
  21. | This program is distributed in the hope that it will be useful,
  22. | but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. | GNU General Public License for more details.
  25. |
  26. | You should have received a copy of the GNU General Public License
  27. | along with this program; see the file LICENSE.txt. If not, write to
  28. | the Free Software Foundation, Inc.,
  29. | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  30. | http://www.gnu.org/licenses/gpl-2.0.html
  31. |
  32. ****************************************************************/
  33. /*----------------------------------------------------------------------
  34. | includes
  35. +---------------------------------------------------------------------*/
  36. #include "PltHttpClientTask.h"
  37. NPT_SET_LOCAL_LOGGER("platinum.core.http.clienttask")
  38. /*----------------------------------------------------------------------
  39. | PLT_HttpTcpConnector::PLT_HttpTcpConnector
  40. +---------------------------------------------------------------------*/
  41. PLT_HttpTcpConnector::PLT_HttpTcpConnector() :
  42. NPT_HttpClient::Connector(),
  43. m_Socket(new NPT_TcpClientSocket())
  44. {
  45. }
  46. /*----------------------------------------------------------------------
  47. | PLT_HttpTcpConnector::~PLT_HttpTcpConnector
  48. +---------------------------------------------------------------------*/
  49. PLT_HttpTcpConnector::~PLT_HttpTcpConnector()
  50. {
  51. }
  52. /*----------------------------------------------------------------------
  53. | PLT_HttpTcpConnector::Connect
  54. +---------------------------------------------------------------------*/
  55. NPT_Result
  56. PLT_HttpTcpConnector::Connect(const char* hostname,
  57. NPT_UInt16 port,
  58. NPT_Timeout connection_timeout,
  59. NPT_Timeout io_timeout,
  60. NPT_Timeout name_resolver_timeout,
  61. NPT_InputStreamReference& input_stream,
  62. NPT_OutputStreamReference& output_stream)
  63. {
  64. if (m_HostName == hostname && m_Port == port) {
  65. input_stream = m_InputStream;
  66. output_stream = m_OutputStream;
  67. return NPT_SUCCESS;
  68. }
  69. // get the address and port to which we need to connect
  70. NPT_IpAddress address;
  71. NPT_CHECK_FATAL(address.ResolveName(hostname, name_resolver_timeout));
  72. // connect to the server
  73. NPT_LOG_FINER_2("NPT_HttpTcpConnector::Connect - will connect to %s:%d\n", hostname, port);
  74. m_Socket->SetReadTimeout(io_timeout);
  75. m_Socket->SetWriteTimeout(io_timeout);
  76. NPT_SocketAddress socket_address(address, port);
  77. NPT_CHECK_FATAL(m_Socket->Connect(socket_address, connection_timeout));
  78. // get and keep the streams
  79. NPT_CHECK(m_Socket->GetInputStream(m_InputStream));
  80. NPT_CHECK(m_Socket->GetOutputStream(m_OutputStream));
  81. NPT_CHECK(m_Socket->GetInfo(m_SocketInfo));
  82. m_HostName = hostname;
  83. m_Port = port;
  84. input_stream = m_InputStream;
  85. output_stream = m_OutputStream;
  86. return NPT_SUCCESS;
  87. }
  88. /*----------------------------------------------------------------------
  89. | PLT_HttpClientSocketTask::PLT_HttpClientSocketTask
  90. +---------------------------------------------------------------------*/
  91. PLT_HttpClientSocketTask::PLT_HttpClientSocketTask(NPT_HttpRequest* request /* = NULL */,
  92. bool wait_forever /* = false */) :
  93. m_WaitForever(wait_forever),
  94. m_Connector(NULL)
  95. {
  96. if (request) m_Requests.Push(request);
  97. }
  98. /*----------------------------------------------------------------------
  99. | PLT_HttpClientSocketTask::~PLT_HttpClientSocketTask
  100. +---------------------------------------------------------------------*/
  101. PLT_HttpClientSocketTask::~PLT_HttpClientSocketTask()
  102. {
  103. // delete any outstanding requests
  104. NPT_HttpRequest* request;
  105. while (NPT_SUCCEEDED(m_Requests.Pop(request, false))) {
  106. delete request;
  107. }
  108. }
  109. /*----------------------------------------------------------------------
  110. | PLT_HttpServerSocketTask::AddRequest
  111. +---------------------------------------------------------------------*/
  112. NPT_Result
  113. PLT_HttpClientSocketTask::AddRequest(NPT_HttpRequest* request)
  114. {
  115. return m_Requests.Push(request);
  116. }
  117. /*----------------------------------------------------------------------
  118. | PLT_HttpServerSocketTask::GetNextRequest
  119. +---------------------------------------------------------------------*/
  120. NPT_Result
  121. PLT_HttpClientSocketTask::GetNextRequest(NPT_HttpRequest*& request, NPT_Timeout timeout)
  122. {
  123. return m_Requests.Pop(request, timeout);
  124. }
  125. /*----------------------------------------------------------------------
  126. | PLT_HttpServerSocketTask::SetConnector
  127. +---------------------------------------------------------------------*/
  128. NPT_Result
  129. PLT_HttpClientSocketTask::SetConnector(PLT_HttpTcpConnector* connector)
  130. {
  131. if (IsAborting(0)) return NPT_ERROR_CONNECTION_ABORTED;
  132. // NPT_HttpClient will delete old connector and own the new one
  133. m_Client.SetConnector(connector);
  134. m_Connector = connector;
  135. return NPT_SUCCESS;
  136. }
  137. /*----------------------------------------------------------------------
  138. | PLT_HttpServerSocketTask::DoAbort
  139. +---------------------------------------------------------------------*/
  140. void
  141. PLT_HttpClientSocketTask::DoAbort()
  142. {
  143. NPT_AutoLock autolock(m_ConnectorLock);
  144. if (m_Connector) m_Connector->Abort();
  145. }
  146. /*----------------------------------------------------------------------
  147. | PLT_HttpServerSocketTask::DoRun
  148. +---------------------------------------------------------------------*/
  149. void
  150. PLT_HttpClientSocketTask::DoRun()
  151. {
  152. NPT_HttpRequest* request = NULL;
  153. NPT_HttpRequestContext context;
  154. bool using_previous_connector = false;
  155. NPT_Result res;
  156. NPT_HttpResponse* response = NULL;
  157. NPT_TimeStamp watchdog;
  158. NPT_System::GetCurrentTimeStamp(watchdog);
  159. do {
  160. // pop next request or wait for one for 100ms
  161. while (NPT_SUCCEEDED(GetNextRequest(request, 100))) {
  162. response = NULL;
  163. retry:
  164. // if body is not seekable, don't even try to
  165. // reuse previous connector since in case it fails because
  166. // server closed connection, we won't be able to
  167. // rewind the body to resend the request
  168. if (!PLT_HttpHelper::IsBodyStreamSeekable(*request) && using_previous_connector) {
  169. NPT_AutoLock autolock(m_ConnectorLock);
  170. using_previous_connector = false;
  171. NPT_CHECK_LABEL_WARNING(SetConnector(NULL), abort);
  172. }
  173. {
  174. // assign a new connector if needed
  175. NPT_AutoLock autolock(m_ConnectorLock);
  176. if (!m_Connector) NPT_CHECK_LABEL_WARNING(SetConnector(new PLT_HttpTcpConnector()), abort);
  177. }
  178. if (IsAborting(0)) goto abort;
  179. // send request
  180. res = m_Client.SendRequest(*request, response);
  181. // retry only if we were reusing a previous connector
  182. if (NPT_FAILED(res) && using_previous_connector) {
  183. using_previous_connector = false;
  184. {
  185. NPT_AutoLock autolock(m_ConnectorLock);
  186. NPT_CHECK_LABEL_WARNING(SetConnector(NULL), abort);
  187. }
  188. // server may have closed socket on us
  189. NPT_HttpEntity* entity = request->GetEntity();
  190. NPT_InputStreamReference input_stream;
  191. // rewind request body if any to be able to resend it
  192. if (entity && NPT_SUCCEEDED(entity->GetInputStream(input_stream)) && !input_stream.IsNull()) {
  193. input_stream->Seek(0);
  194. }
  195. goto retry;
  196. }
  197. NPT_LOG_FINER_1("PLT_HttpClientSocketTask receiving: res = %d", res);
  198. PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINER, response);
  199. // callback to process response
  200. NPT_SocketInfo info;
  201. m_Connector->GetInfo(info);
  202. context.SetLocalAddress(info.local_address);
  203. context.SetRemoteAddress(info.remote_address);
  204. ProcessResponse(res, request, context, response);
  205. // check if server says keep-alive to keep our connector
  206. if (response && PLT_HttpHelper::IsConnectionKeepAlive(*response)) {
  207. using_previous_connector = true;
  208. } else {
  209. using_previous_connector = false;
  210. NPT_AutoLock autolock(m_ConnectorLock);
  211. NPT_CHECK_LABEL_WARNING(SetConnector(NULL), abort);
  212. }
  213. // cleanup
  214. delete response;
  215. response = NULL;
  216. delete request;
  217. request = NULL;
  218. }
  219. // DLNA requires that we abort unanswered/unused sockets after 60 secs
  220. NPT_TimeStamp now;
  221. NPT_System::GetCurrentTimeStamp(now);
  222. if (now > watchdog + NPT_TimeInterval(60, 0)) {
  223. using_previous_connector = false;
  224. NPT_AutoLock autolock(m_ConnectorLock);
  225. NPT_CHECK_LABEL_WARNING(SetConnector(NULL), abort);
  226. watchdog = now;
  227. }
  228. } while (m_WaitForever && !IsAborting(0));
  229. abort:
  230. if (request) delete request;
  231. if (response) delete response;
  232. }
  233. /*----------------------------------------------------------------------
  234. | PLT_HttpServerSocketTask::ProcessResponse
  235. +---------------------------------------------------------------------*/
  236. NPT_Result
  237. PLT_HttpClientSocketTask::ProcessResponse(NPT_Result res,
  238. NPT_HttpRequest* request,
  239. const NPT_HttpRequestContext& context,
  240. NPT_HttpResponse* response)
  241. {
  242. NPT_COMPILER_UNUSED(request);
  243. NPT_COMPILER_UNUSED(context);
  244. NPT_LOG_FINE_1("PLT_HttpClientSocketTask::ProcessResponse (result=%d)", res);
  245. NPT_CHECK_WARNING(res);
  246. NPT_HttpEntity* entity;
  247. NPT_InputStreamReference body;
  248. if (!response ||
  249. !(entity = response->GetEntity()) ||
  250. NPT_FAILED(entity->GetInputStream(body)) ||
  251. body.IsNull()) {
  252. return NPT_FAILURE;
  253. }
  254. // dump body into memory
  255. // (if no content-length specified, read until disconnected)
  256. NPT_MemoryStream output;
  257. NPT_CHECK_SEVERE(NPT_StreamToStreamCopy(*body,
  258. output,
  259. 0,
  260. entity->GetContentLength()));
  261. return NPT_SUCCESS;
  262. }
  263. /*----------------------------------------------------------------------
  264. | PLT_FileHttpClientTask::ProcessResponse
  265. +---------------------------------------------------------------------*/
  266. NPT_Result
  267. PLT_FileHttpClientTask::ProcessResponse(NPT_Result res,
  268. NPT_HttpRequest* request,
  269. const NPT_HttpRequestContext& context,
  270. NPT_HttpResponse* response)
  271. {
  272. NPT_COMPILER_UNUSED(res);
  273. NPT_COMPILER_UNUSED(request);
  274. NPT_COMPILER_UNUSED(context);
  275. NPT_COMPILER_UNUSED(response);
  276. NPT_LOG_FINE_1("PLT_FileHttpClientTask::ProcessResponse (status=%d)\n", res);
  277. return NPT_SUCCESS;
  278. }