comparison deps/Platinum/Source/Core/PltHttpClientTask.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3425707ddbf6
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 /*----------------------------------------------------------------------
35 | includes
36 +---------------------------------------------------------------------*/
37 #include "PltHttpClientTask.h"
38
39 NPT_SET_LOCAL_LOGGER("platinum.core.http.clienttask")
40
41 /*----------------------------------------------------------------------
42 | PLT_HttpTcpConnector::PLT_HttpTcpConnector
43 +---------------------------------------------------------------------*/
44 PLT_HttpTcpConnector::PLT_HttpTcpConnector() :
45 NPT_HttpClient::Connector(),
46 m_Socket(new NPT_TcpClientSocket())
47 {
48 }
49
50 /*----------------------------------------------------------------------
51 | PLT_HttpTcpConnector::Connect
52 +---------------------------------------------------------------------*/
53 NPT_Result
54 PLT_HttpTcpConnector::Connect(const char* hostname,
55 NPT_UInt16 port,
56 NPT_Timeout connection_timeout,
57 NPT_Timeout io_timeout,
58 NPT_Timeout name_resolver_timeout,
59 NPT_InputStreamReference& input_stream,
60 NPT_OutputStreamReference& output_stream)
61 {
62 if (m_HostName == hostname && m_Port == port) {
63 input_stream = m_InputStream;
64 output_stream = m_OutputStream;
65 return NPT_SUCCESS;
66 }
67
68 // get the address and port to which we need to connect
69 NPT_IpAddress address;
70 NPT_CHECK_FATAL(address.ResolveName(hostname, name_resolver_timeout));
71
72 // connect to the server
73 NPT_LOG_FINE_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
79 // get and keep the streams
80 NPT_CHECK(m_Socket->GetInputStream(m_InputStream));
81 NPT_CHECK(m_Socket->GetOutputStream(m_OutputStream));
82 NPT_CHECK(m_Socket->GetInfo(m_SocketInfo));
83
84 m_HostName = hostname;
85 m_Port = port;
86 input_stream = m_InputStream;
87 output_stream = m_OutputStream;
88 return NPT_SUCCESS;
89 }
90
91 /*----------------------------------------------------------------------
92 | PLT_HttpClientSocketTask::PLT_HttpClientSocketTask
93 +---------------------------------------------------------------------*/
94 PLT_HttpClientSocketTask::PLT_HttpClientSocketTask(NPT_HttpRequest* request,
95 bool wait_forever /* = false */) :
96 m_WaitForever(wait_forever),
97 m_Connector(NULL)
98 {
99 if (request) m_Requests.Push(request);
100 }
101
102 /*----------------------------------------------------------------------
103 | PLT_HttpClientSocketTask::~PLT_HttpClientSocketTask
104 +---------------------------------------------------------------------*/
105 PLT_HttpClientSocketTask::~PLT_HttpClientSocketTask()
106 {
107 // delete any outstanding requests
108 NPT_HttpRequest* request;
109 while (NPT_SUCCEEDED(m_Requests.Pop(request, false))) {
110 delete request;
111 }
112 }
113
114 /*----------------------------------------------------------------------
115 | PLT_HttpServerSocketTask::AddRequest
116 +---------------------------------------------------------------------*/
117 NPT_Result
118 PLT_HttpClientSocketTask::AddRequest(NPT_HttpRequest* request)
119 {
120 return m_Requests.Push(request);
121 }
122
123 /*----------------------------------------------------------------------
124 | PLT_HttpServerSocketTask::GetNextRequest
125 +---------------------------------------------------------------------*/
126 NPT_Result
127 PLT_HttpClientSocketTask::GetNextRequest(NPT_HttpRequest*& request, NPT_Timeout timeout)
128 {
129 return m_Requests.Pop(request, timeout);
130 }
131
132 /*----------------------------------------------------------------------
133 | PLT_HttpServerSocketTask::SetConnector
134 +---------------------------------------------------------------------*/
135 NPT_Result
136 PLT_HttpClientSocketTask::SetConnector(PLT_HttpTcpConnector* connector)
137 {
138 NPT_AutoLock autolock(m_ConnectorLock);
139
140 if (IsAborting(0)) return NPT_ERROR_CONNECTION_ABORTED;
141
142 // NPT_HttpClient will delete old connector and own the new one
143 m_Client.SetConnector(connector);
144 m_Connector = connector;
145 return NPT_SUCCESS;
146 }
147
148 /*----------------------------------------------------------------------
149 | PLT_HttpServerSocketTask::DoAbort
150 +---------------------------------------------------------------------*/
151 void
152 PLT_HttpClientSocketTask::DoAbort()
153 {
154 NPT_AutoLock autolock(m_ConnectorLock);
155 if (m_Connector) m_Connector->Abort();
156 }
157
158 /*----------------------------------------------------------------------
159 | PLT_HttpServerSocketTask::DoRun
160 +---------------------------------------------------------------------*/
161 void
162 PLT_HttpClientSocketTask::DoRun()
163 {
164 NPT_HttpRequest* request;
165 NPT_HttpRequestContext context;
166 bool using_previous_connector;
167 NPT_Result res;
168 NPT_HttpResponse* response;
169
170 using_previous_connector = false;
171 SetConnector(new PLT_HttpTcpConnector());
172
173 do {
174 // pop next request or wait for one for 100ms
175 if (NPT_SUCCEEDED(GetNextRequest(request, 100))) {
176 response = NULL;
177 retry:
178 // if body is not seekable, don't even try to
179 // reuse previous connector since in case it fails because
180 // server closed connection, we won't be able to
181 // rewind the body to resend the request
182 if (!PLT_HttpHelper::IsBodyStreamSeekable(*request) && using_previous_connector) {
183 using_previous_connector = false;
184 if (NPT_FAILED(SetConnector(new PLT_HttpTcpConnector())))
185 goto abort;
186 }
187
188 if (IsAborting(0)) goto abort;
189
190 // send request
191 res = m_Client.SendRequest(*request, response);
192
193 // retry only if we were reusing a previous connector
194 if (NPT_FAILED(res) && using_previous_connector) {
195 using_previous_connector = false;
196 if (NPT_FAILED(SetConnector(new PLT_HttpTcpConnector())))
197 goto abort;
198
199 // server may have closed socket on us
200 NPT_HttpEntity* entity = request->GetEntity();
201 NPT_InputStreamReference input_stream;
202
203 // rewind request body if any to be able to resend it
204 if (entity && NPT_SUCCEEDED(entity->GetInputStream(input_stream))) {
205 input_stream->Seek(0);
206 }
207
208 goto retry;
209 }
210
211 NPT_LOG_FINE_1("PLT_HttpClientSocketTask receiving: res = %d", res);
212 PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, response);
213
214 // callback to process response
215 NPT_SocketInfo info;
216 m_Connector->GetInfo(info);
217 context.SetLocalAddress(info.local_address);
218 context.SetRemoteAddress(info.remote_address);
219 ProcessResponse(res, request, context, response);
220
221 // check if server says keep-alive to keep our connector
222 if (response && PLT_HttpHelper::IsConnectionKeepAlive(*response)) {
223 using_previous_connector = true;
224 } else {
225 using_previous_connector = false;
226 if (NPT_FAILED(SetConnector(new PLT_HttpTcpConnector())))
227 goto abort;
228 }
229
230 // cleanup
231 delete response;
232 response = NULL;
233 delete request;
234 request = NULL;
235 }
236 } while (m_WaitForever && !IsAborting(0));
237
238 abort:
239 if (request) delete request;
240 if (response) delete response;
241 }
242
243 /*----------------------------------------------------------------------
244 | PLT_HttpServerSocketTask::ProcessResponse
245 +---------------------------------------------------------------------*/
246 NPT_Result
247 PLT_HttpClientSocketTask::ProcessResponse(NPT_Result res,
248 NPT_HttpRequest* request,
249 const NPT_HttpRequestContext& context,
250 NPT_HttpResponse* response)
251 {
252 NPT_COMPILER_UNUSED(request);
253 NPT_COMPILER_UNUSED(context);
254
255 NPT_LOG_FINE_1("PLT_HttpClientSocketTask::ProcessResponse (result=%d)", res);
256 NPT_CHECK_WARNING(res);
257
258 NPT_HttpEntity* entity;
259 NPT_InputStreamReference body;
260 if (!response ||
261 !(entity = response->GetEntity()) ||
262 NPT_FAILED(entity->GetInputStream(body))) {
263 return NPT_FAILURE;
264 }
265
266 // dump body into memory (if no content-length specified, read until disconnection)
267 NPT_MemoryStream output;
268 NPT_CHECK_SEVERE(NPT_StreamToStreamCopy(*body,
269 output,
270 0,
271 entity->GetContentLength()));
272
273 return NPT_SUCCESS;
274 }
275
276 /*----------------------------------------------------------------------
277 | PLT_FileHttpClientTask::ProcessResponse
278 +---------------------------------------------------------------------*/
279 NPT_Result
280 PLT_FileHttpClientTask::ProcessResponse(NPT_Result res,
281 NPT_HttpRequest* request,
282 const NPT_HttpRequestContext& context,
283 NPT_HttpResponse* response)
284 {
285 NPT_COMPILER_UNUSED(res);
286 NPT_COMPILER_UNUSED(request);
287 NPT_COMPILER_UNUSED(context);
288 NPT_COMPILER_UNUSED(response);
289
290 NPT_LOG_FINE_1("PLT_FileHttpClientTask::ProcessResponse (status=%d)\n", res);
291 return NPT_SUCCESS;
292 }