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)) {
> 

Reply via email to