FcgiStatAccelWithMoreHttpHeaders » History » Revision 5
Revision 4 (Anonymous, 2008-03-03 17:07) → Revision 5/8 (Anonymous, 2008-03-03 17:07)
h2. == Intro == Fobax and darix built a nice hack that implements non-blocking stat() system calls, which is very useful for lighty <1.5 . http://trac.lighttpd.net/trac/wiki/HowtoSpeedUpStatWithFastcgi However, where we previously used mod_expire to dump caching related directives in our http headers, we now have to do this manually. manually.[[BR]] Therefore we created this extended version of fcgi-stat-accel. h2. == Changes == 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) Currently we didn't implement checking whether the file is readable or not, as we don't need this, maybe you do .. [[BR]] Some things are hardcoded ( eg only use ETAG_USE_SIZE not MTIME or INODE ) and the expiration time. Adapt if needed h2. needed[[BR]] == The code <pre> == {{{ #!c /* 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(stream) \ FCGX_FPrintF(stream, "Status: 403 Forbidden\r\nContent-Type: text/html\r\n\r\n<h1>403 Forbidden</h1>\n"); #define NOTFOUND(stream, filename) \ 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(stream, filename, headers) \ 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 timebuf[32]; //possibly unsafe char lastmodbuf[32]; //possibly unsafe char etag[128]; //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 (&(exp))); strftime (lastmodbuf, (sizeof (lastmodbuf) / sizeof (char)) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&(lastmod))); etag_create(etag_raw, &statbuf, ETAG_USE_SIZE); etag_mutate(etag_ok, etag_raw); buffer_free(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); buffer_free(etag_ok); return 0; } static void *doit(void *a){ FCGX_Request request; int rc; char *filename; char extraheaders[192]; int r; FCGX_InitRequest(&request, 0, FCGI_FAIL_ACCEPT_ON_INTR); while(1){ //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) break; //get the filename if((filename = FCGX_GetParam("SCRIPT_FILENAME", request.envp)) == NULL){ FORBIDDEN(request.out); //don't try to open directories }else if(filename[strlen(filename)-1] == '/'){ FORBIDDEN(request.out); //open the file }else if((r = genheaders (extraheaders, 191, filename)) != 0){ NOTFOUND(request.out, filename); //no error, serve it }else{ SENDFILE(request.out, filename, extraheaders); } FCGX_Finish_r(&request); } return NULL; } int main(void){ int i,j,thread_count; pthread_t* id; char* env_val; FCGX_Init(); 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); } doit(NULL); free(id); return 0; } </pre> h2. }}} == 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