[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