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
16 #include "fast_dispatcher.h"
18 #include "fast_request.h"
19 #include "fast_session.h"
26 #include <utils/debug.h>
27 #include <threading/thread.h>
28 #include <threading/condvar.h>
29 #include <threading/mutex.h>
30 #include <collections/linked_list.h>
31 #include <collections/hashtable.h>
33 /** Intervall to check for expired sessions, in seconds */
34 #define CLEANUP_INTERVAL 30
36 typedef struct private_fast_dispatcher_t private_fast_dispatcher_t
;
39 * private data of the task manager
41 struct private_fast_dispatcher_t
{
46 fast_dispatcher_t
public;
59 * number of threads in "threads"
64 * session locking mutex
69 * Hahstable with active sessions
71 hashtable_t
*sessions
;
79 * timestamp of last session cleanup round
84 * running in debug mode?
89 * List of controllers controller_constructor_t
91 linked_list_t
*controllers
;
94 * List of filters filter_constructor_t
96 linked_list_t
*filters
;
99 * constructor function to create session context (in controller_entry_t)
101 fast_context_constructor_t context_constructor
;
104 * user param to context constructor
110 /** constructor function */
111 fast_controller_constructor_t constructor
;
112 /** parameter to constructor */
114 } controller_entry_t
;
117 /** constructor function */
118 fast_filter_constructor_t constructor
;
119 /** parameter to constructor */
124 /** session instance */
125 fast_session_t
*session
;
126 /** condvar to wait for session */
128 /** client host address, to prevent session hijacking */
130 /** TRUE if session is in use */
132 /** last use of the session */
134 /** has the session been closed by the handler? */
139 * create a session and instanciate controllers
141 static fast_session_t
* load_session(private_fast_dispatcher_t
*this)
143 enumerator_t
*enumerator
;
144 controller_entry_t
*centry
;
145 filter_entry_t
*fentry
;
146 fast_session_t
*session
;
147 fast_context_t
*context
= NULL
;
148 fast_controller_t
*controller
;
149 fast_filter_t
*filter
;
151 if (this->context_constructor
)
153 context
= this->context_constructor(this->param
);
155 session
= fast_session_create(context
);
161 enumerator
= this->controllers
->create_enumerator(this->controllers
);
162 while (enumerator
->enumerate(enumerator
, ¢ry
))
164 controller
= centry
->constructor(context
, centry
->param
);
165 session
->add_controller(session
, controller
);
167 enumerator
->destroy(enumerator
);
169 enumerator
= this->filters
->create_enumerator(this->filters
);
170 while (enumerator
->enumerate(enumerator
, &fentry
))
172 filter
= fentry
->constructor(context
, fentry
->param
);
173 session
->add_filter(session
, filter
);
175 enumerator
->destroy(enumerator
);
181 * create a new session entry
183 static session_entry_t
*session_entry_create(private_fast_dispatcher_t
*this,
186 session_entry_t
*entry
;
187 fast_session_t
*session
;
189 session
= load_session(this);
195 .cond
= condvar_create(CONDVAR_TYPE_DEFAULT
),
197 .host
= strdup(host
),
198 .used
= time_monotonic(NULL
),
206 static void session_entry_destroy(session_entry_t
*entry
)
208 entry
->session
->destroy(entry
->session
);
209 entry
->cond
->destroy(entry
->cond
);
214 METHOD(fast_dispatcher_t
, add_controller
, void,
215 private_fast_dispatcher_t
*this, fast_controller_constructor_t constructor
,
218 controller_entry_t
*entry
;
221 .constructor
= constructor
,
224 this->controllers
->insert_last(this->controllers
, entry
);
227 METHOD(fast_dispatcher_t
, add_filter
, void,
228 private_fast_dispatcher_t
*this, fast_filter_constructor_t constructor
,
231 filter_entry_t
*entry
;
234 .constructor
= constructor
,
237 this->filters
->insert_last(this->filters
, entry
);
241 * Hashtable hash function
243 static u_int
session_hash(char *sid
)
245 return chunk_hash(chunk_create(sid
, strlen(sid
)));
249 * Hashtable equals function
251 static bool session_equals(char *sid1
, char *sid2
)
253 return streq(sid1
, sid2
);
257 * Cleanup unused sessions
259 static void cleanup_sessions(private_fast_dispatcher_t
*this, time_t now
)
261 if (this->last_cleanup
< now
- CLEANUP_INTERVAL
)
264 session_entry_t
*entry
;
265 enumerator_t
*enumerator
;
266 linked_list_t
*remove
;
268 this->last_cleanup
= now
;
269 remove
= linked_list_create();
270 enumerator
= this->sessions
->create_enumerator(this->sessions
);
271 while (enumerator
->enumerate(enumerator
, &sid
, &entry
))
273 /* check all sessions for timeout or close flag */
274 if (!entry
->in_use
&&
275 (entry
->used
< now
- this->timeout
|| entry
->closed
))
277 remove
->insert_last(remove
, sid
);
280 enumerator
->destroy(enumerator
);
282 while (remove
->remove_last(remove
, (void**)&sid
) == SUCCESS
)
284 entry
= this->sessions
->remove(this->sessions
, sid
);
287 session_entry_destroy(entry
);
290 remove
->destroy(remove
);
295 * Actual dispatching code
297 static void dispatch(private_fast_dispatcher_t
*this)
299 thread_cancelability(FALSE
);
303 fast_request_t
*request
;
304 session_entry_t
*found
= NULL
;
308 thread_cancelability(TRUE
);
309 request
= fast_request_create(this->fd
, this->debug
);
310 thread_cancelability(FALSE
);
316 now
= time_monotonic(NULL
);
317 sid
= request
->get_cookie(request
, "SID");
319 this->mutex
->lock(this->mutex
);
322 found
= this->sessions
->get(this->sessions
, sid
);
324 if (found
&& !streq(found
->host
, request
->get_host(request
)))
330 /* wait until session is unused */
331 while (found
->in_use
)
333 found
->cond
->wait(found
->cond
, this->mutex
);
337 { /* create a new session if not found */
338 found
= session_entry_create(this, request
->get_host(request
));
341 request
->destroy(request
);
342 this->mutex
->unlock(this->mutex
);
345 sid
= found
->session
->get_sid(found
->session
);
346 this->sessions
->put(this->sessions
, sid
, found
);
348 found
->in_use
= TRUE
;
349 this->mutex
->unlock(this->mutex
);
351 /* start processing */
352 found
->session
->process(found
->session
, request
);
353 found
->used
= time_monotonic(NULL
);
355 /* release session */
356 this->mutex
->lock(this->mutex
);
357 found
->in_use
= FALSE
;
358 found
->closed
= request
->session_closed(request
);
359 found
->cond
->signal(found
->cond
);
360 cleanup_sessions(this, now
);
361 this->mutex
->unlock(this->mutex
);
363 request
->destroy(request
);
367 METHOD(fast_dispatcher_t
, run
, void,
368 private_fast_dispatcher_t
*this, int threads
)
370 this->thread_count
= threads
;
371 this->threads
= malloc(sizeof(thread_t
*) * threads
);
374 this->threads
[threads
- 1] = thread_create((thread_main_t
)dispatch
,
376 if (this->threads
[threads
- 1])
383 METHOD(fast_dispatcher_t
, waitsignal
, void,
384 private_fast_dispatcher_t
*this)
389 sigaddset(&set
, SIGINT
);
390 sigaddset(&set
, SIGTERM
);
391 sigaddset(&set
, SIGHUP
);
392 sigprocmask(SIG_BLOCK
, &set
, NULL
);
393 while (sigwaitinfo(&set
, NULL
) == -1 && errno
== EINTR
)
395 /* wait for signal */
399 METHOD(fast_dispatcher_t
, destroy
, void,
400 private_fast_dispatcher_t
*this)
403 session_entry_t
*entry
;
404 enumerator_t
*enumerator
;
406 FCGX_ShutdownPending();
407 while (this->thread_count
--)
409 thread_t
*thread
= this->threads
[this->thread_count
];
410 thread
->cancel(thread
);
411 thread
->join(thread
);
413 enumerator
= this->sessions
->create_enumerator(this->sessions
);
414 while (enumerator
->enumerate(enumerator
, &sid
, &entry
))
416 session_entry_destroy(entry
);
418 enumerator
->destroy(enumerator
);
419 this->sessions
->destroy(this->sessions
);
420 this->controllers
->destroy_function(this->controllers
, free
);
421 this->filters
->destroy_function(this->filters
, free
);
422 this->mutex
->destroy(this->mutex
);
430 fast_dispatcher_t
*fast_dispatcher_create(char *socket
, bool debug
, int timeout
,
431 fast_context_constructor_t constructor
, void *param
)
433 private_fast_dispatcher_t
*this;
437 .add_controller
= _add_controller
,
438 .add_filter
= _add_filter
,
440 .waitsignal
= _waitsignal
,
443 .sessions
= hashtable_create((void*)session_hash
,
444 (void*)session_equals
, 4096),
445 .controllers
= linked_list_create(),
446 .filters
= linked_list_create(),
447 .context_constructor
= constructor
,
448 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
451 .last_cleanup
= time_monotonic(NULL
),
460 this->fd
= FCGX_OpenSocket(socket
, 10);
462 return &this->public;