a.js
b.js
c.js
合并为 ??a.js,b.js,c.js
减少http 请求,并且不影响 mod_expires, gzip 模块的功能
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "apr_buckets.h"
#include "apr_general.h"
#include "apr_lib.h"
#include "http_request.h"
struct m_fileinfo{
apr_file_t *file;
apr_off_t size;
};
/* The sample content handler */
static int mergerdownload_handler(request_rec *r)
{
char *get=NULL;
char *param=NULL;
char *tmp=NULL;
char *fname=NULL;
if (strcmp(r->handler, "mergerdownload")) {
return DECLINED;
}
//not allow sub request
if( r->main ){
return DECLINED;
}
if (!r->header_only){
//GET /css??a.css,b.css,c.css HTTP/1.1
//GET /js??a.js,b.js,c.js HTTP/1.1
//valid url
if( !(strlen(r->content_type) == 20 && strcmp(r->content_type, "httpd/unix-directory") == 0 &&
r->args != NULL && *(r->args) == '?') ){
return DECLINED;
}
get = r->the_request+5;
param = strstr(get, "??");
if( param != NULL ){
apr_finfo_t finfo ;
apr_file_t* file = NULL ;
apr_bucket *bucket = NULL;
apr_size_t fsize;
apr_bucket_brigade *fcs = apr_brigade_create(r->pool, r->connection->bucket_alloc);
apr_status_t status;
apr_time_t modified=0;
apr_time_t request_m=0;
char *request_time;
char *timestr;
char *arg=NULL;
param+=2;
tmp = param;
//only support .css .js
if( NULL != strstr(get, ".css") ){
r->content_type = "text/css";
}else if( NULL != strstr(get, ".js")){
r->content_type = "application/javascript";
}else{
return DECLINED;
}
int end = 0;
int filenumber = 0;
int alloc_file = 10;
struct m_fileinfo *fileinfo = malloc( sizeof(struct m_fileinfo) * alloc_file );
if( fileinfo == NULL ){
return DECLINED;
}
struct m_fileinfo *tmpfile;
struct m_fileinfo *refileinfo;
do{
//get first char ? address
if( *param == '?' && arg == NULL){
arg = param;
}
if( *param == ',' || *param == ' ' ){
fname = apr_pstrcat(r->pool, r->filename ,apr_pstrndup( r->pool, (const char *)tmp, (arg!=NULL?arg:param)-tmp ), NULL);
if( arg != NULL ){
arg = NULL;
}
if( *param == ' ' ){
end = 1;
}else{
tmp = param+1;
}
if ( (status = apr_stat(&finfo, fname, APR_FINFO_SIZE, r->pool)) == APR_SUCCESS && finfo.filetype != APR_DIR ) {
if ( apr_file_open(&file, fname, APR_READ|APR_SHARELOCK|APR_SENDFILE_ENABLED,
APR_OS_DEFAULT, r->pool ) == APR_SUCCESS ) {
if( file ){
++filenumber;
if( filenumber > alloc_file ){
alloc_file*=2;
refileinfo = realloc(fileinfo, sizeof(struct m_fileinfo)*alloc_file);
if( refileinfo == NULL ){
free(fileinfo);
return DECLINED;
}
fileinfo = refileinfo;
}
tmpfile = (fileinfo+(filenumber-1));
tmpfile->file = file;
tmpfile->size = finfo.size;
modified = finfo.mtime>modified?finfo.mtime:modified;
}
}
}
}
++param;
}while( end == 0 );
apr_pool_cleanup_register(r->pool, fileinfo, free, apr_pool_cleanup_null);
if( modified > 0 ){
request_time = apr_table_get(r->headers_in, "If-Modified-Since");
timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
apr_rfc822_date(timestr, modified);
if( request_time != NULL && strcmp(request_time, timestr) == 0 ){
return HTTP_NOT_MODIFIED;
}
int n = 0;
for(n; n<filenumber; n++){
tmpfile = (fileinfo+n);
APR_BRIGADE_INSERT_TAIL(fcs, apr_bucket_file_create(tmpfile->file, 0, tmpfile->size, r->pool,r->connection->bucket_alloc));
}
apr_table_setn(r->headers_out, "Last-Modified", timestr);
}
bucket = apr_bucket_eos_create(r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(fcs, bucket);
return ap_pass_brigade(r->output_filters, fcs);
}else{
return DECLINED;
}
}
return OK;
}
static void mergerdownload_register_hooks(apr_pool_t *p)
{
ap_hook_handler(mergerdownload_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA mergerdownload_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
mergerdownload_register_hooks /* register hooks */
};