2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 #include <ClearSilver/ClearSilver.h>
27 #include <threading/thread.h>
28 #include <threading/thread_value.h>
30 typedef struct private_request_t private_request_t
;
33 * private data of the task manager
35 struct private_request_t
{
43 * FastCGI request object
48 * length of the req.envp array
53 * ClearSilver CGI Kit context
58 * ClearSilver HDF dataset for this request
74 * ClearSilver cgiwrap is not threadsave, so we use a private
75 * context for each thread.
77 static thread_value_t
*thread_this
;
80 * control variable for pthread_once
82 pthread_once_t once
= PTHREAD_ONCE_INIT
;
85 * fcgiwrap read callback
87 static int read_cb(void *null
, char *buf
, int size
)
89 private_request_t
*this = (private_request_t
*)thread_this
->get(thread_this
);
91 return FCGX_GetStr(buf
, size
, this->req
.in
);
95 * fcgiwrap writef callback
97 static int writef_cb(void *null
, const char *format
, va_list args
)
99 private_request_t
*this = (private_request_t
*)thread_this
->get(thread_this
);
101 FCGX_VFPrintF(this->req
.out
, format
, args
);
105 * fcgiwrap write callback
107 static int write_cb(void *null
, const char *buf
, int size
)
109 private_request_t
*this = (private_request_t
*)thread_this
->get(thread_this
);
111 return FCGX_PutStr(buf
, size
, this->req
.out
);
115 * fcgiwrap getenv callback
117 static char *getenv_cb(void *null
, const char *key
)
120 private_request_t
*this = (private_request_t
*)thread_this
->get(thread_this
);
122 value
= FCGX_GetParam(key
, this->req
.envp
);
123 return strdupnull(value
);
127 * fcgiwrap getenv callback
129 static int putenv_cb(void *null
, const char *key
, const char *value
)
136 * fcgiwrap iterenv callback
138 static int iterenv_cb(void *null
, int num
, char **key
, char **value
)
142 private_request_t
*this = (private_request_t
*)thread_this
->get(thread_this
);
143 if (num
< this->req_env_len
)
147 eq
= strchr(this->req
.envp
[num
], '=');
150 *key
= strndup(this->req
.envp
[num
], eq
- this->req
.envp
[num
]);
151 *value
= strdup(eq
+ 1);
153 if (*key
== NULL
|| *value
== NULL
)
163 METHOD(request_t
, get_cookie
, char*,
164 private_request_t
*this, char *name
)
166 return hdf_get_valuef(this->hdf
, "Cookie.%s", name
);
169 METHOD(request_t
, get_path
, char*,
170 private_request_t
*this)
172 char * path
= FCGX_GetParam("PATH_INFO", this->req
.envp
);
173 return path ? path
: "";
176 METHOD(request_t
, get_host
, char*,
177 private_request_t
*this)
179 char *addr
= FCGX_GetParam("REMOTE_ADDR", this->req
.envp
);
180 return addr ? addr
: "";
183 METHOD(request_t
, get_user_agent
, char*,
184 private_request_t
*this)
186 char *agent
= FCGX_GetParam("HTTP_USER_AGENT", this->req
.envp
);
187 return agent ? agent
: "";
190 METHOD(request_t
, get_query_data
, char*,
191 private_request_t
*this, char *name
)
193 return hdf_get_valuef(this->hdf
, "Query.%s", name
);
196 METHOD(request_t
, get_env_var
, char*,
197 private_request_t
*this, char *name
)
199 return FCGX_GetParam(name
, this->req
.envp
);
202 METHOD(request_t
, read_data
, int,
203 private_request_t
*this, char *buf
, int len
)
205 return FCGX_GetStr(buf
, len
, this->req
.in
);
208 METHOD(request_t
, get_base
, char*,
209 private_request_t
*this)
211 return FCGX_GetParam("SCRIPT_NAME", this->req
.envp
);
214 METHOD(request_t
, add_cookie
, void,
215 private_request_t
*this, char *name
, char *value
)
217 thread_this
->set(thread_this
, this);
218 cgi_cookie_set(this->cgi
, name
, value
, NULL
, NULL
, NULL
, 0, 0);
221 METHOD(request_t
, redirect
, void,
222 private_request_t
*this, char *fmt
, ...)
226 FCGX_FPrintF(this->req
.out
, "Status: 303 See Other\n");
227 FCGX_FPrintF(this->req
.out
, "Location: %s%s", get_base(this),
228 *fmt
== '/' ?
"" : "/");
230 FCGX_VFPrintF(this->req
.out
, fmt
, args
);
232 FCGX_FPrintF(this->req
.out
, "\n\n");
235 METHOD(request_t
, get_referer
, char*,
236 private_request_t
*this)
238 return FCGX_GetParam("HTTP_REFERER", this->req
.envp
);
241 METHOD(request_t
, to_referer
, void,
242 private_request_t
*this)
246 referer
= get_referer(this);
249 FCGX_FPrintF(this->req
.out
, "Status: 303 See Other\n");
250 FCGX_FPrintF(this->req
.out
, "Location: %s\n\n", referer
);
258 METHOD(request_t
, session_closed
, bool,
259 private_request_t
*this)
264 METHOD(request_t
, close_session
, void,
265 private_request_t
*this)
270 METHOD(request_t
, serve
, void,
271 private_request_t
*this, char *headers
, chunk_t chunk
)
273 FCGX_FPrintF(this->req
.out
, "%s\n\n", headers
);
275 FCGX_PutStr(chunk
.ptr
, chunk
.len
, this->req
.out
);
278 METHOD(request_t
, render
, void,
279 private_request_t
*this, char *template)
283 thread_this
->set(thread_this
, this);
284 err
= cgi_display(this->cgi
, template);
287 cgi_neo_error(this->cgi
, err
);
292 METHOD(request_t
, streamf
, int,
293 private_request_t
*this, char *format
, ...)
298 va_start(args
, format
);
299 written
= FCGX_VFPrintF(this->req
.out
, format
, args
);
302 FCGX_FFlush(this->req
.out
) == -1)
309 METHOD(request_t
, set
, void,
310 private_request_t
*this, char *key
, char *value
)
312 hdf_set_value(this->hdf
, key
, value
);
315 METHOD(request_t
, setf
, void,
316 private_request_t
*this, char *format
, ...)
320 va_start(args
, format
);
321 hdf_set_valuevf(this->hdf
, format
, args
);
325 METHOD(request_t
, get_ref
, request_t
*,
326 private_request_t
*this)
329 return &this->public;
332 METHOD(request_t
, destroy
, void,
333 private_request_t
*this)
335 if (ref_put(&this->ref
))
337 thread_this
->set(thread_this
, this);
338 cgi_destroy(&this->cgi
);
339 FCGX_Finish_r(&this->req
);
345 * This initialization method is guaranteed to run only once
348 static void init(void)
350 cgiwrap_init_emu(NULL
, read_cb
, writef_cb
, write_cb
,
351 getenv_cb
, putenv_cb
, iterenv_cb
);
352 thread_this
= thread_value_create(NULL
);
358 request_t
*request_create(int fd
, bool debug
)
361 private_request_t
*this;
366 .get_path
= _get_path
,
367 .get_base
= _get_base
,
368 .get_host
= _get_host
,
369 .get_user_agent
= _get_user_agent
,
370 .add_cookie
= _add_cookie
,
371 .get_cookie
= _get_cookie
,
372 .get_query_data
= _get_query_data
,
373 .get_env_var
= _get_env_var
,
374 .read_data
= _read_data
,
375 .session_closed
= _session_closed
,
376 .close_session
= _close_session
,
377 .redirect
= _redirect
,
378 .get_referer
= _get_referer
,
379 .to_referer
= _to_referer
,
391 thread_cleanup_push(free
, this);
392 if (FCGX_InitRequest(&this->req
, fd
, 0) != 0 ||
393 FCGX_Accept_r(&this->req
) != 0)
397 thread_cleanup_pop(failed
);
403 pthread_once(&once
, init
);
404 thread_this
->set(thread_this
, this);
406 while (this->req
.envp
[this->req_env_len
] != NULL
)
411 err
= hdf_init(&this->hdf
);
414 hdf_set_value(this->hdf
, "base", get_base(this));
415 hdf_set_value(this->hdf
, "Config.NoCache", "true");
418 hdf_set_value(this->hdf
, "Config.TimeFooter", "0");
419 hdf_set_value(this->hdf
, "Config.CompressionEnabled", "1");
420 hdf_set_value(this->hdf
, "Config.WhiteSpaceStrip", "2");
423 err
= cgi_init(&this->cgi
, this->hdf
);
426 err
= cgi_parse(this->cgi
);
429 return &this->public;
431 cgi_destroy(&this->cgi
);
435 FCGX_Finish_r(&this->req
);