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); }