├── config ├── README.markdown └── ngx_http_static_etags_module.c /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_static_etags_module 2 | HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_static_etags_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_static_etags_module.c" -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Nginx Static Etags 2 | ------------------ 3 | 4 | Nginx doesn't generate etags for static content. I think it should. If I can remember enough C from college to make it work as a module, I will. 5 | 6 | ### Installation 7 | 8 | Download the module however you like. I'd recommend pulling it down with Git by simply cloning this repository: 9 | 10 | mkdir ~/src 11 | cd ~/src 12 | git clone git://github.com/mikewest/nginx-static-etags.git ./nginx-static-etags 13 | 14 | To use the module, you'll have to compile it into Nginx. So, download the Nginx source, configure it with the module path, and compile: 15 | 16 | mkdir ~/src 17 | cd ~/src 18 | curl -O http://sysoev.ru/nginx/nginx-0.6.32.tar.gz 19 | tar -zxvf ./nginx-0.6.32.tar.gz 20 | cd ./nginx-0.6.32 21 | ./configure --add-module=/Users/mikewest/Repositories/nginx-static-etags 22 | make 23 | sudo make install 24 | 25 | And you're done! 26 | 27 | ### Configuration 28 | 29 | Add `FileEtag` to the relevant `location` blocks in your `nginx.conf` file: 30 | 31 | location / { 32 | ... 33 | FileETag on; 34 | ... 35 | } 36 | 37 | It's currently an on/off toggle. The plan is to bring it to feature parity with [the Apache configuration option][apache]. It's really not there yet. 38 | 39 | [apache]: http://httpd.apache.org/docs/1.3/mod/core.html#fileetag -------------------------------------------------------------------------------- /ngx_http_static_etags_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Mike West ( http://mikewest.org/ ) 3 | * 4 | * The following is released under the Creative Commons BSD license, 5 | * available for your perusal at `http://creativecommons.org/licenses/BSD/` 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * Two configuration elements: `enable_etags` and `etag_format`, specified in 14 | * the `Location` block. 15 | */ 16 | typedef struct { 17 | ngx_uint_t FileETag; 18 | ngx_str_t etag_format; 19 | } ngx_http_static_etags_loc_conf_t; 20 | 21 | static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 22 | /*static ngx_http_output_body_filter_pt ngx_http_next_body_filter;*/ 23 | 24 | static void * ngx_http_static_etags_create_loc_conf(ngx_conf_t *cf); 25 | static char * ngx_http_static_etags_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); 26 | static ngx_int_t ngx_http_static_etags_init(ngx_conf_t *cf); 27 | static ngx_int_t ngx_http_static_etags_header_filter(ngx_http_request_t *r); 28 | 29 | static ngx_command_t ngx_http_static_etags_commands[] = { 30 | { ngx_string( "FileETag" ), 31 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 32 | ngx_conf_set_flag_slot, 33 | NGX_HTTP_LOC_CONF_OFFSET, 34 | offsetof( ngx_http_static_etags_loc_conf_t, FileETag ), 35 | NULL }, 36 | 37 | { ngx_string( "etag_format" ), 38 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 39 | ngx_conf_set_str_slot, 40 | NGX_HTTP_LOC_CONF_OFFSET, 41 | offsetof( ngx_http_static_etags_loc_conf_t, etag_format ), 42 | NULL }, 43 | 44 | ngx_null_command 45 | }; 46 | 47 | static ngx_http_module_t ngx_http_static_etags_module_ctx = { 48 | NULL, /* preconfiguration */ 49 | ngx_http_static_etags_init, /* postconfiguration */ 50 | 51 | NULL, /* create main configuration */ 52 | NULL, /* init main configuration */ 53 | 54 | NULL, /* create server configuration */ 55 | NULL, /* merge server configuration */ 56 | 57 | ngx_http_static_etags_create_loc_conf, /* create location configuration */ 58 | ngx_http_static_etags_merge_loc_conf, /* merge location configuration */ 59 | }; 60 | 61 | ngx_module_t ngx_http_static_etags_module = { 62 | NGX_MODULE_V1, 63 | &ngx_http_static_etags_module_ctx, /* module context */ 64 | ngx_http_static_etags_commands, /* module directives */ 65 | NGX_HTTP_MODULE, /* module type */ 66 | NULL, /* init master */ 67 | NULL, /* init module */ 68 | NULL, /* init process */ 69 | NULL, /* init thread */ 70 | NULL, /* exit thread */ 71 | NULL, /* exit process */ 72 | NULL, /* exit master */ 73 | NGX_MODULE_V1_PADDING 74 | }; 75 | 76 | 77 | static void * ngx_http_static_etags_create_loc_conf(ngx_conf_t *cf) { 78 | ngx_http_static_etags_loc_conf_t *conf; 79 | 80 | conf = ngx_pcalloc( cf->pool, sizeof( ngx_http_static_etags_loc_conf_t ) ); 81 | if ( NULL == conf ) { 82 | return NGX_CONF_ERROR; 83 | } 84 | conf->FileETag = NGX_CONF_UNSET_UINT; 85 | return conf; 86 | } 87 | 88 | static char * ngx_http_static_etags_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { 89 | ngx_http_static_etags_loc_conf_t *prev = parent; 90 | ngx_http_static_etags_loc_conf_t *conf = child; 91 | 92 | ngx_conf_merge_uint_value( conf->FileETag, prev->FileETag, 0 ); 93 | ngx_conf_merge_str_value( conf->etag_format, prev->etag_format, "%s_%X_%X" ); 94 | 95 | if ( conf->FileETag != 0 && conf->FileETag != 1 ) { 96 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 97 | "FileETag must be 'on' or 'off'"); 98 | return NGX_CONF_ERROR; 99 | } 100 | 101 | return NGX_CONF_OK; 102 | } 103 | 104 | static ngx_int_t ngx_http_static_etags_init(ngx_conf_t *cf) { 105 | ngx_http_next_header_filter = ngx_http_top_header_filter; 106 | ngx_http_top_header_filter = ngx_http_static_etags_header_filter; 107 | 108 | return NGX_OK; 109 | } 110 | 111 | static ngx_int_t ngx_http_static_etags_header_filter(ngx_http_request_t *r) { 112 | int status; 113 | ngx_log_t *log; 114 | u_char *p; 115 | size_t root; 116 | ngx_str_t path; 117 | ngx_http_static_etags_loc_conf_t *loc_conf; 118 | struct stat stat_result; 119 | char *str_buffer; 120 | int str_len; 121 | 122 | log = r->connection->log; 123 | 124 | loc_conf = ngx_http_get_module_loc_conf( r, ngx_http_static_etags_module ); 125 | 126 | // Is the module active? 127 | if ( 1 == loc_conf->FileETag ) { 128 | p = ngx_http_map_uri_to_path( r, &path, &root, 0 ); 129 | if ( NULL == p ) { 130 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 131 | } 132 | 133 | 134 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, 135 | "http filename: \"%s\"", path.data); 136 | 137 | status = stat( (char *) path.data, &stat_result ); 138 | 139 | // Did the `stat` succeed? 140 | if ( 0 == status) { 141 | str_len = 1000; 142 | str_buffer = malloc( str_len + sizeof(char) ); 143 | sprintf( str_buffer, (char *) loc_conf->etag_format.data, r->uri.data, (unsigned int) stat_result.st_size, (unsigned int) stat_result.st_mtime ); 144 | 145 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, 146 | "stat returned: \"%d\"", status); 147 | 148 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, 149 | "st_size: '%d'", stat_result.st_size); 150 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, 151 | "st_mtime: '%d'", stat_result.st_mtime); 152 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, 153 | "Concatted: '%s'", str_buffer ); 154 | 155 | r->headers_out.etag = ngx_list_push(&r->headers_out.headers); 156 | if (r->headers_out.etag == NULL) { 157 | return NGX_ERROR; 158 | } 159 | r->headers_out.etag->hash = 1; 160 | r->headers_out.etag->key.len = sizeof("Etag") - 1; 161 | r->headers_out.etag->key.data = (u_char *) "Etag"; 162 | r->headers_out.etag->value.len = strlen( str_buffer ); 163 | r->headers_out.etag->value.data = (u_char *) str_buffer; 164 | } 165 | } 166 | 167 | return ngx_http_next_header_filter(r); 168 | } --------------------------------------------------------------------------------