compatible to nginx' mime.types file which can be included directly.
If not present, use a few built-in defaults for html, css, txt, jpeg,
gif, png, and js.
-/* $OpenBSD: config.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
env->sc_prefork_server = SERVER_NUMPROC;
ps->ps_what[PROC_PARENT] = CONFIG_ALL;
- ps->ps_what[PROC_SERVER] = CONFIG_SERVERS;
+ ps->ps_what[PROC_SERVER] = CONFIG_SERVERS|CONFIG_MEDIA;
}
/* Other configuration */
TAILQ_INIT(env->sc_servers);
}
+ if (what & CONFIG_MEDIA) {
+ if ((env->sc_mediatypes =
+ calloc(1, sizeof(*env->sc_mediatypes))) == NULL)
+ return (-1);
+ RB_INIT(env->sc_mediatypes);
+ }
+
return (0);
}
free(srv);
}
}
+
+ if (what & CONFIG_MEDIA && env->sc_mediatypes != NULL)
+ media_purge(env->sc_mediatypes);
}
int
return (0);
}
+
+int
+config_setmedia(struct httpd *env, struct media_type *media)
+{
+ struct privsep *ps = env->sc_ps;
+ int id;
+ u_int what;
+
+ for (id = 0; id < PROC_MAX; id++) {
+ what = ps->ps_what[id];
+
+ if ((what & CONFIG_MEDIA) == 0 || id == privsep_process)
+ continue;
+
+ DPRINTF("%s: sending media \"%s\" to %s", __func__,
+ media->media_name, ps->ps_title[id]);
+
+ proc_compose_imsg(ps, id, -1, IMSG_CFG_MEDIA, -1,
+ media, sizeof(*media));
+ }
+
+ return (0);
+}
+
+int
+config_getmedia(struct httpd *env, struct imsg *imsg)
+{
+#ifdef DEBUG
+ struct privsep *ps = env->sc_ps;
+#endif
+ struct media_type media;
+ u_int8_t *p = imsg->data;
+
+ IMSG_SIZE_CHECK(imsg, &media);
+ memcpy(&media, p, sizeof(media));
+
+ if (media_add(env->sc_mediatypes, &media) == NULL) {
+ log_debug("%s: failed to add media \"%s\"",
+ __func__, media.media_name);
+ return (-1);
+ }
+
+ DPRINTF("%s: %s %d received media \"%s\"", __func__,
+ ps->ps_title[privsep_process], ps->ps_instance,
+ media.media_name);
+
+ return (0);
+}
-/* $OpenBSD: http.h,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: http.h,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2012 - 2014 Reyk Floeter <reyk@openbsd.org>
{ 0, NULL } \
}
+struct http_mediatype {
+ char *media_name;
+ char *media_type;
+ char *media_subtype;
+};
+/* Some default media types */
+#define MEDIA_TYPES { \
+ { "css", "text", "css" }, \
+ { "html", "text", "html" }, \
+ { "txt", "text", "plain" }, \
+ { "gif", "image", "gif" }, \
+ { "jpeg", "image", "jpeg" }, \
+ { "jpg", "image", "jpeg" }, \
+ { "png", "image", "png" }, \
+ { "js", "application", "javascript" }, \
+ { NULL } \
+}
+
/* Used during runtime */
struct http_descriptor {
struct kv http_pathquery;
-/* $OpenBSD: httpd.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: httpd.c,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
struct ctl_flags cf;
int ret = -1;
struct server *srv;
+ struct media_type *media;
+
+ RB_FOREACH(media, mediatypes, env->sc_mediatypes) {
+ if (config_setmedia(env, media) == -1)
+ fatal("send media");
+ }
TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
if (config_setserver(env, srv) == -1)
- fatal("create server");
+ fatal("send server");
}
/* The servers need to reload their config. */
ret = 0;
- config_purge(env, CONFIG_ALL & ~CONFIG_SERVERS);
+ config_purge(env, CONFIG_ALL);
return (ret);
}
}
RB_GENERATE(kvtree, kv, kv_node, kv_cmp);
+
+struct media_type *
+media_add(struct mediatypes *types, struct media_type *media)
+{
+ struct media_type *entry;
+
+ if ((entry = RB_FIND(mediatypes, types, media)) != NULL) {
+ log_debug("%s: duplicated entry for \"%s\"", __func__,
+ media->media_name);
+ return (NULL);
+ }
+
+ if ((entry = malloc(sizeof(*media))) == NULL)
+ return (NULL);
+
+ memcpy(entry, media, sizeof(*entry));
+ RB_INSERT(mediatypes, types, entry);
+
+ return (entry);
+}
+
+void
+media_delete(struct mediatypes *types, struct media_type *media)
+{
+ RB_REMOVE(mediatypes, types, media);
+ if (media->media_encoding != NULL)
+ free(media->media_encoding);
+ free(media);
+}
+
+void
+media_purge(struct mediatypes *types)
+{
+ struct media_type *media;
+
+ while ((media = RB_MIN(mediatypes, types)) != NULL)
+ media_delete(types, media);
+}
+
+struct media_type *
+media_find(struct mediatypes *types, char *file)
+{
+ struct media_type *match, media;
+ char *p;
+
+ if ((p = strrchr(file, '.')) == NULL) {
+ p = file;
+ } else if (*p++ == '\0') {
+ return (NULL);
+ }
+ if (strlcpy(media.media_name, p,
+ sizeof(media.media_name)) >=
+ sizeof(media.media_name)) {
+ return (NULL);
+ }
+
+ /* Find media type by extension name */
+ match = RB_FIND(mediatypes, types, &media);
+
+ return (match);
+}
+
+int
+media_cmp(struct media_type *a, struct media_type *b)
+{
+ return (strcasecmp(a->media_name, b->media_name));
+}
+
+RB_GENERATE(mediatypes, media_type, media_entry, media_cmp);
-.\" $OpenBSD: httpd.conf.5,v 1.1 2014/07/12 23:34:54 reyk Exp $
+.\" $OpenBSD: httpd.conf.5,v 1.2 2014/07/13 14:17:37 reyk Exp $
.\"
.\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
.\"
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: July 12 2014 $
+.Dd $Mdocdate: July 13 2014 $
.Dt HTTPD.CONF 5
.Os
.Sh NAME
.Xr httpd 8 .
.Sh SECTIONS
.Nm
-is divided into three main sections:
+is divided into four main sections:
.Bl -tag -width xxxx
.It Sy Macros
User-defined variables may be defined and used later, simplifying the
.Xr httpd 8 .
.It Sy Servers
Listening HTTP web servers.
+.It Sy Types
+Media types and extensions.
.El
.Pp
Within the sections,
.It Ic listen on Ar address Ic port Ar number
Set the listen address and port.
.El
+.Sh TYPES
+Configure the supported media types.
+.Nm httpd
+will set the
+.Ar Content-Type
+of the response header based on the file extension that is listed in the
+.Ic types
+section.
+If not specified,
+.Nm httpd
+will use built-in media types for
+.Ar text/css ,
+.Ar text/html ,
+.Ar text/plain ,
+.Ar image/gif ,
+.Ar image/png ,
+.Ar image/jpeg ,
+and
+.Ar application/javascript .
+.Pp
+The
+.Ic types
+section must include one or more lines of the following syntax:
+.Bl -tag -width Ds
+.It Ar type/subtype Ar name Oo Ar name ... Oc Ic ;
+Set the media
+.Ar type
+and
+.Ar subtype
+to the specified extension
+.Ar name .
+One or more names can be specified per line.
+.El
+.Sh EAMPLES
+The following example will start two pre-forked servers that are
+listening on the primary IP address of the network interface that is a
+member of the
+.Ar egress
+group.
+It additionally defines some media types overriding the defaults.
+.Bd -literal -offset indent
+prefork 2
+
+server "default" {
+ listen on egress port 80
+}
+
+types {
+ text/css css;
+ text/html html html;
+ text/txt txt;
+ image/gif gif;
+ image/jpeg jpg jpeg;
+ image/png png;
+ application/javascript js;
+ application/xml xml;
+}
+.Ed
+.Pp
+The syntax of the types section is compatible to the format that is used by
+.Xr nginx 8 ,
+so you can optionally include its
+.Pa mime.types
+file directly:
+.Bd -literal -offset indent
+include "/etc/nginx/mime.types"
+.Ed
.Sh SEE ALSO
.Xr httpd 8 .
.Sh AUTHORS
-/* $OpenBSD: httpd.h,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
#define SERVER_BACKLOG 10
#define SERVER_OUTOF_FD_RETRIES 5
+#define MEDIATYPE_NAMEMAX 128 /* file name extension */
+#define MEDIATYPE_TYPEMAX 64 /* length of type/subtype */
+
#define CONFIG_RELOAD 0x00
-#define CONFIG_SERVERS 0x01
+#define CONFIG_MEDIA 0x01
+#define CONFIG_SERVERS 0x02
#define CONFIG_ALL 0xff
#define TCPFLAG_NODELAY 0x01
IMSG_CTL_END,
IMSG_CTL_START,
IMSG_CFG_SERVER,
+ IMSG_CFG_MEDIA,
IMSG_CFG_DONE
};
};
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;
u_int16_t sc_id;
struct serverlist *sc_servers;
+ struct mediatypes *sc_mediatypes;
struct privsep *sc_ps;
int sc_reload;
void server_close_http(struct client *);
/* server_file.c */
-int server_response(struct client *);
+int server_response(struct httpd *, struct client *);
/* httpd.c */
void event_again(struct event *, int, short,
int kv_log(struct evbuffer *, struct kv *);
struct kv *kv_find(struct kvtree *, struct kv *);
int kv_cmp(struct kv *, struct kv *);
+struct media_type
+ *media_add(struct mediatypes *, struct media_type *);
+void media_delete(struct mediatypes *, struct media_type *);
+void media_purge(struct mediatypes *);
+struct media_type *
+ media_find(struct mediatypes *, 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);
/* log.c */
void log_init(int);
int config_getcfg(struct httpd *, struct imsg *);
int config_setserver(struct httpd *, struct server *);
int config_getserver(struct httpd *, struct imsg *);
+int config_setmedia(struct httpd *, struct media_type *);
+int config_getmedia(struct httpd *, struct imsg *);
#endif /* _HTTPD_H */
-/* $OpenBSD: parse.y,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: parse.y,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
static struct server *srv = NULL;
struct serverlist servers;
+struct media_type media;
struct address *host_v4(const char *);
struct address *host_v6(const char *);
%}
-%token ALL PORT LISTEN PREFORK SERVER ERROR INCLUDE LOG VERBOSE ON
+%token ALL PORT LISTEN PREFORK SERVER ERROR INCLUDE LOG VERBOSE ON TYPES
%token UPDATES INCLUDE
%token <v.string> STRING
%token <v.number> NUMBER
| grammar varset '\n'
| grammar main '\n'
| grammar server '\n'
+ | grammar types '\n'
| grammar error '\n' { file->errors++; }
;
}
;
+types : TYPES '{' optnl mediaopts_l '}'
+ ;
+
+mediaopts_l : mediaopts_l mediaoptsl nl
+ | mediaoptsl optnl
+ ;
+
+mediaoptsl : STRING '/' STRING {
+ if (strlcpy(media.media_type, $1,
+ sizeof(media.media_type)) >=
+ sizeof(media.media_type) ||
+ strlcpy(media.media_subtype, $3,
+ sizeof(media.media_subtype)) >=
+ sizeof(media.media_subtype)) {
+ yyerror("media type too long");
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ free($1);
+ free($3);
+ } medianames_l ';'
+ ;
+
+medianames_l : medianames_l medianamesl
+ | medianamesl
+ ;
+
+medianamesl : STRING {
+ if (strlcpy(media.media_name, $1,
+ sizeof(media.media_name)) >=
+ sizeof(media.media_name)) {
+ yyerror("media name too long");
+ free($1);
+ YYERROR;
+ }
+ free($1);
+
+ if (media_add(conf->sc_mediatypes, &media) == NULL) {
+ yyerror("failed to add media type");
+ YYERROR;
+ }
+ }
+ ;
+
port : PORT STRING {
char *a, *b;
int p[2];
| ALL { $$ = HTTPD_OPT_LOGALL; }
;
-comma : ','
- | nl
- | /* empty */
- ;
-
optnl : '\n' optnl
|
;
{ "port", PORT },
{ "prefork", PREFORK },
{ "server", SERVER },
+ { "types", TYPES },
{ "updates", UPDATES }
};
const struct keywords *p;
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
x != '{' && x != '}' && x != '<' && x != '>' && \
x != '!' && x != '=' && x != '#' && \
- x != ',' && x != '/'))
+ x != ',' && x != ';' && x != '/'))
if (isalnum(c) || c == ':' || c == '_') {
do {
load_config(const char *filename, struct httpd *x_conf)
{
struct sym *sym, *next;
+ struct http_mediatype mediatypes[] = MEDIA_TYPES;
+ struct media_type m;
+ int i;
conf = x_conf;
conf->sc_flags = 0;
errors++;
}
+ if (RB_EMPTY(conf->sc_mediatypes)) {
+ /* Add default media types */
+ for (i = 0; mediatypes[i].media_name != NULL; i++) {
+ (void)strlcpy(m.media_name, mediatypes[i].media_name,
+ sizeof(m.media_name));
+ (void)strlcpy(m.media_type, mediatypes[i].media_type,
+ sizeof(m.media_type));
+ (void)strlcpy(m.media_subtype,
+ mediatypes[i].media_subtype,
+ sizeof(m.media_subtype));
+
+ if (media_add(conf->sc_mediatypes, &m) == NULL) {
+ log_warnx("failed to add default media \"%s\"",
+ m.media_name);
+ errors++;
+ }
+ }
+ }
+
return (errors ? -1 : 0);
}
-/* $OpenBSD: server.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: server.c,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg->hdr.type) {
+ case IMSG_CFG_MEDIA:
+ config_getmedia(env, imsg);
+ break;
case IMSG_CFG_SERVER:
config_getserver(env, imsg);
break;
-/* $OpenBSD: server_file.c,v 1.1 2014/07/12 23:34:54 reyk Exp $ */
+/* $OpenBSD: server_file.c,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
#include "http.h"
int
-server_response(struct client *clt)
+server_response(struct httpd *env, struct client *clt)
{
struct http_descriptor *desc = clt->clt_desc;
struct server *srv = clt->clt_server;
- struct kv *kv;
+ struct kv *ct, *cl;
+ struct media_type *media;
const char *errstr = NULL;
int fd = -1;
char path[MAXPATHLEN];
if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1)
goto fail;
- /* XXX verify results XXX */
kv_purge(&desc->http_headers);
- kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME);
- kv_add(&desc->http_headers, "Connection", "close");
- if ((kv = kv_add(&desc->http_headers, "Content-Length", NULL)) != NULL)
- kv_set(kv, "%ld", st.st_size);
- kv_setkey(&desc->http_pathquery, "200");
- kv_set(&desc->http_pathquery, "%s", server_httperror_byid(200));
+
+ /* Add error codes */
+ if (kv_setkey(&desc->http_pathquery, "200") == -1 ||
+ kv_set(&desc->http_pathquery, "%s",
+ server_httperror_byid(200)) == -1)
+ goto fail;
+
+ /* Add headers */
+ if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL ||
+ kv_add(&desc->http_headers, "Connection", "close") == NULL ||
+ (ct = kv_add(&desc->http_headers, "Content-Type", NULL)) == NULL ||
+ (cl = kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL)
+ goto fail;
+
+ /* Set content type */
+ media = media_find(env->sc_mediatypes, path);
+ if (kv_set(ct, "%s/%s",
+ media == NULL ? "application" : media->media_type,
+ media == NULL ? "octet-stream" : media->media_subtype) == -1)
+ goto fail;
+
+ /* Set content length */
+ if (kv_set(cl, "%ld", st.st_size) == -1)
+ goto fail;
if (server_writeresponse_http(clt) == -1 ||
server_bufferevent_print(clt, "\r\n") == -1 ||
-/* $OpenBSD: server_http.c,v 1.3 2014/07/13 09:46:19 beck Exp $ */
+/* $OpenBSD: server_http.c,v 1.4 2014/07/13 14:17:37 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
done:
if (clt->clt_toread <= 0) {
- if (server_response(clt) == -1)
+ if (server_response(env, clt) == -1)
return;
}