[Patch] type{} directive
Michael Lustfield
mtecknology at ubuntu.com
Wed Aug 18 18:26:00 MSD 2010
That looks like an amazing feature. I'm excited to see and use it.
On Wed, Aug 18, 2010 at 9:19 AM, Grzegorz Nosek
<grzegorz.nosek at gmail.com> wrote:
> Hi all,
>
> After a recent discussion I decided to whip up a patch implementing the
> type {} directive to route requests according to MIME types of requested
> files.
>
> The main advantage is a clearer config file, as instead of:
>
> | location ~ \.(gif|png|jpe?g|tif|ico) {
> | expires max;
> | access_log off;
> | }
>
> you can now say:
>
> | type image/* {
> | expires max;
> | access_log off;
> | }
>
> (foo/* is the only wildcard type available right now, so e.g. text/*ml
> or application/vnd.* won't work).
>
> A particularly nice aspect of this is that type{}s are looked up
> independently from location{}s, so the config below sets "expires max"
> both on /foo.txt and on /foo/foo.txt:
>
> | location / {
> | allow all;
> | }
> |
> | location /foo {
> | allow 127.0.0.1;
> | deny all;
> | }
> |
> | type text/plain {
> | expires max;
> | }
>
> The catch is that it only works on static files, as processed by the
> ngx_http_static_module, not on e.g. proxied responses (sorry Mike!)
>
> There's another use case, but Igor said he doesn't like it, so pretend
> you haven't seen it ;)
>
> | types {
> | application/x-httpd-php php;
> | }
> |
> | location / {
> | # all the usual stuff
> | }
> |
> | type application/x-httpd-php {
> | fastcgi_pass ... # etc.
> | }
>
> The above config (inside a server{} block, of course) pushes all .php
> files via FastCGI (to php-fpm or something). This happens very late in
> the request processing stage, just before the raw php file would have
> been served to the client.
>
> The patch is based on 0.8.49. I'm sure it has flaws but all comments are
> gladly accepted (both for high-level feature discussion and low-level
> code review).
>
> Best regards,
> Grzegorz Nosek
>
> ---
> src/http/modules/ngx_http_static_module.c | 30 +++-
> src/http/ngx_http_core_module.c | 351 ++++++++++++++++++++++++++---
> src/http/ngx_http_core_module.h | 10 +
> 3 files changed, 363 insertions(+), 28 deletions(-)
>
> diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c
> index 57b5130..b30ce8b 100644
> --- a/src/http/modules/ngx_http_static_module.c
> +++ b/src/http/modules/ngx_http_static_module.c
> @@ -49,14 +49,15 @@ ngx_http_static_handler(ngx_http_request_t *r)
> {
> u_char *last, *location;
> size_t root, len;
> - ngx_str_t path;
> + ngx_str_t path, content_type;
> ngx_int_t rc;
> ngx_uint_t level;
> ngx_log_t *log;
> ngx_buf_t *b;
> ngx_chain_t out;
> ngx_open_file_info_t of;
> - ngx_http_core_loc_conf_t *clcf;
> + ngx_http_core_loc_conf_t *clcf, *sclcf, *type_clcf;
> + ngx_http_core_srv_conf_t *cscf;
>
> if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
> return NGX_HTTP_NOT_ALLOWED;
> @@ -193,6 +194,31 @@ ngx_http_static_handler(ngx_http_request_t *r)
>
> #endif
>
> + content_type.data = NULL;
> + content_type.len = 0;
> + if (!clcf->type_loc) {
> + /*
> + * try to find a type { } block matching the content type
> + * as determined by file extension
> + */
> + rc = ngx_http_find_content_type(r, &content_type);
> + if (rc == NGX_OK && content_type.data) {
> + /* look in type { } blocks in location { } */
> + type_clcf = ngx_http_core_find_type_location(clcf, &content_type, 0);
> + if (type_clcf) {
> + return ngx_http_use_location(r, type_clcf);
> + }
> +
> + /* look in type { } blocks in server { } */
> + cscf = r->srv_conf[ngx_http_core_module.ctx_index];
> + sclcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
> + type_clcf = ngx_http_core_find_type_location(sclcf, &content_type, 0);
> + if (type_clcf) {
> + return ngx_http_use_location(r, type_clcf);
> + }
> + }
> + }
> +
> if (r->method & NGX_HTTP_POST) {
> return NGX_HTTP_NOT_ALLOWED;
> }
> diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
> index 6a11a87..9fb54fb 100644
> --- a/src/http/ngx_http_core_module.c
> +++ b/src/http/ngx_http_core_module.c
> @@ -15,6 +15,13 @@ typedef struct {
> } ngx_http_method_name_t;
>
>
> +#define DEFAULT_NUM_TYPES 4
> +typedef struct {
> + ngx_str_t mime_type;
> + ngx_http_core_loc_conf_t *clcf;
> +} ngx_http_type_conf_t;
> +
> +
> #define NGX_HTTP_REQUEST_BODY_FILE_OFF 0
> #define NGX_HTTP_REQUEST_BODY_FILE_ON 1
> #define NGX_HTTP_REQUEST_BODY_FILE_CLEAN 2
> @@ -77,6 +84,9 @@ static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
> static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);
> static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);
>
> +static char *ngx_http_type(ngx_conf_t *cf, ngx_command_t *cmd,
> + void *conf);
> +
> static ngx_conf_post_t ngx_http_core_lowat_post =
> { ngx_http_core_lowat_check };
>
> @@ -724,6 +734,13 @@ static ngx_command_t ngx_http_core_commands[] = {
>
> #endif
>
> + { ngx_string("type"),
> + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
> + ngx_http_type,
> + NGX_HTTP_LOC_CONF_OFFSET,
> + 0,
> + NULL },
> +
> ngx_null_command
> };
>
> @@ -1657,17 +1674,13 @@ ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)
>
>
> ngx_int_t
> -ngx_http_set_content_type(ngx_http_request_t *r)
> +ngx_http_find_content_type(ngx_http_request_t *r, ngx_str_t *typeptr)
> {
> u_char c, *exten;
> ngx_str_t *type;
> ngx_uint_t i, hash;
> ngx_http_core_loc_conf_t *clcf;
>
> - if (r->headers_out.content_type.len) {
> - return NGX_OK;
> - }
> -
> clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
>
> if (r->exten.len) {
> @@ -1698,15 +1711,35 @@ ngx_http_set_content_type(ngx_http_request_t *r)
> r->exten.data, r->exten.len);
>
> if (type) {
> - r->headers_out.content_type_len = type->len;
> - r->headers_out.content_type = *type;
> + *typeptr = *type;
>
> return NGX_OK;
> }
> }
>
> - r->headers_out.content_type_len = clcf->default_type.len;
> - r->headers_out.content_type = clcf->default_type;
> + *typeptr = clcf->default_type;
> +
> + return NGX_OK;
> +}
> +
> +
> +ngx_int_t
> +ngx_http_set_content_type(ngx_http_request_t *r)
> +{
> + ngx_str_t content_type;
> + ngx_int_t rv;
> +
> + if (r->headers_out.content_type.len) {
> + return NGX_OK;
> + }
> +
> + rv = ngx_http_find_content_type(r, &content_type);
> +
> + if (rv != NGX_OK)
> + return rv;
> +
> + r->headers_out.content_type_len = content_type.len;
> + r->headers_out.content_type = content_type;
>
> return NGX_OK;
> }
> @@ -2332,14 +2365,11 @@ ngx_http_internal_redirect(ngx_http_request_t *r,
> }
>
>
> -ngx_int_t
> -ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)
> +ngx_http_core_loc_conf_t *
> +ngx_http_find_named_location(ngx_http_request_t *r, ngx_str_t *name)
> {
> ngx_http_core_srv_conf_t *cscf;
> ngx_http_core_loc_conf_t **clcfp;
> - ngx_http_core_main_conf_t *cmcf;
> -
> - r->main->count++;
>
> cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
>
> @@ -2356,24 +2386,48 @@ ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)
> continue;
> }
>
> - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
> - "using location: %V \"%V?%V\"",
> - name, &r->uri, &r->args);
> + return *clcfp;
> + }
> + }
> +
> + return NULL;
> +}
> +
>
> - r->internal = 1;
> - r->content_handler = NULL;
> - r->loc_conf = (*clcfp)->loc_conf;
> +ngx_int_t
> +ngx_http_use_location(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf)
> +{
> + ngx_http_core_main_conf_t *cmcf;
>
> - ngx_http_update_location_config(r);
> + r->main->count++;
>
> - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
> + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
> + "using location: %V \"%V?%V\"",
> + name, &r->uri, &r->args);
>
> - r->phase_handler = cmcf->phase_engine.location_rewrite_index;
> + r->internal = 1;
> + r->content_handler = NULL;
> + r->loc_conf = clcf->loc_conf;
>
> - ngx_http_core_run_phases(r);
> + ngx_http_update_location_config(r);
>
> - return NGX_DONE;
> - }
> + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
> +
> + r->phase_handler = cmcf->phase_engine.location_rewrite_index;
> +
> + ngx_http_core_run_phases(r);
> +
> + return NGX_DONE;
> +}
> +
> +ngx_int_t
> +ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)
> +{
> + ngx_http_core_loc_conf_t *clcf;
> +
> + clcf = ngx_http_find_named_location(r, name);
> + if (clcf) {
> + return ngx_http_use_location(r, clcf);
> }
>
> ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> @@ -3375,6 +3429,9 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
>
> #endif
>
> + ngx_conf_merge_ptr_value(conf->type_locations,
> + prev->type_locations, NULL);
> +
> return NGX_CONF_OK;
> }
>
> @@ -4479,3 +4536,245 @@ ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)
>
> return NGX_CONF_OK;
> }
> +
> +
> +static char *
> +ngx_http_add_type_location(ngx_conf_t *cf, ngx_str_t *mime_type,
> + ngx_http_core_loc_conf_t *pclcf, ngx_http_core_loc_conf_t *clcf)
> +{
> + ngx_http_type_conf_t *tcf;
> +
> + if (!pclcf->type_locations) {
> + pclcf->type_locations = ngx_list_create(cf->pool, DEFAULT_NUM_TYPES,
> + sizeof(ngx_http_type_conf_t));
> +
> + if (!pclcf->type_locations) {
> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> + "failed to allocate memory for mime type list");
> + return NGX_CONF_ERROR;
> + }
> + }
> +
> + tcf = ngx_list_push(pclcf->type_locations);
> + if (!tcf) {
> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> + "failed to allocate memory for mime type data");
> + return NGX_CONF_ERROR;
> + }
> +
> + tcf->mime_type = *mime_type;
> + tcf->clcf = clcf;
> +
> + return NGX_CONF_OK;
> +}
> +
> +
> +static inline ngx_int_t
> +ngx_str_equal(ngx_str_t *a, ngx_str_t *b)
> +{
> + if (a->len != b->len) {
> + return 0;
> + }
> +
> + return !ngx_strncmp(a->data, b->data, a->len);
> +}
> +
> +
> +static inline ngx_int_t
> +ngx_mimetype_equal(ngx_str_t *a, ngx_str_t *b)
> +{
> + size_t pos;
> + const u_char *p;
> +
> + p = memchr(a->data, '/', a->len);
> + if (!p)
> + return 0;
> +
> + pos = p - a->data;
> +
> + if (pos >= b->len || pos == a->len || b->data[pos] != '/') {
> + return 0;
> + }
> +
> + if ((char)a->data[pos + 1] == '*') {
> + return !memcmp(a->data, b->data, pos);
> + }
> +
> + return ngx_str_equal(a, b);
> +}
> +
> +
> +static inline ngx_int_t
> +ngx_is_mimetype_valid(ngx_str_t *type)
> +{
> + size_t pos;
> + const u_char *p;
> +
> + p = memchr(type->data, '/', type->len);
> + if (!p) {
> + return 0;
> + }
> + pos = p - type->data;
> + if (pos == 0 || pos == type->len - 1) {
> + return 0;
> + }
> +
> + if (pos == type->len - 2 && p[1] == '*') {
> + /* a lone asterisk is allowed after the slash... */
> + return 1;
> + }
> +
> + if (memchr(p+1, '/', type->len - pos - 2) != NULL)
> + return 0;
> +
> + /* ... but nowhere else */
> + p = memchr(type->data, '*', type->len);
> + return (p == NULL);
> +}
> +
> +
> +ngx_http_core_loc_conf_t *
> +ngx_http_core_find_type_location(ngx_http_core_loc_conf_t *pclcf,
> + ngx_str_t *mime_type, ngx_int_t exact)
> +{
> + ngx_list_part_t *part;
> + ngx_http_type_conf_t *data;
> + ngx_http_core_loc_conf_t *clcf;
> + ngx_uint_t i;
> +
> + if (!pclcf->type_locations) {
> + return NULL;
> + }
> +
> + part = &pclcf->type_locations->part;
> + data = part->elts;
> + clcf = NULL;
> +
> + for (i = 0 ;; i++) {
> + if (i >= part->nelts) {
> + if (part->next == NULL) {
> + break;
> + }
> +
> + part = part->next;
> + data = part->elts;
> + i = 0;
> + }
> +
> + if (exact) {
> + if (ngx_str_equal(&data[i].mime_type, mime_type)) {
> + clcf = data[i].clcf;
> + break;
> + }
> + } else {
> + if (ngx_mimetype_equal(&data[i].mime_type, mime_type)) {
> + clcf = data[i].clcf;
> + break;
> + }
> + }
> + }
> +
> + return clcf;
> +}
> +
> +static char *
> +ngx_http_type(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> +{
> + void *mconf;
> + char *rv;
> + ngx_str_t *value, mime_type;
> + ngx_uint_t i, ctx_index;
> + ngx_conf_t save;
> + ngx_http_module_t *module;
> + ngx_http_conf_ctx_t *ctx, *pctx;
> + ngx_http_core_loc_conf_t *clcf, *pclcf;
> +
> + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
> + if (ctx == NULL) {
> + return NGX_CONF_ERROR;
> + }
> +
> + value = cf->args->elts;
> + mime_type = value[1];
> +
> + if (!ngx_is_mimetype_valid(&mime_type)) {
> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> + "invalid mime type %V", &mime_type);
> + return NGX_CONF_ERROR;
> + }
> +
> + pctx = cf->ctx;
> + pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
> +
> + if (ngx_http_core_find_type_location(pclcf, &mime_type, 1)) {
> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> + "duplicate mime type %V", &mime_type);
> + return NGX_CONF_ERROR;
> + }
> +
> + ctx->main_conf = pctx->main_conf;
> + ctx->srv_conf = pctx->srv_conf;
> +
> + ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
> + if (ctx->loc_conf == NULL) {
> + return NGX_CONF_ERROR;
> + }
> +
> + for (i = 0; ngx_modules[i]; i++) {
> + if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
> + continue;
> + }
> +
> + module = ngx_modules[i]->ctx;
> +
> + if (module->create_loc_conf) {
> +
> + mconf = module->create_loc_conf(cf);
> + if (mconf == NULL) {
> + return NGX_CONF_ERROR;
> + }
> +
> + ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
> + }
> + }
> +
> + clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
> + clcf->loc_conf = ctx->loc_conf;
> + clcf->name = mime_type;
> + clcf->noname = 1;
> + clcf->type_loc = 1;
> +
> + save = *cf;
> + cf->ctx = ctx;
> + cf->cmd_type = NGX_HTTP_LOC_CONF;
> +
> + rv = ngx_conf_parse(cf, NULL);
> +
> + for (i = 0; ngx_modules[i]; i++) {
> + if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
> + continue;
> + }
> +
> + module = ngx_modules[i]->ctx;
> + ctx_index = ngx_modules[i]->ctx_index;
> +
> + if (module->merge_loc_conf) {
> + rv = module->merge_loc_conf(cf,
> + pctx->loc_conf[ctx_index],
> + ctx->loc_conf[ctx_index]);
> + if (rv != NGX_CONF_OK) {
> + break;
> + }
> + }
> + }
> +
> + *cf = save;
> +
> + if (rv != NGX_CONF_OK) {
> + return rv;
> + }
> +
> + rv = ngx_http_add_type_location(cf, &mime_type, pclcf, clcf);
> +
> + return rv;
> +}
> diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
> index f163e9a..982db27 100644
> --- a/src/http/ngx_http_core_module.h
> +++ b/src/http/ngx_http_core_module.h
> @@ -288,6 +288,7 @@ struct ngx_http_core_loc_conf_s {
> unsigned noname:1; /* "if () {}" block or limit_except */
> unsigned lmt_excpt:1;
> unsigned named:1;
> + unsigned type_loc:1;
>
> unsigned exact_match:1;
> unsigned noregex:1;
> @@ -398,6 +399,7 @@ struct ngx_http_core_loc_conf_s {
> ngx_uint_t types_hash_bucket_size;
>
> ngx_queue_t *locations;
> + ngx_list_t *type_locations;
>
> #if 0
> ngx_http_core_loc_conf_t *prev_location;
> @@ -450,6 +452,7 @@ ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
>
>
> void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
> +ngx_int_t ngx_http_find_content_type(ngx_http_request_t *r, ngx_str_t *typeptr);
> ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
> void ngx_http_set_exten(ngx_http_request_t *r);
> ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
> @@ -467,6 +470,10 @@ ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
> ngx_http_post_subrequest_t *psr, ngx_uint_t flags);
> ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,
> ngx_str_t *uri, ngx_str_t *args);
> +ngx_http_core_loc_conf_t *ngx_http_find_named_location(ngx_http_request_t *r,
> + ngx_str_t *name);
> +ngx_int_t ngx_http_use_location(ngx_http_request_t *r,
> + ngx_http_core_loc_conf_t *clcf);
> ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);
>
>
> @@ -482,6 +489,9 @@ ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain);
> ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);
>
>
> +ngx_http_core_loc_conf_t *ngx_http_core_find_type_location(
> + ngx_http_core_loc_conf_t *pclcf, ngx_str_t *mime_type, ngx_int_t exact);
> +
> extern ngx_module_t ngx_http_core_module;
>
> extern ngx_uint_t ngx_http_max_module;
> --
> 1.6.2.4
>
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://nginx.org/mailman/listinfo/nginx-devel
>
--
Michael Lustfield
Kalliki Software, SD LoCo
Network and Systems Administrator
More information about the nginx-devel
mailing list