Hi,

as requested by many, the following simple diff allows to change the
default media type globally or per location, eg.
        default type text/plain

Note that this diff conflicts with florian's HSTS diff, we'll reassign
the flag in httpd.h based on which one goes in first.

OK?

Reyk

Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.39
diff -u -p -u -p -r1.39 config.c
--- config.c    15 Jul 2015 16:00:39 -0000      1.39
+++ config.c    18 Jul 2015 00:54:45 -0000
@@ -436,6 +436,13 @@ config_getserver_config(struct httpd *en
                                goto fail;
                }
 
+               f = SRVFLAG_DEFAULT_TYPE;
+               if ((srv_conf->flags & f) == 0) {
+                       srv_conf->flags |= parent->flags & f;
+                       memcpy(&srv_conf->default_type,
+                           &parent->default_type, sizeof(struct media_type));
+               }
+
                memcpy(&srv_conf->timeout, &parent->timeout,
                    sizeof(srv_conf->timeout));
                srv_conf->maxrequests = parent->maxrequests;
Index: httpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.c,v
retrieving revision 1.37
diff -u -p -u -p -r1.37 httpd.c
--- httpd.c     3 Jun 2015 02:24:36 -0000       1.37
+++ httpd.c     18 Jul 2015 00:54:45 -0000
@@ -1217,7 +1217,7 @@ media_purge(struct mediatypes *types)
 }
 
 struct media_type *
-media_find(struct mediatypes *types, char *file)
+media_find(struct mediatypes *types, const char *file)
 {
        struct media_type       *match, media;
        char                    *p;
@@ -1239,6 +1239,21 @@ media_find(struct mediatypes *types, cha
        match = RB_FIND(mediatypes, types, &media);
 
        return (match);
+}
+
+struct media_type *
+media_find_config(struct httpd *env, struct server_config *srv_conf,
+    const char *file)
+{
+       struct media_type       *match;
+
+       if ((match = media_find(env->sc_mediatypes, file)) != NULL)
+               return (match);
+       else if (srv_conf->flags & SRVFLAG_DEFAULT_TYPE)
+               return (&srv_conf->default_type);
+
+       /* fallback to the global default type */
+       return (&env->sc_default_type);
 }
 
 int
Index: httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.64
diff -u -p -u -p -r1.64 httpd.conf.5
--- httpd.conf.5        15 Jul 2015 17:10:47 -0000      1.64
+++ httpd.conf.5        18 Jul 2015 00:54:45 -0000
@@ -112,6 +112,15 @@ directory.
 If not specified, it defaults to
 .Pa /var/www ,
 the home directory of the www user.
+.It Ic default type Ar type/subtype
+Set the default media type that is used if the media type for a
+specified extension is not found in the configured types or for files
+without a file extension;
+see the
+.Sx TYPES
+section below.
+If not specified, the default type is set to
+.Ar application/octet-stream .
 .It Ic logdir Ar directory
 Specifies the full path of the directory in which log files will be written.
 If not specified, it defaults to
@@ -236,6 +245,9 @@ Specify the inactivity timeout in second
 The default timeout is 600 seconds (10 minutes).
 The maximum is 2147483647 seconds (68 years).
 .El
+.It Ic default type Ar type/subtype
+Set the default media type for the specified location,
+overwriting the global setting.
 .It Ic directory Ar option
 Set the specified options when serving or accessing directories.
 Valid options are:
Index: httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.88
diff -u -p -u -p -r1.88 httpd.h
--- httpd.h     16 Jul 2015 16:29:25 -0000      1.88
+++ httpd.h     18 Jul 2015 00:54:45 -0000
@@ -48,6 +48,7 @@
 #define HTTPD_LOGROOT          "/logs"
 #define HTTPD_ACCESS_LOG       "access.log"
 #define HTTPD_ERROR_LOG                "error.log"
+#define HTTPD_DEFAULT_TYPE     { "bin", "application", "octet-stream", NULL }
 #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"
@@ -351,13 +352,14 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_NO_BLOCK       0x00080000
 #define SRVFLAG_LOCATION_MATCH 0x00100000
 #define SRVFLAG_SERVER_MATCH   0x00200000
+#define SRVFLAG_DEFAULT_TYPE   0x00400000
 
 #define SRVFLAG_BITS                                                   \
        "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"           \
        "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET"   \
        "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG"          \
        "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH"         \
-       "\26SERVER_MATCH"
+       "\26SERVER_MATCH\27DEFAULT_TYPE"
 
 #define TCPFLAG_NODELAY                0x01
 #define TCPFLAG_NNODELAY       0x02
@@ -387,6 +389,15 @@ struct log_file {
 };
 TAILQ_HEAD(log_files, log_file) log_files;
 
+struct media_type {
+       char                     media_name[MEDIATYPE_NAMEMAX];
+       char                     media_type[MEDIATYPE_TYPEMAX];
+       char                     media_subtype[MEDIATYPE_TYPEMAX];
+       char                    *media_encoding;
+       RB_ENTRY(media_type)     media_entry;
+};
+RB_HEAD(mediatypes, media_type);
+
 struct auth {
        char                     auth_htpasswd[PATH_MAX];
        u_int32_t                auth_id;
@@ -404,6 +415,7 @@ struct server_config {
        char                     socket[PATH_MAX];
        char                     accesslog[NAME_MAX];
        char                     errorlog[NAME_MAX];
+       struct media_type        default_type;
 
        in_port_t                port;
        struct sockaddr_storage  ss;
@@ -473,15 +485,6 @@ struct server {
 };
 TAILQ_HEAD(serverlist, server);
 
-struct media_type {
-       char                     media_name[MEDIATYPE_NAMEMAX];
-       char                     media_type[MEDIATYPE_TYPEMAX];
-       char                     media_subtype[MEDIATYPE_TYPEMAX];
-       char                    *media_encoding;
-       RB_ENTRY(media_type)     media_entry;
-};
-RB_HEAD(mediatypes, media_type);
-
 struct httpd {
        u_int8_t                 sc_opts;
        u_int32_t                sc_flags;
@@ -495,6 +498,7 @@ struct httpd {
 
        struct serverlist       *sc_servers;
        struct mediatypes       *sc_mediatypes;
+       struct media_type        sc_default_type;
        struct serverauth       *sc_auth;
 
        struct privsep          *sc_ps;
@@ -639,7 +643,10 @@ struct media_type
 void            media_delete(struct mediatypes *, struct media_type *);
 void            media_purge(struct mediatypes *);
 struct media_type *
-                media_find(struct mediatypes *, char *);
+                media_find(struct mediatypes *, const char *);
+struct media_type *
+                media_find_config(struct httpd *, struct server_config *,
+                   const char *);
 int             media_cmp(struct media_type *, struct media_type *);
 RB_PROTOTYPE(kvtree, kv, kv_node, kv_cmp);
 RB_PROTOTYPE(mediatypes, media_type, media_entry, media_cmp);
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.70
diff -u -p -u -p -r1.70 parse.y
--- parse.y     16 Jul 2015 19:05:28 -0000      1.70
+++ parse.y     18 Jul 2015 00:54:45 -0000
@@ -133,8 +133,8 @@ typedef struct {
 %token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY LISTEN
 %token LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT PREFORK PROTOCOLS
 %token REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TIMEOUT
-%token TLS TYPES
-%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
+%token TLS TYPE TYPES
+%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS DEFAULT
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
@@ -198,6 +198,10 @@ main               : PREFORK NUMBER        {
                | LOGDIR STRING         {
                        conf->sc_logdir = $2;
                }
+               | DEFAULT TYPE mediastring      {
+                       memcpy(&conf->sc_default_type, &media,
+                           sizeof(struct media_type));
+               }
                ;
 
 server         : SERVER optmatch STRING        {
@@ -555,6 +559,11 @@ serveroptsl        : LISTEN ON STRING opttls po
                        srv_conf = &parentsrv->srv_conf;
                        parentsrv = NULL;
                }
+               | DEFAULT TYPE mediastring      {
+                       srv_conf->flags |= SRVFLAG_DEFAULT_TYPE;
+                       memcpy(&srv_conf->default_type, &media,
+                           sizeof(struct media_type));
+               }
                | include
                ;
 
@@ -961,7 +970,11 @@ mediaopts_l        : mediaopts_l mediaoptsl nl
                | mediaoptsl nl
                ;
 
-mediaoptsl     : STRING '/' STRING     {
+mediaoptsl     : mediastring medianames_l optsemicolon
+               | include
+               ;
+
+mediastring    : STRING '/' STRING     {
                        if (strlcpy(media.media_type, $1,
                            sizeof(media.media_type)) >=
                            sizeof(media.media_type) ||
@@ -975,8 +988,7 @@ mediaoptsl  : STRING '/' STRING     {
                        }
                        free($1);
                        free($3);
-               } medianames_l optsemicolon
-               | include
+               }
                ;
 
 medianames_l   : medianames_l medianamesl
@@ -1109,6 +1121,7 @@ lookup(char *s)
                { "combined",           COMBINED },
                { "common",             COMMON },
                { "connection",         CONNECTION },
+               { "default",            DEFAULT },
                { "dhe",                DHE },
                { "directory",          DIRECTORY },
                { "drop",               DROP },
@@ -1145,6 +1158,7 @@ lookup(char *s)
                { "tcp",                TCP },
                { "timeout",            TIMEOUT },
                { "tls",                TLS },
+               { "type",               TYPE },
                { "types",              TYPES },
                { "with",               WITH }
        };
@@ -1472,13 +1486,17 @@ popfile(void)
 int
 parse_config(const char *filename, struct httpd *x_conf)
 {
-       struct sym      *sym, *next;
+       struct sym              *sym, *next;
+       struct media_type        dflt = HTTPD_DEFAULT_TYPE;
 
        conf = x_conf;
        if (config_init(conf) == -1) {
                log_warn("%s: cannot initialize configuration", __func__);
                return (-1);
        }
+
+       /* Set default media type */
+       memcpy(&conf->sc_default_type, &dflt, sizeof(struct media_type));
 
        errors = 0;
 
Index: server_file.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_file.c,v
retrieving revision 1.56
diff -u -p -u -p -r1.56 server_file.c
--- server_file.c       17 Jul 2015 21:53:57 -0000      1.56
+++ server_file.c       18 Jul 2015 00:54:45 -0000
@@ -238,7 +238,7 @@ server_file_request(struct httpd *env, s
        if ((fd = open(path, O_RDONLY)) == -1)
                goto abort;
 
-       media = media_find(env->sc_mediatypes, path);
+       media = media_find_config(env, srv_conf, path);
        ret = server_response_http(clt, 200, media, st->st_size,
            MINIMUM(time(NULL), st->st_mtim.tv_sec));
        switch (ret) {
@@ -290,6 +290,7 @@ int
 server_partial_file_request(struct httpd *env, struct client *clt, char *path,
     struct stat *st, char *range_str)
 {
+       struct server_config    *srv_conf = clt->clt_srv_conf;
        struct http_descriptor  *resp = clt->clt_descresp;
        struct http_descriptor  *desc = clt->clt_descreq;
        struct media_type       *media, multipart_media;
@@ -317,7 +318,7 @@ server_partial_file_request(struct httpd
        if ((fd = open(path, O_RDONLY)) == -1)
                goto abort;
 
-       media = media_find(env->sc_mediatypes, path);
+       media = media_find_config(env, srv_conf, path);
        if ((evb = evbuffer_new()) == NULL) {
                errstr = "failed to allocate file buffer";
                goto abort;
@@ -347,9 +348,7 @@ server_partial_file_request(struct httpd
                        content_length += i;
                        if ((i = evbuffer_add_printf(evb,
                            "Content-Type: %s/%s\r\n",
-                           media == NULL ? "application" : media->media_type,
-                           media == NULL ?
-                           "octet-stream" : media->media_subtype)) == -1)
+                           media->media_type, media->media_subtype)) == -1)
                                goto abort;
 
                        content_length += i;
@@ -542,7 +541,7 @@ server_file_index(struct httpd *env, str
        close(fd);
        fd = -1;
 
-       media = media_find(env->sc_mediatypes, "index.html");
+       media = media_find_config(env, srv_conf, "index.html");
        ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb),
            dir_mtime);
        switch (ret) {
Index: server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.89
diff -u -p -u -p -r1.89 server_http.c
--- server_http.c       16 Jul 2015 19:05:28 -0000      1.89
+++ server_http.c       18 Jul 2015 00:54:45 -0000
@@ -1216,7 +1216,8 @@ server_response_http(struct client *clt,
        struct kv               *ct, *cl;
        char                     tmbuf[32];
 
-       if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
+       if (desc == NULL || media == NULL ||
+           (error = server_httperror_byid(code)) == NULL)
                return (-1);
 
        if (server_log_http(clt, code, size) == -1)
@@ -1241,9 +1242,7 @@ server_response_http(struct client *clt,
 
        /* Set media type */
        if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL ||
-           kv_set(ct, "%s/%s",
-           media == NULL ? "application" : media->media_type,
-           media == NULL ? "octet-stream" : media->media_subtype) == -1)
+           kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1)
                return (-1);
 
        /* Set content length, if specified */

Reply via email to