[PATCH] Add support for HTTP 451 Return Code (RFC 7725)

Babak Farrokhi babak at farrokhi.net
Wed Nov 23 07:48:12 UTC 2016


Hello,

A recent RFC (7725) has introduced HTTP Return Code 451 to be returned when access to a resource is forbidden for legal reasons [1].
In addition to the return code, HTTP server may return an additional “Link” field in HTTP header. The new header should be formatted as below:

Link: <https://spqr.example.org/legislatione>; rel="blocked-by"

Please also note that the new Relation Name (blocked-by) is also added to IANA Link Relation Type Registry [2].

A user may simply return a 451 code based on a condition or additionally provide a link to a URL (usually pointed to the applicable legislation or regulation):

    if ($http_user_agent !~ MSIE) {
        return 451 https://spqr.example.org/legislatione;
    }

    if ($http_user_agent ~* (Wget) ) {
    	return 451;
    }

Here is the proposed patch:

# HG changeset patch
# User Babak Farrokhi <babak at farrokhi.net>
# Date 1479886479 -12600
#      Wed Nov 23 11:04:39 2016 +0330
# Node ID d56f78822c5bcaa8bde2d95c862d06491618154c
# Parent  2c7a2d75938a31044552b0a6cd6edaebdaf0bd69
Add support for HTTP 451 Return Code (RFC 7725)

RFC 7725 introduced HTTP 451 “Unavailable For Legal Reasons” return code,
to be returned when the user requests a resource which cannot be served
for legal reasons.
It also introduces an optional "Link" header field that should contain a
link to relevant webpage (e.g. legal documents) with following format:

Link: <https://spqr.example.org/legislatione>; rel="blocked-by"

A user can now configure nginx to return this code using a return clause
in configuration file:

    if ($http_user_agent !~ MSIE) {
        return 451 https://spqr.example.org/legislatione;
    }

    if ($http_user_agent ~* (Wget) ) {
    	return 451;
    }

Please note that the URL is optional and if not specified, no "Link"
field will be added to header.

diff -r 2c7a2d75938a -r d56f78822c5b src/http/modules/perl/nginx.pm
--- a/src/http/modules/perl/nginx.pm	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/modules/perl/nginx.pm	Wed Nov 23 11:04:39 2016 +0330
@@ -88,6 +88,7 @@
 use constant HTTP_REQUEST_URI_TOO_LARGE     => 414;
 use constant HTTP_UNSUPPORTED_MEDIA_TYPE    => 415;
 use constant HTTP_RANGE_NOT_SATISFIABLE     => 416;
+use constant HTTP_NOT_ALLOWED_LEGALLY       => 451;

 use constant HTTP_INTERNAL_SERVER_ERROR     => 500;
 use constant HTTP_SERVER_ERROR              => 500;
diff -r 2c7a2d75938a -r d56f78822c5b src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/ngx_http_core_module.c	Wed Nov 23 11:04:39 2016 +0330
@@ -1862,6 +1862,7 @@
     ngx_str_t     val;
     ngx_buf_t    *b;
     ngx_chain_t   out;
+    ngx_str_t     link;

     if (ngx_http_discard_request_body(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -1873,6 +1874,32 @@
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }

+    if (status == NGX_HTTP_NOT_ALLOWED_LEGALLY) {
+        ngx_http_clear_link(r);
+
+        r->headers_out.link = ngx_list_push(&r->headers_out.headers);
+        if (r->headers_out.link == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->headers_out.link->hash = 1;
+        ngx_str_set(&r->headers_out.link->key, "Link");
+
+        link.data = ngx_pcalloc(r->pool, val.len +
+                        ngx_strlen("<>; rel=\"blocked-by\""));
+        if (link.data == NULL) {
+                r->headers_out.link->hash = 0;
+                return NGX_ERROR;
+        }
+
+        link.len = ngx_sprintf(link.data, "<%s>; rel=\"blocked-by\"",
+                        val.data) - link.data;
+
+        r->headers_out.link->value = link;
+
+        return status;
+    }
+
     if (status == NGX_HTTP_MOVED_PERMANENTLY
         || status == NGX_HTTP_MOVED_TEMPORARILY
         || status == NGX_HTTP_SEE_OTHER
diff -r 2c7a2d75938a -r d56f78822c5b src/http/ngx_http_core_module.h
--- a/src/http/ngx_http_core_module.h	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/ngx_http_core_module.h	Wed Nov 23 11:04:39 2016 +0330
@@ -570,6 +570,13 @@
         r->headers_out.location = NULL;                                       \
     }

+#define ngx_http_clear_link(r)                                                \
+                                                                              \
+    if (r->headers_out.link) {                                                \
+        r->headers_out.link->hash = 0;                                        \
+        r->headers_out.link = NULL;                                           \
+    }
+
 #define ngx_http_clear_etag(r)                                                \
                                                                               \
     if (r->headers_out.etag) {                                                \
diff -r 2c7a2d75938a -r d56f78822c5b src/http/ngx_http_header_filter_module.c
--- a/src/http/ngx_http_header_filter_module.c	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/ngx_http_header_filter_module.c	Wed Nov 23 11:04:39 2016 +0330
@@ -100,12 +100,38 @@
     ngx_null_string,  /* "419 unused" */
     ngx_null_string,  /* "420 unused" */
     ngx_string("421 Misdirected Request"),
+    ngx_null_string,  /* "422 Unprocessable Entity" */
+    ngx_null_string,  /* "423 Locked" */
+    ngx_null_string,  /* "424 Failed Dependency" */
+    ngx_null_string,  /* "425 unused" */
+    ngx_null_string,  /* "426 Upgrade Required" */
+    ngx_null_string,  /* "427 unused" */
+    ngx_null_string,  /* "428 Precondition Required" */
+    ngx_null_string,  /* "429 Too Many Requests" */
+    ngx_null_string,  /* "430 unused" */
+    ngx_null_string,  /* "431 Request Header Fields Too Large" */
+    ngx_null_string,  /* "432 unused" */
+    ngx_null_string,  /* "433 unused" */
+    ngx_null_string,  /* "434 unused" */
+    ngx_null_string,  /* "435 unused" */
+    ngx_null_string,  /* "436 unused" */
+    ngx_null_string,  /* "437 unused" */
+    ngx_null_string,  /* "438 unused" */
+    ngx_null_string,  /* "439 unused" */
+    ngx_null_string,  /* "440 unused" */
+    ngx_null_string,  /* "441 unused" */
+    ngx_null_string,  /* "442 unused" */
+    ngx_null_string,  /* "443 unused" */
+    ngx_null_string,  /* "444 unused" */
+    ngx_null_string,  /* "445 unused" */
+    ngx_null_string,  /* "446 unused" */
+    ngx_null_string,  /* "447 unused" */
+    ngx_null_string,  /* "448 unused" */
+    ngx_null_string,  /* "449 unused" */
+    ngx_null_string,  /* "450 unused" */
+    ngx_string("451 Unavailable For Legal Reasons"),

-    /* ngx_null_string, */  /* "422 Unprocessable Entity" */
-    /* ngx_null_string, */  /* "423 Locked" */
-    /* ngx_null_string, */  /* "424 Failed Dependency" */
-
-#define NGX_HTTP_LAST_4XX  422
+#define NGX_HTTP_LAST_4XX  452
 #define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)

     ngx_string("500 Internal Server Error"),
@@ -133,6 +159,7 @@
                  offsetof(ngx_http_headers_out_t, content_length) },
     { ngx_string("Content-Encoding"),
                  offsetof(ngx_http_headers_out_t, content_encoding) },
+    { ngx_string("Link"), offsetof(ngx_http_headers_out_t, link) },
     { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) },
     { ngx_string("Last-Modified"),
                  offsetof(ngx_http_headers_out_t, last_modified) },
diff -r 2c7a2d75938a -r d56f78822c5b src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/ngx_http_request.h	Wed Nov 23 11:04:39 2016 +0330
@@ -97,6 +97,7 @@
 #define NGX_HTTP_RANGE_NOT_SATISFIABLE     416
 #define NGX_HTTP_MISDIRECTED_REQUEST       421

+#define NGX_HTTP_NOT_ALLOWED_LEGALLY       451

 /* Our own HTTP codes */

@@ -253,6 +254,7 @@
     ngx_table_elt_t                  *date;
     ngx_table_elt_t                  *content_length;
     ngx_table_elt_t                  *content_encoding;
+    ngx_table_elt_t                  *link;
     ngx_table_elt_t                  *location;
     ngx_table_elt_t                  *refresh;
     ngx_table_elt_t                  *last_modified;
diff -r 2c7a2d75938a -r d56f78822c5b src/http/ngx_http_special_response.c
--- a/src/http/ngx_http_special_response.c	Mon Nov 21 16:49:19 2016 +0300
+++ b/src/http/ngx_http_special_response.c	Wed Nov 23 11:04:39 2016 +0330
@@ -217,6 +217,12 @@
 "<center><h1>421 Misdirected Request</h1></center>" CRLF
 ;

+static char ngx_http_error_451_page[] =
+"<html>" CRLF
+"<head><title>451 Unavailable For Legal Reasons</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>451 Unavailable For Legal Reasons</h1></center>" CRLF
+;

 static char ngx_http_error_494_page[] =
 "<html>" CRLF
@@ -347,8 +353,38 @@
     ngx_null_string,                     /* 419 */
     ngx_null_string,                     /* 420 */
     ngx_string(ngx_http_error_421_page),
+    ngx_null_string,                     /* 422 */
+    ngx_null_string,                     /* 423 */
+    ngx_null_string,                     /* 424 */
+    ngx_null_string,                     /* 425 */
+    ngx_null_string,                     /* 426 */
+    ngx_null_string,                     /* 427 */
+    ngx_null_string,                     /* 428 */
+    ngx_null_string,                     /* 429 */
+    ngx_null_string,                     /* 430 */
+    ngx_null_string,                     /* 431 */
+    ngx_null_string,                     /* 432 */
+    ngx_null_string,                     /* 433 */
+    ngx_null_string,                     /* 434 */
+    ngx_null_string,                     /* 435 */
+    ngx_null_string,                     /* 436 */
+    ngx_null_string,                     /* 437 */
+    ngx_null_string,                     /* 438 */
+    ngx_null_string,                     /* 439 */
+    ngx_null_string,                     /* 440 */
+    ngx_null_string,                     /* 441 */
+    ngx_null_string,                     /* 442 */
+    ngx_null_string,                     /* 443 */
+    ngx_null_string,                     /* 444 */
+    ngx_null_string,                     /* 445 */
+    ngx_null_string,                     /* 446 */
+    ngx_null_string,                     /* 447 */
+    ngx_null_string,                     /* 448 */
+    ngx_null_string,                     /* 449 */
+    ngx_null_string,                     /* 450 */
+    ngx_string(ngx_http_error_451_page), /* 451, Unavailable For Legal Reasons */

-#define NGX_HTTP_LAST_4XX  422
+#define NGX_HTTP_LAST_4XX  452
 #define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)

     ngx_string(ngx_http_error_494_page), /* 494, request header too large */




[1] https://datatracker.ietf.org/doc/rfc7725/?include_text=1
[2] http://www.iana.org/assignments/link-relations/link-relations.xhtml

Kind Regards,
-- 
Babak Farrokhi
https://github.com/farrokhi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 931 bytes
Desc: OpenPGP digital signature
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20161123/d8b52b66/attachment.bin>


More information about the nginx-devel mailing list