ngninx and apache support url rewriting, letting you redirect from
arbitrary urls with pattern matching. In my experience the primary
uses for this are to redirect from http to https or to remove/add www
in the hostname, so I thought it might be useful to have options making
these specific uses possible and simple to do.

This adds an "enforce" option, where "enforce https" redirects non-http
to https, "enforce www" redirects from example.org to www.example.org 
<http://www.example.org>,
and "enforce no www" redirects from www.example.org <http://www.example.org> to 
example.org.

Would appreciate any feedback or suggestions.

Thanks



Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.64
diff -u -p -u -r1.64 httpd.h
--- usr.sbin/httpd/httpd.h      12 Dec 2014 14:45:59 -0000      1.64
+++ usr.sbin/httpd/httpd.h      23 Dec 2014 17:20:55 -0000
@@ -345,6 +345,10 @@ SPLAY_HEAD(client_tree, client);
        "\10\01NODELAY\02NO_NODELAY\03SACK\04NO_SACK"           \
        "\05SOCKET_BUFFER_SIZE\06IP_TTL\07IP_MINTTL\10NO_SPLICE"
 
+#define ENFORCE_HTTPS          0x10
+#define ENFORCE_WWW            0x01
+#define ENFORCE_NO_WWW         0x02
+
 enum log_format {
        LOG_FORMAT_COMMON,
        LOG_FORMAT_COMBINED,
@@ -391,6 +395,8 @@ struct server_config {
        u_int8_t                 tcpipttl;
        u_int8_t                 tcpipminttl;
 
+       u_int8_t                 enforce;
+
        enum log_format          logformat;
        struct log_file         *logaccess;
        struct log_file         *logerror;
@@ -499,6 +505,7 @@ struct server_config *
         serverconfig_byid(u_int32_t);
 int     server_foreach(int (*)(struct server *,
            struct server_config *, void *), void *);
+int     server_enforce_redirect(struct client *);
 
 SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
 
Index: usr.sbin/httpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.46
diff -u -p -u -r1.46 parse.y
--- usr.sbin/httpd/parse.y      21 Dec 2014 00:54:49 -0000      1.46
+++ usr.sbin/httpd/parse.y      23 Dec 2014 17:20:56 -0000
@@ -129,7 +129,7 @@ typedef struct {
 %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION
 %token LOG LOGDIR MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT
 %token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES 
-%token ERROR INCLUDE
+%token ERROR INCLUDE ENFORCE HTTPS WWW
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
@@ -371,6 +371,15 @@ serveroptsl        : LISTEN ON STRING opttls po
                }
                | DIRECTORY dirflags
                | DIRECTORY '{' dirflags_l '}'
+               | ENFORCE HTTPS                 {
+                       srv_conf->enforce |= ENFORCE_HTTPS;
+               }
+               | ENFORCE WWW                   {
+                       srv_conf->enforce |= ENFORCE_WWW;
+               }
+               | ENFORCE NO WWW                {
+                       srv_conf->enforce |= ENFORCE_NO_WWW;
+               }
                | logformat
                | fastcgi
                | LOCATION STRING               {
@@ -863,8 +872,10 @@ lookup(char *s)
                { "common",             COMMON },
                { "connection",         CONNECTION },
                { "directory",          DIRECTORY },
+               { "enforce",            ENFORCE },
                { "error",              ERR },
                { "fastcgi",            FCGI },
+               { "https",              HTTPS },
                { "include",            INCLUDE },
                { "index",              INDEX },
                { "ip",                 IP },
@@ -890,7 +901,8 @@ lookup(char *s)
                { "tcp",                TCP },
                { "timeout",            TIMEOUT },
                { "tls",                TLS },
-               { "types",              TYPES }
+               { "types",              TYPES },
+               { "www",                WWW }
        };
        const struct keywords   *p;
 
Index: usr.sbin/httpd/server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.57
diff -u -p -u -r1.57 server_http.c
--- usr.sbin/httpd/server_http.c        21 Dec 2014 00:54:49 -0000      1.57
+++ usr.sbin/httpd/server_http.c        23 Dec 2014 17:20:56 -0000
@@ -882,6 +882,9 @@ server_response(struct httpd *httpd, str
        /* Now search for the location */
        srv_conf = server_getlocation(clt, desc->http_path);
 
+       if (server_enforce_redirect(clt))
+               return (-1);
+
        return (server_file(httpd, clt));
  fail:
        server_abort_http(clt, 400, "bad request");
@@ -1209,4 +1212,46 @@ server_log_http(struct client *clt, u_in
        }
 
        return (0);
+}
+
+int
+server_enforce_redirect(struct client *clt)
+{
+       struct http_descriptor  *desc = clt->clt_descreq;
+       struct server_config    *srv_conf = clt->clt_srv_conf;
+       char                    *url;
+       int                      enforce = 0;
+
+       if (srv_conf->enforce & ENFORCE_HTTPS &&
+          (srv_conf->flags & SRVFLAG_TLS) == 0)
+               enforce = 1;
+
+       if (srv_conf->enforce & ENFORCE_WWW &&
+           strncasecmp(desc->http_host, "www.", 4))
+               enforce = 1;
+       else if (srv_conf->enforce & ENFORCE_NO_WWW &&
+          (strncasecmp(desc->http_host, "www.", 4) == 0))
+               enforce = 1;
+
+       if (enforce == 0)
+               return (0);
+
+       if (asprintf(&url, "http%s://%s%s%s%s%s",
+               (srv_conf->enforce & ENFORCE_HTTPS ||
+                srv_conf->flags & SRVFLAG_TLS) ? "s" : "",
+               (srv_conf->enforce & ENFORCE_WWW &&
+                strncasecmp(desc->http_host, "www.", 4)) ? "www." : "",
+               (srv_conf->enforce & ENFORCE_NO_WWW &&
+               (strncasecmp(desc->http_host, "www.", 4) == 0)
+                       ? desc->http_host + 4 : desc->http_host),
+               desc->http_path == NULL ? "" : desc->http_path,
+               desc->http_query == NULL ? "" : "?",
+               desc->http_query == NULL ? "" : desc->http_query) == -1) {
+                       server_abort_http(clt, 500, "server error");
+                       return (-1);
+       }
+
+       server_abort_http(clt, 301, url);
+       free(url);
+       return (1);
 }

Reply via email to