[PATCH] "Allow" header
Evan Miller
emmiller+gmane at gmail.com
Sun May 20 01:00:28 MSD 2007
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;
More information about the nginx
mailing list