Mercurial > projects > hoofbaby
comparison deps/Platinum/Source/Core/PltHttpServerTask.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 Server 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 "PltHttpServerTask.h" | |
38 #include "PltHttp.h" | |
39 #include "PltVersion.h" | |
40 | |
41 NPT_SET_LOCAL_LOGGER("platinum.core.http.servertask") | |
42 | |
43 /*---------------------------------------------------------------------- | |
44 | external references | |
45 +---------------------------------------------------------------------*/ | |
46 extern NPT_String HttpServerHeader; | |
47 | |
48 /*---------------------------------------------------------------------- | |
49 | PLT_HttpServerSocketTask::PLT_HttpServerSocketTask | |
50 +---------------------------------------------------------------------*/ | |
51 PLT_HttpServerSocketTask::PLT_HttpServerSocketTask(NPT_Socket* socket, | |
52 bool stay_alive_forever) : | |
53 m_Socket(socket), | |
54 m_StayAliveForever(stay_alive_forever) | |
55 { | |
56 socket->SetReadTimeout(30000); | |
57 socket->SetWriteTimeout(30000); | |
58 } | |
59 | |
60 /*---------------------------------------------------------------------- | |
61 | PLT_HttpServerSocketTask::~PLT_HttpServerSocketTask | |
62 +---------------------------------------------------------------------*/ | |
63 PLT_HttpServerSocketTask::~PLT_HttpServerSocketTask() | |
64 { | |
65 delete m_Socket; | |
66 } | |
67 | |
68 /*---------------------------------------------------------------------- | |
69 | PLT_HttpServerSocketTask::DoRun | |
70 +---------------------------------------------------------------------*/ | |
71 void | |
72 PLT_HttpServerSocketTask::DoRun() | |
73 { | |
74 NPT_BufferedInputStreamReference buffered_input_stream; | |
75 NPT_HttpRequestContext context; | |
76 NPT_Result res = NPT_SUCCESS; | |
77 bool headers_only; | |
78 bool keep_alive = false; | |
79 | |
80 // create a buffered input stream to parse http request | |
81 // as it comes | |
82 NPT_InputStreamReference input_stream; | |
83 NPT_CHECK_LABEL_SEVERE(GetInputStream(input_stream), done); | |
84 buffered_input_stream = new NPT_BufferedInputStream(input_stream); | |
85 | |
86 while (!IsAborting(0)) { | |
87 NPT_HttpRequest* request = NULL; | |
88 NPT_HttpResponse* response = NULL; | |
89 | |
90 // reset keep-alive in case of failure | |
91 keep_alive = false; | |
92 | |
93 // wait for a request | |
94 res = Read(buffered_input_stream, request, &context); | |
95 if (NPT_FAILED(res) || (request == NULL)) goto cleanup; | |
96 | |
97 // callback to process request and get back a response | |
98 headers_only = false; | |
99 res = ProcessRequest(*request, context, response, headers_only); | |
100 if (NPT_FAILED(res) || (response == NULL)) goto cleanup; | |
101 | |
102 // send back response | |
103 keep_alive = PLT_HttpHelper::IsConnectionKeepAlive(*request); | |
104 res = Write(response, keep_alive, headers_only); | |
105 | |
106 cleanup: | |
107 // cleanup | |
108 delete request; | |
109 delete response; | |
110 | |
111 if (!keep_alive && !m_StayAliveForever) { | |
112 // if we were to support persistent connections | |
113 // we would stop only if res would be a failure | |
114 // (like a timeout or a read/write error) | |
115 goto done; | |
116 } | |
117 } | |
118 | |
119 done: | |
120 return; | |
121 } | |
122 | |
123 /*---------------------------------------------------------------------- | |
124 | PLT_HttpServerSocketTask::GetInputStream | |
125 +---------------------------------------------------------------------*/ | |
126 NPT_Result | |
127 PLT_HttpServerSocketTask::GetInputStream(NPT_InputStreamReference& stream) | |
128 { | |
129 return m_Socket->GetInputStream(stream); | |
130 } | |
131 | |
132 /*---------------------------------------------------------------------- | |
133 | PLT_HttpServerSocketTask::GetInfo | |
134 +---------------------------------------------------------------------*/ | |
135 NPT_Result | |
136 PLT_HttpServerSocketTask::GetInfo(NPT_SocketInfo& info) | |
137 { | |
138 return m_Socket->GetInfo(info); | |
139 } | |
140 | |
141 /*---------------------------------------------------------------------- | |
142 | PLT_HttpServerSocketTask::Read | |
143 +---------------------------------------------------------------------*/ | |
144 NPT_Result | |
145 PLT_HttpServerSocketTask::Read(NPT_BufferedInputStreamReference& buffered_input_stream, | |
146 NPT_HttpRequest*& request, | |
147 NPT_HttpRequestContext* context) | |
148 { | |
149 NPT_SocketInfo info; | |
150 GetInfo(info); | |
151 | |
152 if (context) { | |
153 // extract socket info | |
154 context->SetLocalAddress(info.local_address); | |
155 context->SetRemoteAddress(info.remote_address); | |
156 } | |
157 | |
158 // parse request | |
159 NPT_Result res = NPT_HttpRequest::Parse(*buffered_input_stream, &info.local_address, request); | |
160 if (NPT_FAILED(res) || !request) { | |
161 // only log if not timeout | |
162 res = NPT_FAILED(res)?res:NPT_FAILURE; | |
163 if (res != NPT_ERROR_TIMEOUT && res != NPT_ERROR_EOS) NPT_CHECK_WARNING(res); | |
164 return res; | |
165 } | |
166 | |
167 // read socket info again to refresh the remote address in case it was a udp socket | |
168 GetInfo(info); | |
169 if (context) { | |
170 context->SetLocalAddress(info.local_address); | |
171 context->SetRemoteAddress(info.remote_address); | |
172 } | |
173 | |
174 // return right away if no body is expected | |
175 if (request->GetMethod() == NPT_HTTP_METHOD_GET || | |
176 request->GetMethod() == NPT_HTTP_METHOD_HEAD) | |
177 return NPT_SUCCESS; | |
178 | |
179 // create an entity | |
180 NPT_HttpEntity* request_entity = new NPT_HttpEntity(request->GetHeaders()); | |
181 request->SetEntity(request_entity); | |
182 | |
183 // buffer body now if any | |
184 if (request_entity->GetContentLength() > 0) { | |
185 // unbuffer the stream | |
186 buffered_input_stream->SetBufferSize(0); | |
187 | |
188 NPT_MemoryStream* body_stream = new NPT_MemoryStream(); | |
189 NPT_CHECK_SEVERE(NPT_StreamToStreamCopy( | |
190 *buffered_input_stream.AsPointer(), | |
191 *body_stream, | |
192 0, | |
193 request_entity->GetContentLength())); | |
194 | |
195 // set entity body | |
196 request_entity->SetInputStream((NPT_InputStreamReference)body_stream); | |
197 | |
198 // rebuffer the stream | |
199 buffered_input_stream->SetBufferSize(NPT_BUFFERED_BYTE_STREAM_DEFAULT_SIZE); | |
200 } | |
201 | |
202 return NPT_SUCCESS; | |
203 } | |
204 | |
205 /*---------------------------------------------------------------------- | |
206 | PLT_HttpServerSocketTask::Write | |
207 +---------------------------------------------------------------------*/ | |
208 NPT_Result | |
209 PLT_HttpServerSocketTask::Write(NPT_HttpResponse* response, | |
210 bool& keep_alive, | |
211 bool headers_only /* = false */) | |
212 { | |
213 // add any headers that may be missing | |
214 NPT_HttpHeaders& headers = response->GetHeaders(); | |
215 const NPT_String* value = headers.GetHeaderValue(NPT_HTTP_HEADER_CONNECTION); | |
216 if (value) { | |
217 // only replace Connection header if already set and keep_alive passed not allowed | |
218 if (!keep_alive) { | |
219 headers.SetHeader(NPT_HTTP_HEADER_CONNECTION, "close"); // override | |
220 } else { | |
221 keep_alive = value->Compare("keep-alive") == 0; // keep-alive ok but use whatever was set | |
222 } | |
223 } else { | |
224 headers.SetHeader(NPT_HTTP_HEADER_CONNECTION, keep_alive?"keep-alive":"close"); | |
225 } | |
226 | |
227 // set user agent | |
228 headers.SetHeader(NPT_HTTP_HEADER_SERVER, | |
229 NPT_HttpServer::m_ServerHeader, false); // set but don't replace | |
230 | |
231 // get the response entity to set additional headers | |
232 NPT_HttpEntity* entity = response->GetEntity(); | |
233 if (entity) { | |
234 if (entity->HasContentLength()) { | |
235 // content length | |
236 headers.SetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH, | |
237 NPT_String::FromIntegerU(entity->GetContentLength())); | |
238 } | |
239 | |
240 // content type | |
241 NPT_String content_type = entity->GetContentType(); | |
242 if (!content_type.IsEmpty()) { | |
243 headers.SetHeader(NPT_HTTP_HEADER_CONTENT_TYPE, content_type); | |
244 } | |
245 | |
246 // content encoding | |
247 NPT_String content_encoding = entity->GetContentEncoding(); | |
248 if (!content_encoding.IsEmpty()) { | |
249 headers.SetHeader(NPT_HTTP_HEADER_CONTENT_ENCODING, content_encoding); | |
250 } | |
251 } else { | |
252 // force content length to 0 is there is no message body | |
253 headers.SetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH, "0"); | |
254 } | |
255 | |
256 //headers.SetHeader("DATE", "Wed, 13 Feb 2008 22:32:57 GMT"); | |
257 //headers.SetHeader("Accept-Ranges", "bytes"); | |
258 | |
259 NPT_LOG_FINE("PLT_HttpServerTask Sending response:"); | |
260 PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, response); | |
261 | |
262 // get the socket stream to send the request | |
263 NPT_OutputStreamReference output_stream; | |
264 NPT_CHECK_SEVERE(m_Socket->GetOutputStream(output_stream)); | |
265 | |
266 // create a memory stream to buffer the headers | |
267 NPT_MemoryStream header_stream; | |
268 | |
269 // emit the response headers into the header buffer | |
270 response->Emit(header_stream); | |
271 | |
272 // send the headers | |
273 NPT_CHECK_SEVERE(output_stream->WriteFully(header_stream.GetData(), header_stream.GetDataSize())); | |
274 | |
275 // send response body if any | |
276 if (!headers_only && entity) { | |
277 NPT_InputStreamReference body_stream; | |
278 entity->GetInputStream(body_stream); | |
279 | |
280 if (!body_stream.IsNull()) { | |
281 NPT_CHECK_SEVERE(NPT_StreamToStreamCopy( | |
282 *body_stream.AsPointer(), | |
283 *output_stream.AsPointer(), | |
284 0, | |
285 entity->GetContentLength())); | |
286 } | |
287 } | |
288 | |
289 // flush the output stream so that everything is sent to the server | |
290 output_stream->Flush(); | |
291 | |
292 return NPT_SUCCESS; | |
293 } | |
294 |