6 messages in ru.sysoev.nginx[PATCH] "Allow" header
FromSent OnAttachments
Evan MillerMay 19, 2007 2:00 pm 
Igor SysoevMay 20, 2007 12:45 am 
Evan MillerMay 20, 2007 1:24 am 
Igor SysoevMay 20, 2007 5:10 am 
Evan MillerMay 20, 2007 11:43 am 
Igor SysoevMay 21, 2007 6:59 am 
Actions with this message:
Paste this link in email or IM:
Paste this link in email or IM:
Atom feed for this thread
Paste this URL into your reader:
Subject:[PATCH] "Allow" headerActions...
From:Evan Miller (emmi@public.gmane.org)
Date:May 19, 2007 2:00:05 pm
List:ru.sysoev.nginx

The "Allow" header in HTTP tells a client what methods (GET, POST, DELETE, etc.) are permitted at a certain URL. RFC 2616 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7) says that an HTTP 1.1 server "MUST" return an Allow header with all 405 Method Not Allowed responses. However, Nginx does not currently comply with this provision. Below is a patch to support the Allow header. It puts the burden of defining legal methods on handler modules, so I have modified all of the modules that return a 405 response to use the Allow header appropriately.

Without patch:

DELETE /static/foo.bar HTTP/1.1 Host: tim

HTTP/1.1 405 Not Allowed Server: nginx/0.5.20 Date: Sat, 19 May 2007 20:22:06 GMT Content-Type: text/html Content-Length: 173

<snip>

With patch:

DELETE /static/foo.bar HTTP/1.1 Host: tim

HTTP/1.1 405 Not Allowed Server: nginx/0.5.20 Date: Sat, 19 May 2007 20:22:06 GMT Content-Type: text/html Content-Length: 173 Allow: GET, HEAD

<snip>

The patch works by letting modules set r->headers_out.allow_methods to a bitwise OR'd representation of the methods they allow. Modules can also set the Allow header explicitly and store the resulting ngx_table_elt_t in r->headers_out.allow, in which case the bitwise representation will be ignored. This follows the pattern of the other headers.

Comments appreciated.

Evan

diff -Naur nginx-0.5.20/src/http/modules/ngx_http_empty_gif_module.c nginx-evanm-0.5.20/src/http/modules/ngx_http_empty_gif_module.c --- nginx-0.5.20/src/http/modules/ngx_http_empty_gif_module.c 2007-01-17 02:50:52.000000000 -0800 +++ nginx-evanm-0.5.20/src/http/modules/ngx_http_empty_gif_module.c 2007-05-19 13:01:05.000000000 -0700 @@ -113,6 +113,7 @@ ngx_chain_t out;

if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + r->headers_out.allow_methods = NGX_HTTP_GET|NGX_HTTP_HEAD; return NGX_HTTP_NOT_ALLOWED; }

diff -Naur nginx-0.5.20/src/http/modules/ngx_http_flv_module.c nginx-evanm-0.5.20/src/http/modules/ngx_http_flv_module.c --- nginx-0.5.20/src/http/modules/ngx_http_flv_module.c 2007-01-18 12:15:09.000000000 -0800 +++ nginx-evanm-0.5.20/src/http/modules/ngx_http_flv_module.c 2007-05-19 13:01:32.000000000 -0700 @@ -77,6 +77,7 @@ ngx_http_core_loc_conf_t *clcf;

if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + r->headers_out.allow_methods = NGX_HTTP_GET|NGX_HTTP_HEAD; return NGX_HTTP_NOT_ALLOWED; }

diff -Naur nginx-0.5.20/src/http/modules/ngx_http_memcached_module.c nginx-evanm-0.5.20/src/http/modules/ngx_http_memcached_module.c --- nginx-0.5.20/src/http/modules/ngx_http_memcached_module.c 2007-01-29 03:54:36.000000000 -0800 +++ nginx-evanm-0.5.20/src/http/modules/ngx_http_memcached_module.c 2007-05-19 13:02:10.000000000 -0700 @@ -164,6 +164,7 @@ ngx_http_memcached_loc_conf_t *mlcf;

if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + r->headers_out.allow_methods = NGX_HTTP_GET|NGX_HTTP_HEAD; return NGX_HTTP_NOT_ALLOWED; }

diff -Naur nginx-0.5.20/src/http/modules/ngx_http_static_module.c nginx-evanm-0.5.20/src/http/modules/ngx_http_static_module.c --- nginx-0.5.20/src/http/modules/ngx_http_static_module.c 2007-01-18 12:15:09.000000000 -0800 +++ nginx-evanm-0.5.20/src/http/modules/ngx_http_static_module.c 2007-05-19 13:02:43.000000000 -0700 @@ -88,6 +88,7 @@ ngx_http_core_loc_conf_t *clcf;

if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + r->headers_out.allow_methods = NGX_HTTP_GET|NGX_HTTP_HEAD; return NGX_HTTP_NOT_ALLOWED; }

diff -Naur nginx-0.5.20/src/http/modules/ngx_http_stub_status_module.c nginx-evanm-0.5.20/src/http/modules/ngx_http_stub_status_module.c --- nginx-0.5.20/src/http/modules/ngx_http_stub_status_module.c 2006-08-30 03:39:17.000000000 -0700 +++ nginx-evanm-0.5.20/src/http/modules/ngx_http_stub_status_module.c 2007-05-19 13:03:35.000000000 -0700 @@ -65,7 +65,8 @@ ngx_chain_t out; ngx_atomic_int_t ap, hn, ac, rq, rd, wr;

- if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { + if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + r->headers_out.allow_methods = NGX_HTTP_GET|NGX_HTTP_HEAD; return NGX_HTTP_NOT_ALLOWED; }

diff -Naur nginx-0.5.20/src/http/ngx_http_header_filter_module.c nginx-evanm-0.5.20/src/http/ngx_http_header_filter_module.c --- nginx-0.5.20/src/http/ngx_http_header_filter_module.c 2007-01-18 12:51:51.000000000 -0800 +++ nginx-evanm-0.5.20/src/http/ngx_http_header_filter_module.c 2007-05-19 13:55:02.000000000 -0700 @@ -121,6 +121,25 @@ /* ngx_null_string, */ /* "510 Not Extended" */ };

+static ngx_str_t ngx_http_methods[] = { + ngx_string("UNKNOWN"), + ngx_string("GET"), + ngx_string("HEAD"), + ngx_string("POST"), + ngx_string("PUT"), + ngx_string("DELETE"), + ngx_string("MKCOL"), + ngx_string("COPY"), + ngx_string("MOVE"), + ngx_string("OPTIONS"), + ngx_string("PROPFIND"), + ngx_string("PROPPATCH"), + ngx_string("LOCK"), + ngx_string("UNLOCK"), + ngx_string("TRACE"), + ngx_null_string, + }; +

ngx_http_header_out_t ngx_http_headers_out[] = { { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) }, @@ -153,7 +172,7 @@ u_char *p; size_t len; ngx_buf_t *b; - ngx_uint_t status, i; + ngx_uint_t status, i, allow_methods_n; ngx_chain_t out; ngx_list_part_t *part; ngx_table_elt_t *header; @@ -268,6 +287,24 @@ len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; }

+ allow_methods_n = 0; + + if (r->headers_out.allow == NULL + && r->headers_out.allow_methods) + { + for (i=0; ngx_http_methods[i].len; i++) { + if (r->headers_out.allow_methods & (1 << i)) { + allow_methods_n++; + len += ngx_http_methods[i].len; + } + } + if (allow_methods_n) { + len += sizeof("Allow: ") - 1; + len += 2 * (allow_methods_n - 1); // commas and spaces + len += sizeof(CRLF) - 1; + } + } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

if (r->headers_out.location @@ -418,6 +455,23 @@ *b->last++ = CR; *b->last++ = LF; }

+ if (r->headers_out.allow == NULL + && allow_methods_n) + { + b->last = ngx_cpymem(b->last, "Allow: ", + sizeof("Allow: ") - 1); + for (i=0; ngx_http_methods[i].len; i++) { + if (r->headers_out.allow_methods & (1 << i)) { + b->last = ngx_copy(b->last, ngx_http_methods[i].data, + ngx_http_methods[i].len); + if (--allow_methods_n) { + *b->last++ = ','; *b->last++ = ' '; + } + } + } + *b->last++ = CR; *b->last++ = LF; + } + if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') diff -Naur nginx-0.5.20/src/http/ngx_http_request.h nginx-evanm-0.5.20/src/http/ngx_http_request.h --- nginx-0.5.20/src/http/ngx_http_request.h 2007-04-21 00:50:19.000000000 -0700 +++ nginx-evanm-0.5.20/src/http/ngx_http_request.h 2007-05-19 13:14:35.000000000 -0700 @@ -230,6 +230,7 @@ ngx_table_elt_t *content_encoding; ngx_table_elt_t *location; ngx_table_elt_t *last_modified; + ngx_table_elt_t *allow; ngx_table_elt_t *content_range; ngx_table_elt_t *accept_ranges; ngx_table_elt_t *www_authenticate; @@ -247,6 +248,8 @@ off_t content_length_n; time_t date_time; time_t last_modified_time; + + ngx_uint_t allow_methods; } ngx_http_headers_out_t;