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