FcgiStatAccelWithMoreHttpHeaders » History » Revision 3

« Previous | Revision 3/8 (diff) | Next »
Anonymous, 2008-03-03 16:17
typo's fixed + some clarifications

Fobax and darix built a nice hack that implements non-blocking stat() system calls, which is very useful for lighty <1.5 .

However, where we previously used mod_expire to dump caching related directives in our http headers, we now have to do this manually.BR
Therefore we created this extended version of fcgi-stat-accel.

We do a stat() call to retrieve the needed information to generate Expires, max-age, Etag and Last-Modified headers. We therefore could also remove the fopen() and fclose() system calls. (I'm led to believe that stat() is better suited then fopen()+fclose() for a task like this anyway)BR
Some things are hardcoded ( eg only use ETAG_USE_SIZE not MTIME or INODE ) and the expiration time. Adapt if neededBR The code {{{
Originally written by Fobax.
Edited by darix to support controlling thread count at runtime.
Edited by poison and Dieter_be to support some http headers derived from the files. Please do not remove any of the above. compile with: $ gcc -lfcgi -lpthread fcgi-stat-accel.c -o fcgi-stat-accel fcgi-stat-accel will use the PHP_FCGI_CHILDREN environment variable to set the thread count. The default value, if spawned from lighttpd, is 20.

#include "fcgi_config.h"

#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "fcgiapp.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <stdlib.h>
#include <stdio.h>

#include "etag.h"
#include "buffer.h"

#define THREAD_COUNT 20

#define FORBIDDEN \
FCGX_FPrintF(stream, "Status: 403 Forbidden\r\nContent-Type: text/html\r\n\r\n<h1>403 Forbidden</h1>\n");
#define NOTFOUND \
FCGX_FPrintF(stream, "Status: 404 Not Found\r\nContent-Type: text/html\r\n\r\n<h1>404 Not Found</h1>\r\n%s", filename);
#define SENDFILE \
FCGX_FPrintF(stream, "%sX-LIGHTTPD-send-file: %s\r\n\r\n", headers, filename);

#define EXPIRATION_TIME (int) 60*60*24*30

int genheaders (char* mybuffer, size_t bufferlen, const char* file) {
char timebuf32; //possibly unsafe
char lastmodbuf32; //possibly unsafe
char etag128; //possibly unsafe
struct stat statbuf;
time_t exp;
time_t lastmod;
buffer *etag_raw;
buffer *etag_ok ;

//create buffers for Etag
etag_raw = buffer_init();
etag_ok = buffer_init();
// Stat the file
if (stat (file, &statbuf) != 0) {
return -1;
// Clear the buffer
memset (mybuffer, 0, bufferlen);
// Get the local time
exp = time (NULL) + EXPIRATION_TIME;
lastmod = statbuf.st_mtime;
strftime (timebuf, (sizeof (timebuf) / sizeof (char)) - 1, "%a, d %b %Y %H:%M:%S GMT", gmtime (x%x(exp)));
strftime (lastmodbuf, (sizeof (lastmodbuf) / sizeof (char)) - 1, "%a, d %b %Y %H:%M:%S GMT", gmtime (x%x(lastmod)));
etag_create(etag_raw, &statbuf, ETAG_USE_SIZE);
etag_mutate(etag_ok, etag_raw);
snprintf (mybuffer, bufferlen, "Cache-Control: max-age=%d\r\nETag: \%s\r\nExpires: %s\r\nLast-Modified: %s\r\n", EXPIRATION_TIME, etag_ok->ptr, timebuf , lastmodbuf);
return 0;

static void *doit(void *a){
FCGX_Request request;
int rc;
char *filename;
char extraheaders192;
int r;

FCGX_InitRequest(&request, 0, FCGI_FAIL_ACCEPT_ON_INTR);
//Some platforms require accept() serialization, some don't. The documentation claims it to be thread safe
// static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_lock(&accept_mutex);
rc = FCGX_Accept_r(&request);
// pthread_mutex_unlock(&accept_mutex);
if(rc < 0)
//get the filename
if((filename = FCGX_GetParam("SCRIPT_FILENAME", request.envp)) NULL){
//don't try to open directories
}else if(filename[strlen(filename)-1] '/'){
//open the file
}else if((r = genheaders (extraheaders, 191, filename)) != 0){
NOTFOUND(request.out, filename);
//no error, serve it
SENDFILE(request.out, filename, extraheaders);
return NULL;

int main(void){
int i,j,thread_count;
pthread_t* id;
char* env_val;

thread_count = THREAD_COUNT;
env_val = getenv("PHP_FCGI_CHILDREN");
if (env_val != NULL) {
j = atoi(env_val);
if (j != 0) {
thread_count = j;
id = malloc(sizeof(*id) * thread_count);
for (i = 0; i < thread_count; i++) {
pthread_create(&id[i], NULL, doit, NULL);
return 0;
Final words
Thanks for lighty, it is great ! BR
..and so is fcgi-stat-accel ! BR
If you have any remarks or questions, feel free to append to this page

Updated by Anonymous over 12 years ago · 3 revisions