[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