On 2021/02/26 15:38, Bruce Hill wrote: > Hello, this is my first time contributing to openbsd and this mailing > list, so please excuse any newbie blunders. I recently switched my > personal website to use httpd with statically generated HTML files, but > was unhappy to find that my HTML files could only be accessed by exact > filename, including the ".html" at the end. My site previously ran on > Apache with rewrite rules to ensure that "example.com/foo" would serve > the file "/foo.html" (when "/foo" didn't exist). I wanted to keep my > original URLs working and I aesthetically prefer URLs without ".html" > suffixes, so I looked around for different options with httpd. The best > option I could find was to create symbolic links from "/foo" to > "/foo.html" and set the default media type to text/html, but this > solution was cumbersome (I had to maintain all the symbolic links) and > had undesirable side effects (all extensionless files were treated as > text/html instead of text/plain). > > I decided instead to look into modifying httpd to support implicit file > extensions. My basic idea was to add a configurable setting called > `implicit extension`. When a file is not found and the file doesn't > already have the implicit extension, then httpd will look for the same > filename but with the implicit extension added at the end, and reroute > to that file if it exists. This neatly solves my problem by adding > `implicit extension ".html"` to the top of my /etc/httpd.conf file, and > `implicit extension ".php"` to the section of my website that uses PHP > scripts. For simplicity (and because my website doesn't use both HTML > and PHP in the same subdomain), I opted to only support a single > implicit extension per config block, though in the future, supporting > globs as implicit extensions might also be nice(e.g. `implicit extension > ".{html,php}"`).
I might be missing something but doesn't this do the same? location not found match "/(.*)" { request rewrite "/%1.html" } > I've been running the code on my personal website for a while now > without any issues. If this patch is accepted, I also have a separate > small patch that displays custom HTTP error pages if you have a > /<error code>[<implicit extension>] file (e.g. /404 or /404.html), which > works well in conjunction with this patch. Custom error pages sound useful indeed. > I'm happy to take any feedback or criticism on both the idea and my > implementation patch below. Hopefully other people find this useful as > well! > > - Bruce Hill > > > Index: config.c > =================================================================== > RCS file: /cvs/src/usr.sbin/httpd/config.c,v > retrieving revision 1.61 > diff -u -p -u -p -r1.61 config.c > --- config.c 21 Sep 2020 09:42:07 -0000 1.61 > +++ config.c 26 Feb 2021 23:30:18 -0000 > @@ -568,6 +568,13 @@ config_getserver_config(struct httpd *en > &parent->default_type, sizeof(struct media_type)); > } > + f = SRVFLAG_IMPLICIT_EXT; > + if ((srv_conf->flags & f) == 0) { > + srv_conf->flags |= parent->flags & f; > + memcpy(&srv_conf->implicit_extension, > + &parent->implicit_extension, > sizeof(parent->implicit_extension)); > + } > + > f = SRVFLAG_PATH_REWRITE|SRVFLAG_NO_PATH_REWRITE; > if ((srv_conf->flags & f) == 0) { > srv_conf->flags |= parent->flags & f; > Index: httpd.h > =================================================================== > RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v > retrieving revision 1.153 > diff -u -p -u -p -r1.153 httpd.h > --- httpd.h 29 Oct 2020 12:30:52 -0000 1.153 > +++ httpd.h 26 Feb 2021 23:30:19 -0000 > @@ -57,6 +57,7 @@ > #define HTTPD_REALM_MAX 255 > #define HTTPD_LOCATION_MAX 255 > #define HTTPD_DEFAULT_TYPE { "bin", "application", "octet-stream", NULL } > +#define HTTPD_IMPLICIT_EXT "" > #define HTTPD_LOGVIS VIS_NL|VIS_TAB|VIS_CSTYLE > #define HTTPD_TLS_CERT "/etc/ssl/server.crt" > #define HTTPD_TLS_KEY "/etc/ssl/private/server.key" > @@ -391,6 +392,7 @@ SPLAY_HEAD(client_tree, client); > #define SRVFLAG_DEFAULT_TYPE 0x00800000 > #define SRVFLAG_PATH_REWRITE 0x01000000 > #define SRVFLAG_NO_PATH_REWRITE 0x02000000 > +#define SRVFLAG_IMPLICIT_EXT 0x04000000 > #define SRVFLAG_LOCATION_FOUND 0x40000000 > #define SRVFLAG_LOCATION_NOT_FOUND 0x80000000 > @@ -399,8 +401,8 @@ SPLAY_HEAD(client_tree, client); > "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG" \ > "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ > "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH" \ > - "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH\32NO_PATH" \ > - "\37LOCATION_FOUND\40LOCATION_NOT_FOUND" > + "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH\32NO_PATH" \ > + "\33IMPLICIT_EXTENSION\37LOCATION_FOUND\40LOCATION_NOT_FOUND" > #define TCPFLAG_NODELAY 0x01 > #define TCPFLAG_NNODELAY 0x02 > @@ -481,6 +483,7 @@ struct server_config { > char accesslog[PATH_MAX]; > char errorlog[PATH_MAX]; > struct media_type default_type; > + char implicit_extension[MEDIATYPE_NAMEMAX]; > struct sockaddr_storage fastcgi_ss; > @@ -594,6 +597,7 @@ struct httpd { > struct serverlist *sc_servers; > struct mediatypes *sc_mediatypes; > struct media_type sc_default_type; > + char *sc_implicit_extension; > struct serverauth *sc_auth; > struct privsep *sc_ps; > Index: parse.y > =================================================================== > RCS file: /cvs/src/usr.sbin/httpd/parse.y,v > retrieving revision 1.120 > diff -u -p -u -p -r1.120 parse.y > --- parse.y 29 Oct 2020 12:30:52 -0000 1.120 > +++ parse.y 26 Feb 2021 23:30:19 -0000 > @@ -138,7 +138,8 @@ typedef struct { > %} > %token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT > CIPHERS COMMON > -%token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY > LIFETIME > +%token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR EXTENSION FCGI > IMPLICIT > +%token INDEX IP KEY LIFETIME > %token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON > PORT PREFORK > %token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG > TCP TICKET > %token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD > REQUEST > @@ -222,6 +223,9 @@ main : PREFORK NUMBER { > memcpy(&conf->sc_default_type, &media, > sizeof(struct media_type)); > } > + | IMPLICIT EXTENSION STRING { > + conf->sc_implicit_extension = $3; > + } > ; > server : SERVER optmatch STRING { > @@ -636,6 +640,16 @@ serveroptsl : LISTEN ON STRING opttls po > memcpy(&srv_conf->default_type, &media, > sizeof(struct media_type)); > } > + | IMPLICIT EXTENSION STRING { > + if (strlcpy(srv_conf->implicit_extension, $3, > + sizeof(srv_conf->implicit_extension)) >= > + sizeof(srv_conf->implicit_extension)) { > + yyerror("implicit extension truncated"); > + free($3); > + YYERROR; > + } > + srv_conf->flags |= SRVFLAG_IMPLICIT_EXT; > + } > | include > | hsts { > if (parentsrv != NULL) { > @@ -1397,10 +1411,12 @@ lookup(char *s) > { "drop", DROP }, > { "ecdhe", ECDHE }, > { "error", ERR }, > + { "extension", EXTENSION }, > { "fastcgi", FCGI }, > { "forwarded", FORWARDED }, > { "found", FOUND }, > { "hsts", HSTS }, > + { "implicit", IMPLICIT }, > { "include", INCLUDE }, > { "index", INDEX }, > { "ip", IP }, > @@ -1807,6 +1823,8 @@ parse_config(const char *filename, struc > /* Set default media type */ > memcpy(&conf->sc_default_type, &dflt, sizeof(struct media_type)); > + > + conf->sc_implicit_extension = HTTPD_IMPLICIT_EXT; > errors = 0; > Index: server_file.c > =================================================================== > RCS file: /cvs/src/usr.sbin/httpd/server_file.c,v > retrieving revision 1.68 > diff -u -p -u -p -r1.68 server_file.c > --- server_file.c 22 May 2020 07:18:17 -0000 1.68 > +++ server_file.c 26 Feb 2021 23:30:19 -0000 > @@ -59,13 +59,43 @@ server_file_access(struct httpd *env, st > struct server_config *srv_conf = clt->clt_srv_conf; > struct stat st; > struct kv *r, key; > - char *newpath, *encodedpath; > + char *newpath, *encodedpath, *implicit_extension; > int ret; > errno = 0; > if (access(path, R_OK) == -1) { > - goto fail; > + /* If the file does exist, use whatever failure happened. */ > + if (errno != ENOENT) > + goto fail; > + > + /* Try to use the implicit extension, if set. */ > + implicit_extension = srv_conf->flags & SRVFLAG_IMPLICIT_EXT ? > + srv_conf->implicit_extension : env->sc_implicit_extension; > + if (!implicit_extension || !implicit_extension[0]) > + goto fail; > + > + /* Abort if the file already has the implicit extension. */ > + if (strlen(path) >= strlen(implicit_extension) && > + strcmp(&path[strlen(path)-strlen(implicit_extension)], > + implicit_extension) == 0) > + goto fail; > + > + if (strlcat(path, implicit_extension, len) >= len) > + return (500); > + > + if (asprintf(&newpath, "%s%s", desc->http_path, > + implicit_extension) == -1) > + return (500); > + > + desc->http_path_alias = newpath; > + > + if (server_getlocation(clt, newpath) != srv_conf) { > + /* Appending the extension has changed the location. */ > + return (server_file(env, clt)); > + } > + > + return (server_file_access(env, clt, path, len)); > } else if (stat(path, &st) == -1) { > goto fail; > } else if (S_ISDIR(st.st_mode)) { >