[on behalf of reyk]

Many people want to test the new httpd in OpenBSD 5.6; so we decided
to provide various improvements from -current for 5.6.
See the description below for more details.


untrusted comment: signature from openbsd 5.6 base private key
RWR0EANmo9nqhn3Gnfk2/2x+xII6do92zreKp/t5zOwfkVgsQAI4ZCPkWAazbbnWNV7Ptkle876f/kb6C2KuvnTqvwUItsyvogA=

OpenBSD 5.6 errata 9, Nov 18, 2014:  httpd was developed very rapidly
in the weeks before 5.6 release, and it has a few flaws.  It would be
nice to get these flaws fully remediated before the next release, and
that requires the community to want to use it.  Therefore here is a
"jumbo" patch that brings in the most important fixes.

Apply patch using:

    signify -Vep /etc/signify/openbsd-56-base.pub -x 009_httpd.patch.sig \
        -m - | (cd /usr/src && patch -p0)

Then build and install httpd:

    cd /usr/src/usr.sbin/httpd
    make obj
    make
    make install

Index: usr.sbin/httpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.21
diff -u -p -r1.21 config.c
--- usr.sbin/httpd/config.c     6 Aug 2014 18:21:14 -0000       1.21
+++ usr.sbin/httpd/config.c     18 Nov 2014 15:02:54 -0000
@@ -223,7 +223,7 @@ config_getserver_config(struct httpd *en
 #ifdef DEBUG
        struct privsep          *ps = env->sc_ps;
 #endif
-       struct server_config    *srv_conf;
+       struct server_config    *srv_conf, *parent;
        u_int8_t                *p = imsg->data;
        u_int                    f;
 
@@ -233,18 +233,28 @@ config_getserver_config(struct httpd *en
        IMSG_SIZE_CHECK(imsg, srv_conf);
        memcpy(srv_conf, p, sizeof(*srv_conf));
 
+       /* Reset these variables to avoid free'ing invalid pointers */
+       serverconfig_reset(srv_conf);
+
+       TAILQ_FOREACH(parent, &srv->srv_hosts, entry) {
+               if (strcmp(parent->name, srv_conf->name) == 0)
+                       break;
+       }
+       if (parent == NULL)
+               parent = &srv->srv_conf;
+
        if (srv_conf->flags & SRVFLAG_LOCATION) {
                /* Inherit configuration from the parent */
                f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX;
                if ((srv_conf->flags & f) == 0) {
-                       srv_conf->flags |= srv->srv_conf.flags & f;
-                       (void)strlcpy(srv_conf->index, srv->srv_conf.index,
+                       srv_conf->flags |= parent->flags & f;
+                       (void)strlcpy(srv_conf->index, parent->index,
                            sizeof(srv_conf->index));
                }
 
                f = SRVFLAG_AUTO_INDEX|SRVFLAG_NO_AUTO_INDEX;
                if ((srv_conf->flags & f) == 0)
-                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       srv_conf->flags |= parent->flags & f;
 
                f = SRVFLAG_SOCKET|SRVFLAG_FCGI;
                if ((srv_conf->flags & f) == SRVFLAG_FCGI) {
@@ -255,48 +265,48 @@ config_getserver_config(struct httpd *en
 
                f = SRVFLAG_ROOT;
                if ((srv_conf->flags & f) == 0) {
-                       srv_conf->flags |= srv->srv_conf.flags & f;
-                       (void)strlcpy(srv_conf->root, srv->srv_conf.root,
+                       srv_conf->flags |= parent->flags & f;
+                       (void)strlcpy(srv_conf->root, parent->root,
                            sizeof(srv_conf->root));
                }
 
                f = SRVFLAG_FCGI|SRVFLAG_NO_FCGI;
                if ((srv_conf->flags & f) == 0)
-                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       srv_conf->flags |= parent->flags & f;
 
                f = SRVFLAG_LOG|SRVFLAG_NO_LOG;
                if ((srv_conf->flags & f) == 0) {
-                       srv_conf->flags |= srv->srv_conf.flags & f;
-                       srv_conf->logformat = srv->srv_conf.logformat;
+                       srv_conf->flags |= parent->flags & f;
+                       srv_conf->logformat = parent->logformat;
                }
 
                f = SRVFLAG_SYSLOG|SRVFLAG_NO_SYSLOG;
                if ((srv_conf->flags & f) == 0)
-                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       srv_conf->flags |= parent->flags & f;
 
                f = SRVFLAG_SSL;
-               srv_conf->flags |= srv->srv_conf.flags & f;
+               srv_conf->flags |= parent->flags & f;
 
                f = SRVFLAG_ACCESS_LOG;
                if ((srv_conf->flags & f) == 0) {
-                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       srv_conf->flags |= parent->flags & f;
                        (void)strlcpy(srv_conf->accesslog,
-                           srv->srv_conf.accesslog,
+                           parent->accesslog,
                            sizeof(srv_conf->accesslog));
                }
 
                f = SRVFLAG_ERROR_LOG;
                if ((srv_conf->flags & f) == 0) {
-                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       srv_conf->flags |= parent->flags & f;
                        (void)strlcpy(srv_conf->errorlog,
-                           srv->srv_conf.errorlog,
+                           parent->errorlog,
                            sizeof(srv_conf->errorlog));
                }
 
-               memcpy(&srv_conf->timeout, &srv->srv_conf.timeout,
+               memcpy(&srv_conf->timeout, &parent->timeout,
                    sizeof(srv_conf->timeout));
-               srv_conf->maxrequests = srv->srv_conf.maxrequests;
-               srv_conf->maxrequestbody = srv->srv_conf.maxrequestbody;
+               srv_conf->maxrequests = parent->maxrequests;
+               srv_conf->maxrequestbody = parent->maxrequestbody;
 
                DPRINTF("%s: %s %d location \"%s\", "
                    "parent \"%s\", flags: %s",
@@ -330,6 +340,9 @@ config_getserver(struct httpd *env, stru
        IMSG_SIZE_CHECK(imsg, &srv_conf);
        memcpy(&srv_conf, p, sizeof(srv_conf));
        s = sizeof(srv_conf);
+
+       /* Reset these variables to avoid free'ing invalid pointers */
+       serverconfig_reset(&srv_conf);
 
        if ((u_int)(IMSG_DATA_SIZE(imsg) - s) <
            (srv_conf.ssl_cert_len + srv_conf.ssl_key_len)) {
Index: usr.sbin/httpd/http.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/http.h,v
retrieving revision 1.5
diff -u -p -r1.5 http.h
--- usr.sbin/httpd/http.h       3 Aug 2014 21:33:27 -0000       1.5
+++ usr.sbin/httpd/http.h       18 Nov 2014 15:02:54 -0000
@@ -44,6 +44,32 @@ enum httpmethod {
        HTTP_METHOD_LOCK,
        HTTP_METHOD_UNLOCK,
 
+       /* WebDAV Versioning Extension, RFC 3253 */
+       HTTP_METHOD_VERSION_CONTROL,
+       HTTP_METHOD_REPORT,
+       HTTP_METHOD_CHECKOUT,
+       HTTP_METHOD_CHECKIN,
+       HTTP_METHOD_UNCHECKOUT,
+       HTTP_METHOD_MKWORKSPACE,
+       HTTP_METHOD_UPDATE,
+       HTTP_METHOD_LABEL,
+       HTTP_METHOD_MERGE,
+       HTTP_METHOD_BASELINE_CONTROL,
+       HTTP_METHOD_MKACTIVITY,
+
+       /* WebDAV Ordered Collections, RFC 3648 */
+       HTTP_METHOD_ORDERPATCH,
+
+       /* WebDAV Access Control, RFC 3744 */
+       HTTP_METHOD_ACL,
+
+       /* WebDAV Redirect Reference Resources, RFC 4437 */
+       HTTP_METHOD_MKREDIRECTREF,
+       HTTP_METHOD_UPDATEREDIRECTREF,
+
+       /* WebDAV Search, RFC 5323 */
+       HTTP_METHOD_SEARCH,
+
        /* PATCH, RFC 5789 */
        HTTP_METHOD_PATCH,
 
@@ -71,6 +97,22 @@ struct http_method {
        { HTTP_METHOD_MOVE,             "MOVE" },       \
        { HTTP_METHOD_LOCK,             "LOCK" },       \
        { HTTP_METHOD_UNLOCK,           "UNLOCK" },     \
+       { HTTP_METHOD_VERSION_CONTROL,  "VERSION-CONTROL" }, \
+       { HTTP_METHOD_REPORT,           "REPORT" },     \
+       { HTTP_METHOD_CHECKOUT,         "CHECKOUT" },   \
+       { HTTP_METHOD_CHECKIN,          "CHECKIN" },    \
+       { HTTP_METHOD_UNCHECKOUT,       "UNCHECKOUT" }, \
+       { HTTP_METHOD_MKWORKSPACE,      "MKWORKSPACE" }, \
+       { HTTP_METHOD_UPDATE,           "UPDATE" },     \
+       { HTTP_METHOD_LABEL,            "LABEL" },      \
+       { HTTP_METHOD_MERGE,            "MERGE" },      \
+       { HTTP_METHOD_BASELINE_CONTROL, "BASELINE-CONTROL" }, \
+       { HTTP_METHOD_MKACTIVITY,       "MKACTIVITY" }, \
+       { HTTP_METHOD_ORDERPATCH,       "ORDERPATCH" }, \
+       { HTTP_METHOD_ACL,              "ACL" },        \
+       { HTTP_METHOD_MKREDIRECTREF,    "MKREDIRECTREF" }, \
+       { HTTP_METHOD_UPDATEREDIRECTREF, "UPDATEREDIRECTREF" }, \
+       { HTTP_METHOD_SEARCH,           "SEARCH" },     \
        { HTTP_METHOD_PATCH,            "PATCH" },      \
        { HTTP_METHOD_NONE,             NULL }          \
 }
@@ -155,6 +197,9 @@ struct http_descriptor {
        enum httpmethod          http_method;
        int                      http_chunked;
        char                    *http_version;
+
+       /* Rewritten path remains NULL if not used */
+       char                    *http_path_alias;
 
        /* A tree of headers and attached lists for repeated headers. */
        struct kv               *http_lastheader;
Index: usr.sbin/httpd/httpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.c,v
retrieving revision 1.17
diff -u -p -r1.17 httpd.c
--- usr.sbin/httpd/httpd.c      5 Aug 2014 15:36:59 -0000       1.17
+++ usr.sbin/httpd/httpd.c      18 Nov 2014 15:02:54 -0000
@@ -289,10 +289,20 @@ parent_configure(struct httpd *env)
                        fatal("send media");
        }
 
+       /* First send the servers... */
        TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+               if (srv->srv_conf.flags & SRVFLAG_LOCATION)
+                       continue;
                if (config_setserver(env, srv) == -1)
                        fatal("send server");
        }
+       /* ...and now send the locations */
+       TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+               if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0)
+                       continue;
+               if (config_setserver(env, srv) == -1)
+                       fatal("send location");
+       }
 
        /* The servers need to reload their config. */
        env->sc_reload = env->sc_prefork_server + 1;
@@ -526,6 +536,46 @@ canonicalize_host(const char *host, char
 }
 
 const char *
+url_decode(char *url)
+{
+       char    *p, *q;
+       char     hex[3];
+       u_long   x;
+
+       hex[2] = '\0';
+       p = q = url;
+
+       while (*p != '\0') {
+               switch (*p) {
+               case '%':
+                       /* Encoding character is followed by two hex chars */
+                       if (!(isxdigit(p[1]) && isxdigit(p[2])))
+                               return (NULL);
+
+                       hex[0] = p[1];
+                       hex[1] = p[2];
+
+                       /*
+                        * We don't have to validate "hex" because it is
+                        * guaranteed to include two hex chars followed by nul.
+                        */
+                       x = strtoul(hex, NULL, 16);             
+                       *q = (char)x;
+                       p += 2;
+                       break;
+               default:
+                       *q = *p;
+                       break;
+               }
+               p++;
+               q++;
+       }
+       *q = '\0';
+
+       return(url);
+}
+
+const char *
 canonicalize_path(const char *input, char *path, size_t len)
 {
        const char      *i;
@@ -580,28 +630,33 @@ canonicalize_path(const char *input, cha
        return (path);
 }
 
-ssize_t
-path_info(char *name)
+size_t
+path_info(char *path)
 {
-       char            *p, *start, *end;
-       char             path[MAXPATHLEN];
+       char            *p, *start, *end, ch;
        struct stat      st;
-
-       if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
-               return (-1);
+       int              ret;
 
        start = path;
        end = start + strlen(path);
 
        for (p = end; p > start; p--) {
-               if (*p != '/')
+               /* Scan every path component from the end and at each '/' */
+               if (p < end && *p != '/')
                        continue;
-               if (stat(path, &st) == 0)
-                       break;
+
+               /* Temporarily cut the path component out */
+               ch = *p;
                *p = '\0';
+               ret = stat(path, &st);
+               *p = ch;
+
+               /* Break if the initial path component was found */
+               if (ret == 0)
+                       break;
        }
 
-       return (strlen(path));
+       return (p - start);
 }
 
 void
@@ -623,6 +678,40 @@ socket_rlimit(int maxfd)
                rl.rlim_cur = MAX(rl.rlim_max, (rlim_t)maxfd);
        if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
                fatal("socket_rlimit: failed to set resource limit");
+}
+
+char *
+evbuffer_getline(struct evbuffer *evb)
+{
+       u_int8_t        *ptr = EVBUFFER_DATA(evb);
+       size_t           len = EVBUFFER_LENGTH(evb);
+       char            *str;
+       u_int            i;
+
+       /* Safe version of evbuffer_readline() */
+       if ((str = get_string(ptr, len)) == NULL)
+               return (NULL);
+
+       for (i = 0; str[i] != '\0'; i++) {
+               if (str[i] == '\r' || str[i] == '\n')
+                       break;
+       }
+
+       if (i == len) {
+               free(str);
+               return (NULL);
+       }
+
+       str[i] = '\0';
+
+       if ((i + 1) < len) {
+               if (ptr[i] == '\r' && ptr[i + 1] == '\n')
+                       i++;
+       }
+
+       evbuffer_drain(evb, ++i);
+
+       return (str);
 }
 
 char *
Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.51
diff -u -p -r1.51 httpd.h
--- usr.sbin/httpd/httpd.h      6 Aug 2014 18:21:14 -0000       1.51
+++ usr.sbin/httpd/httpd.h      18 Nov 2014 15:02:54 -0000
@@ -276,7 +276,8 @@ struct client {
        size_t                   clt_buflen;
        struct evbuffer         *clt_output;
        struct event             clt_ev;
-       void                    *clt_desc;
+       void                    *clt_descreq;
+       void                    *clt_descresp;
        int                      clt_sndbufsiz;
 
        int                      clt_fd;
@@ -294,6 +295,8 @@ struct client {
        int                      clt_fcgi_toread;
        int                      clt_fcgi_padding_len;
        int                      clt_fcgi_type;
+       int                      clt_fcgi_chunked;
+       int                      clt_fcgi_end;
        struct evbuffer         *clt_srvevb;
 
        struct evbuffer         *clt_log;
@@ -463,6 +466,8 @@ pid_t        server(struct privsep *, struct p
 int     server_ssl_load_keypair(struct server *);
 int     server_privinit(struct server *);
 void    server_purge(struct server *);
+void    serverconfig_free(struct server_config *);
+void    serverconfig_reset(struct server_config *);
 int     server_socket_af(struct sockaddr_storage *, in_port_t);
 in_port_t
         server_socket_getport(struct sockaddr_storage *);
@@ -477,6 +482,8 @@ void         server_sendlog(struct server_confi
 void    server_close(struct client *, const char *);
 void    server_dump(struct client *, const void *, size_t);
 int     server_client_cmp(struct client *, struct client *);
+int     server_bufferevent_printf(struct client *, const char *, ...)
+           __attribute__((__format__ (printf, 2, 3)));
 int     server_bufferevent_print(struct client *, const char *);
 int     server_bufferevent_write_buffer(struct client *,
            struct evbuffer *);
@@ -508,17 +515,20 @@ const char
 void    server_read_httpcontent(struct bufferevent *, void *);
 void    server_read_httpchunks(struct bufferevent *, void *);
 int     server_writeheader_http(struct client *clt, struct kv *, void *);
-int     server_headers(struct client *,
+int     server_headers(struct client *, void *,
            int (*)(struct client *, struct kv *, void *), void *);
 int     server_writeresponse_http(struct client *);
 int     server_response_http(struct client *, u_int, struct media_type *,
-           size_t);
+           size_t, time_t);
 void    server_reset_http(struct client *);
 void    server_close_http(struct client *);
 int     server_response(struct httpd *, struct client *);
+struct server_config *
+        server_getlocation(struct client *, const char *);
 const char *
         server_http_host(struct sockaddr_storage *, char *, size_t);
-void    server_http_date(char *, size_t);
+char   *server_http_parsehost(char *, char *, size_t, int *);
+ssize_t         server_http_time(time_t, char *, size_t);
 int     server_log_http(struct client *, u_int, size_t);
 
 /* server_file.c */
@@ -533,13 +543,15 @@ int        fcgi_add_stdin(struct client *, str
 void            event_again(struct event *, int, short,
                    void (*)(int, short, void *),
                    struct timeval *, struct timeval *, void *);
+const char     *url_decode(char *);
 const char     *canonicalize_host(const char *, char *, size_t);
 const char     *canonicalize_path(const char *, char *, size_t);
-ssize_t                 path_info(char *);
+size_t          path_info(char *);
 void            imsg_event_add(struct imsgev *);
 int             imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
                    pid_t, int, void *, u_int16_t);
 void            socket_rlimit(int);
+char           *evbuffer_getline(struct evbuffer *);
 char           *get_string(u_int8_t *, size_t);
 void           *get_data(u_int8_t *, size_t);
 int             sockaddr_cmp(struct sockaddr *, struct sockaddr *, int);
@@ -575,6 +587,7 @@ void        log_warn(const char *, ...) __attri
 void   log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
 void   log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
 void   log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+void   logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 
3)));
 void   vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 
0)));
 __dead void fatal(const char *);
 __dead void fatalx(const char *);
Index: usr.sbin/httpd/logger.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/logger.c,v
retrieving revision 1.5
diff -u -p -r1.5 logger.c
--- usr.sbin/httpd/logger.c     6 Aug 2014 12:56:58 -0000       1.5
+++ usr.sbin/httpd/logger.c     18 Nov 2014 15:02:55 -0000
@@ -194,6 +194,9 @@ logger_open(struct server *srv, struct s
 {
        struct log_file *log, *logfile = NULL, *errfile = NULL;
 
+       if (srv_conf->flags & SRVFLAG_SYSLOG)
+               return(0);
+
        /* disassociate */
        srv_conf->logaccess = srv_conf->logerror = NULL;
 
Index: usr.sbin/httpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.34
diff -u -p -r1.34 parse.y
--- usr.sbin/httpd/parse.y      6 Aug 2014 20:29:54 -0000       1.34
+++ usr.sbin/httpd/parse.y      18 Nov 2014 15:02:55 -0000
@@ -180,7 +180,7 @@ main                : PREFORK NUMBER        {
                                break;
                        if ($2 <= 0 || $2 > SERVER_MAXPROC) {
                                yyerror("invalid number of preforked "
-                                   "servers: %d", $2);
+                                   "servers: %lld", $2);
                                YYERROR;
                        }
                        conf->sc_prefork_server = $2;
@@ -198,15 +198,6 @@ server             : SERVER STRING         {
                                YYACCEPT;
                        }
 
-                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry)
-                               if (!strcmp(s->srv_conf.name, $2))
-                                       break;
-                       if (s != NULL) {
-                               yyerror("server %s defined twice", $2);
-                               free($2);
-                               YYERROR;
-                       }
-
                        if ((s = calloc(1, sizeof (*s))) == NULL)
                                fatal("out of memory");
 
@@ -252,18 +243,46 @@ server            : SERVER STRING         {
                        srv_conf = &srv->srv_conf;
 
                        SPLAY_INIT(&srv->srv_clients);
-                       TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
                } '{' optnl serveropts_l '}'    {
+                       struct server   *s = NULL;
+
+                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+                               if ((s->srv_conf.flags &
+                                   SRVFLAG_LOCATION) == 0 &&
+                                   strcmp(s->srv_conf.name,
+                                   srv->srv_conf.name) == 0 &&
+                                   s->srv_conf.port == srv->srv_conf.port &&
+                                   sockaddr_cmp(
+                                   (struct sockaddr *)&s->srv_conf.ss,
+                                   (struct sockaddr *)&srv->srv_conf.ss,
+                                   s->srv_conf.prefixlen) == 0)
+                                       break;
+                       }
+                       if (s != NULL) {
+                               yyerror("server \"%s\" defined twice",
+                                   srv->srv_conf.name);
+                               serverconfig_free(srv_conf);
+                               free(srv);
+                               YYABORT;
+                       }
+
                        if (srv->srv_conf.ss.ss_family == AF_UNSPEC) {
                                yyerror("listen address not specified");
-                               free($2);
+                               serverconfig_free(srv_conf);
+                               free(srv);
                                YYERROR;
                        }
+
                        if (server_ssl_load_keypair(srv) == -1) {
                                yyerror("failed to load public/private keys "
                                    "for server %s", srv->srv_conf.name);
+                               serverconfig_free(srv_conf);
+                               free(srv);
                                YYERROR;
                        }
+
+                       TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+
                        srv = NULL;
                        srv_conf = NULL;
                }
@@ -367,17 +386,6 @@ serveroptsl        : LISTEN ON STRING optssl po
                                YYACCEPT;
                        }
 
-                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry)
-                               if (strcmp(s->srv_conf.name,
-                                   srv->srv_conf.name) == 0 &&
-                                   strcmp(s->srv_conf.location, $2) == 0)
-                                       break;
-                       if (s != NULL) {
-                               yyerror("location %s defined twice", $2);
-                               free($2);
-                               YYERROR;
-                       }
-
                        if ((s = calloc(1, sizeof (*s))) == NULL)
                                fatal("out of memory");
 
@@ -416,12 +424,31 @@ serveroptsl       : LISTEN ON STRING optssl po
                        srv = s;
                        srv_conf = &srv->srv_conf;
                        SPLAY_INIT(&srv->srv_clients);
-                       TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
                } '{' optnl serveropts_l '}'    {
+                       struct server   *s = NULL;
+
+                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
+                               if ((s->srv_conf.flags & SRVFLAG_LOCATION) &&
+                                   s->srv_conf.id == srv_conf->id &&
+                                   strcmp(s->srv_conf.location,
+                                   srv_conf->location) == 0)
+                                       break;
+                       }
+                       if (s != NULL) {
+                               yyerror("location \"%s\" defined twice",
+                                   srv->srv_conf.location);
+                               serverconfig_free(srv_conf);
+                               free(srv);
+                               YYABORT;
+                       }
+
+                       TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+
                        srv = parentsrv;
                        srv_conf = &parentsrv->srv_conf;
                        parentsrv = NULL;
                }
+               | include
                ;
 
 fastcgi                : NO FCGI               {
@@ -623,7 +650,7 @@ tcpflags    : SACK                  { srv_conf->tcpflags |
                }
                | BACKLOG NUMBER        {
                        if ($2 < 0 || $2 > SERVER_MAX_CLIENTS) {
-                               yyerror("invalid backlog: %d", $2);
+                               yyerror("invalid backlog: %lld", $2);
                                YYERROR;
                        }
                        srv_conf->tcpbacklog = $2;
@@ -631,13 +658,13 @@ tcpflags  : SACK                  { srv_conf->tcpflags |
                | SOCKET BUFFER NUMBER  {
                        srv_conf->tcpflags |= TCPFLAG_BUFSIZ;
                        if ((srv_conf->tcpbufsiz = $3) < 0) {
-                               yyerror("invalid socket buffer size: %d", $3);
+                               yyerror("invalid socket buffer size: %lld", $3);
                                YYERROR;
                        }
                }
                | IP STRING NUMBER      {
                        if ($3 < 0) {
-                               yyerror("invalid ttl: %d", $3);
+                               yyerror("invalid ttl: %lld", $3);
                                free($2);
                                YYERROR;
                        }
@@ -694,6 +721,9 @@ medianamesl : STRING                                {
                        }
                        free($1);
 
+                       if (!loadcfg)
+                               break;
+
                        if (media_add(conf->sc_mediatypes, &media) == NULL) {
                                yyerror("failed to add media type");
                                YYERROR;
@@ -729,7 +759,7 @@ port                : PORT STRING {
                }
                | PORT NUMBER {
                        if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
-                               yyerror("invalid port: %d", $2);
+                               yyerror("invalid port: %lld", $2);
                                YYERROR;
                        }
                        $$.val[0] = htons($2);
@@ -740,7 +770,7 @@ port                : PORT STRING {
 timeout                : NUMBER
                {
                        if ($1 < 0) {
-                               yyerror("invalid timeout: %d\n", $1);
+                               yyerror("invalid timeout: %lld", $1);
                                YYERROR;
                        }
                        $$.tv_sec = $1;
@@ -771,15 +801,15 @@ int
 yyerror(const char *fmt, ...)
 {
        va_list          ap;
-       char            *nfmt;
+       char            *msg;
 
        file->errors++;
        va_start(ap, fmt);
-       if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1)
-               fatalx("yyerror asprintf");
-       vlog(LOG_CRIT, nfmt, ap);
+       if (vasprintf(&msg, fmt, ap) == -1)
+               fatalx("yyerror vasprintf");
        va_end(ap);
-       free(nfmt);
+       logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+       free(msg);
        return (0);
 }
 
Index: usr.sbin/httpd/server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.39
diff -u -p -r1.39 server.c
--- usr.sbin/httpd/server.c     6 Aug 2014 18:38:11 -0000       1.39
+++ usr.sbin/httpd/server.c     18 Nov 2014 15:02:55 -0000
@@ -285,8 +285,7 @@ server_purge(struct server *srv)
 
                /* It might point to our own "default" entry */
                if (srv_conf != &srv->srv_conf) {
-                       free(srv_conf->ssl_cert);
-                       free(srv_conf->ssl_key);
+                       serverconfig_free(srv_conf);
                        free(srv_conf);
                }
        }
@@ -297,6 +296,22 @@ server_purge(struct server *srv)
        free(srv);
 }
 
+void
+serverconfig_free(struct server_config *srv_conf)
+{
+       free(srv_conf->ssl_cert_file);
+       free(srv_conf->ssl_cert);
+       free(srv_conf->ssl_key_file);
+       free(srv_conf->ssl_key);
+}
+
+void
+serverconfig_reset(struct server_config *srv_conf)
+{
+       srv_conf->ssl_cert_file = srv_conf->ssl_cert =
+           srv_conf->ssl_key_file = srv_conf->ssl_key = NULL;
+}
+
 struct server *
 server_byaddr(struct sockaddr *addr, in_port_t port)
 {
@@ -750,23 +765,36 @@ void
 server_error(struct bufferevent *bev, short error, void *arg)
 {
        struct client           *clt = arg;
+       struct evbuffer         *dst;
 
        if (error & EVBUFFER_TIMEOUT) {
                server_close(clt, "buffer event timeout");
                return;
        }
-       if (error & EVBUFFER_ERROR && errno == EFBIG) {
-               bufferevent_enable(bev, EV_READ);
+       if (error & EVBUFFER_ERROR) {
+               if (errno == EFBIG) {
+                       bufferevent_enable(bev, EV_READ);
+                       return;
+               }
+               server_close(clt, "buffer event error");
                return;
        }
        if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
                bufferevent_disable(bev, EV_READ|EV_WRITE);
 
                clt->clt_done = 1;
+
+               dst = EVBUFFER_OUTPUT(clt->clt_bev);
+               if (EVBUFFER_LENGTH(dst)) {
+                       /* Finish writing all data first */
+                       bufferevent_enable(clt->clt_bev, EV_WRITE);
+                       return;
+               }
+
                server_close(clt, "done");
                return;
        }
-       server_close(clt, "buffer event error");
+       server_close(clt, "unknown event error");
        return;
 }
 
@@ -1109,6 +1137,26 @@ server_bufferevent_add(struct event *ev,
        }
 
        return (event_add(ev, ptv));
+}
+
+int
+server_bufferevent_printf(struct client *clt, const char *fmt, ...)
+{
+       int      ret;
+       va_list  ap;
+       char    *str;
+
+       va_start(ap, fmt);
+       ret = vasprintf(&str, fmt, ap);
+       va_end(ap);
+
+       if (ret == -1)
+               return (ret);
+
+       ret = server_bufferevent_print(clt, str);
+       free(str);
+
+       return (ret);
 }
 
 int
Index: usr.sbin/httpd/server_fcgi.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
retrieving revision 1.29
diff -u -p -r1.29 server_fcgi.c
--- usr.sbin/httpd/server_fcgi.c        7 Aug 2014 12:43:22 -0000       1.29
+++ usr.sbin/httpd/server_fcgi.c        18 Nov 2014 15:02:55 -0000
@@ -87,22 +87,24 @@ struct server_fcgi_param {
 int    server_fcgi_header(struct client *, u_int);
 void   server_fcgi_read(struct bufferevent *, void *);
 int    server_fcgi_writeheader(struct client *, struct kv *, void *);
+int    server_fcgi_writechunk(struct client *);
+int    server_fcgi_getheaders(struct client *);
 int    fcgi_add_param(struct server_fcgi_param *, const char *, const char *,
            struct client *);
-int    get_status(struct evbuffer *);
 
 int
 server_fcgi(struct httpd *env, struct client *clt)
 {
        struct server_fcgi_param         param;
-       char                             hbuf[MAXHOSTNAMELEN];
        struct server_config            *srv_conf = clt->clt_srv_conf;
-       struct http_descriptor          *desc   = clt->clt_desc;
+       struct http_descriptor          *desc = clt->clt_descreq;
        struct sockaddr_un               sun;
        struct fcgi_record_header       *h;
        struct fcgi_begin_request_body  *begin;
        size_t                           len;
-       ssize_t                          scriptlen;
+       char                             hbuf[MAXHOSTNAMELEN];
+       size_t                           scriptlen;
+       int                              pathlen;
        int                              fd = -1, ret;
        const char                      *errstr = NULL;
        char                            *str, *p, *script = NULL;
@@ -189,14 +191,21 @@ server_fcgi(struct httpd *env, struct cl
        h->type = FCGI_PARAMS;
        h->content_len = param.total_len = 0;
 
-       if (asprintf(&script, "%s%s", srv_conf->root,
-           desc->http_path) == -1 ||
-           (scriptlen = path_info(script)) == -1) {
+       if ((pathlen = asprintf(&script, "%s%s", srv_conf->root,
+           desc->http_path_alias != NULL ?
+           desc->http_path_alias : desc->http_path)) == -1) {
                errstr = "failed to get script name";
                goto fail;
        }
 
-       if (scriptlen) {
+       scriptlen = path_info(script);
+       /*
+        * no part of root should show up in PATH_INFO.
+        * therefore scriptlen should be >= strlen(root)
+        */
+       if (scriptlen < strlen(srv_conf->root))
+               scriptlen = strlen(srv_conf->root);
+       if ((int)scriptlen < pathlen) {
                if (fcgi_add_param(&param, "PATH_INFO",
                    script + scriptlen, clt) == -1) {
                        errstr = "failed to encode param";
@@ -239,7 +248,7 @@ server_fcgi(struct httpd *env, struct cl
        }
 
        /* Add HTTP_* headers */
-       if (server_headers(clt, server_fcgi_writeheader, &param) == -1) {
+       if (server_headers(clt, desc, server_fcgi_writeheader, &param) == -1) {
                errstr = "failed to encode param";
                goto fail;
        }
@@ -337,11 +346,14 @@ server_fcgi(struct httpd *env, struct cl
                fcgi_add_stdin(clt, NULL);
        }
 
-       /*
-        * persist is not supported yet because we don't get the
-        * Content-Length from slowcgi and don't support chunked encoding.
-        */
-       clt->clt_persist = 0;
+       if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
+               clt->clt_fcgi_chunked = 1;
+       } else {
+               /* HTTP/1.0 does not support chunked encoding */
+               clt->clt_fcgi_chunked = 0;
+               clt->clt_persist = 0;
+       }
+       clt->clt_fcgi_end = 0;
        clt->clt_done = 0;
 
        free(script);
@@ -444,9 +456,9 @@ server_fcgi_read(struct bufferevent *bev
        char                            *ptr;
 
        do {
-               len = bufferevent_read(bev, &buf, clt->clt_fcgi_toread);
+               len = bufferevent_read(bev, buf, clt->clt_fcgi_toread);
                /* XXX error handling */
-               evbuffer_add(clt->clt_srvevb, &buf, len);
+               evbuffer_add(clt->clt_srvevb, buf, len);
                clt->clt_fcgi_toread -= len;
                DPRINTF("%s: len: %lu toread: %d state: %d", __func__, len,
                    clt->clt_fcgi_toread, clt->clt_fcgi_state);
@@ -478,9 +490,10 @@ server_fcgi_read(struct bufferevent *bev
 
                        /* fallthrough if content_len == 0 */
                case FCGI_READ_CONTENT:
-                       if (clt->clt_fcgi_type == FCGI_STDERR &&
-                           EVBUFFER_LENGTH(clt->clt_srvevb) > 0) {
-                               if ((ptr = get_string(
+                       switch (clt->clt_fcgi_type) {
+                       case FCGI_STDERR:
+                               if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 &&
+                                   (ptr = get_string(
                                    EVBUFFER_DATA(clt->clt_srvevb),
                                    EVBUFFER_LENGTH(clt->clt_srvevb)))
                                    != NULL) {
@@ -488,14 +501,27 @@ server_fcgi_read(struct bufferevent *bev
                                            IMSG_LOG_ERROR, "%s", ptr);
                                        free(ptr);
                                }
-                       }
-                       if (clt->clt_fcgi_type == FCGI_STDOUT &&
-                           EVBUFFER_LENGTH(clt->clt_srvevb) > 0) {
-                               if (++clt->clt_chunk == 1)
-                                       server_fcgi_header(clt,
-                                           get_status(clt->clt_srvevb));
-                               server_bufferevent_write_buffer(clt,
-                                   clt->clt_srvevb);
+                               break;
+                       case FCGI_STDOUT:
+                               if (++clt->clt_chunk == 1) {
+                                       if (server_fcgi_header(clt,
+                                           server_fcgi_getheaders(clt))
+                                           == -1) {
+                                               server_abort_http(clt, 500,
+                                                   "malformed fcgi headers");
+                                               return;
+                                       }
+                                       if (!EVBUFFER_LENGTH(clt->clt_srvevb))
+                                               break;
+                               }
+                               /* FALLTHROUGH */
+                       case FCGI_END_REQUEST:
+                               if (server_fcgi_writechunk(clt) == -1) {
+                                       server_abort_http(clt, 500,
+                                           "encoding error");
+                                       return;
+                               }
+                               break;
                        }
                        evbuffer_drain(clt->clt_srvevb,
                            EVBUFFER_LENGTH(clt->clt_srvevb));
@@ -523,9 +549,11 @@ server_fcgi_read(struct bufferevent *bev
 int
 server_fcgi_header(struct client *clt, u_int code)
 {
-       struct http_descriptor  *desc = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
+       struct http_descriptor  *resp = clt->clt_descresp;
        const char              *error;
        char                     tmbuf[32];
+       struct kv               *kv, key;
 
        if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
                return (-1);
@@ -533,34 +561,49 @@ server_fcgi_header(struct client *clt, u
        if (server_log_http(clt, code, 0) == -1)
                return (-1);
 
-       kv_purge(&desc->http_headers);
-
        /* Add error codes */
-       if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 ||
-           kv_set(&desc->http_pathquery, "%s", error) == -1)
+       if (kv_setkey(&resp->http_pathquery, "%lu", code) == -1 ||
+           kv_set(&resp->http_pathquery, "%s", error) == -1)
                return (-1);
 
        /* Add headers */
-       if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+       if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
                return (-1);
 
+       /* Set chunked encoding */
+       if (clt->clt_fcgi_chunked) {
+               /* XXX Should we keep and handle Content-Length instead? */
+               key.kv_key = "Content-Length";
+               if ((kv = kv_find(&resp->http_headers, &key)) != NULL)
+                       kv_delete(&resp->http_headers, kv);
+
+               /*
+                * XXX What if the FastCGI added some kind of Transfer-Encoding?
+                * XXX like gzip, deflate or even "chunked"?
+                */
+               if (kv_add(&resp->http_headers,
+                   "Transfer-Encoding", "chunked") == NULL)
+                       return (-1);
+       }
+
        /* Is it a persistent connection? */
        if (clt->clt_persist) {
-               if (kv_add(&desc->http_headers,
+               if (kv_add(&resp->http_headers,
                    "Connection", "keep-alive") == NULL)
                        return (-1);
-       } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+       } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
                return (-1);
 
-       /* Date header is mandatory and should be added last */
-       server_http_date(tmbuf, sizeof(tmbuf));
-       if (kv_add(&desc->http_headers, "Date", tmbuf) == NULL)
+       /* Date header is mandatory and should be added as late as possible */
+       if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
+           kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
                return (-1);
 
        /* Write initial header (fcgi might append more) */
        if (server_writeresponse_http(clt) == -1 ||
            server_bufferevent_print(clt, "\r\n") == -1 ||
-           server_headers(clt, server_writeheader_http, NULL) == -1)
+           server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
+           server_bufferevent_print(clt, "\r\n") == -1)
                return (-1);
 
        return (0);
@@ -608,26 +651,63 @@ server_fcgi_writeheader(struct client *c
 }
 
 int
-get_status(struct evbuffer *bev)
+server_fcgi_writechunk(struct client *clt)
 {
-       int code;
-       char *statusline, *tok;
-       const char *errstr;
-
-       /* XXX This is a hack. We need to parse the response header. */
-       code = 200;
-       if (strncmp(EVBUFFER_DATA(bev), "Status: ", strlen("Status: ")) == 0) {
-               statusline = get_string(EVBUFFER_DATA(bev),
-                   EVBUFFER_LENGTH(bev));
-               if (strtok(statusline, " ") != NULL) {
-                       if ((tok = strtok(NULL, " ")) != NULL) {
-                               code = (int) strtonum(tok, 100, 600, &errstr);
-                               if (errstr != NULL || server_httperror_byid(
-                                  code) == NULL)
-                                       code = 200;
-                       }
+       struct evbuffer *evb = clt->clt_srvevb;
+       size_t           len;
+
+       if (clt->clt_fcgi_type == FCGI_END_REQUEST) {
+               len = 0;
+       } else
+               len = EVBUFFER_LENGTH(evb);
+
+       /* If len is 0, make sure to write the end marker only once */
+       if (len == 0 && clt->clt_fcgi_end++)
+               return (0);
+
+       if (clt->clt_fcgi_chunked) {
+               if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 ||
+                   server_bufferevent_write_chunk(clt, evb, len) == -1 ||
+                   server_bufferevent_print(clt, "\r\n") == -1)
+                       return (-1);
+       } else
+               return (server_bufferevent_write_buffer(clt, evb));
+
+       return (0);
+}
+
+int
+server_fcgi_getheaders(struct client *clt)
+{
+       struct http_descriptor  *resp = clt->clt_descresp;
+       struct evbuffer         *evb = clt->clt_srvevb;
+       int                      code = 200;
+       char                    *line, *key, *value;
+       const char              *errstr;
+
+       while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') {
+               key = line;
+
+               if ((value = strchr(key, ':')) == NULL)
+                       break;
+               if (*value == ':') {
+                       *value++ = '\0';
+                       value += strspn(value, " \t");
+               } else {
+                       *value++ = '\0';
+               }
+
+               if (strcasecmp("Status", key) == 0) {
+                       value[strcspn(value, " \t")] = '\0';
+                       code = (int)strtonum(value, 100, 600, &errstr);
+                       if (errstr != NULL || server_httperror_byid(
+                           code) == NULL)
+                               code = 200;
+               } else {
+                       (void)kv_add(&resp->http_headers, key, value);
                }
-               free(statusline);
+               free(line);
        }
-       return code;
+
+       return (code);
 }
Index: usr.sbin/httpd/server_file.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_file.c,v
retrieving revision 1.31
diff -u -p -r1.31 server_file.c
--- usr.sbin/httpd/server_file.c        6 Aug 2014 11:24:12 -0000       1.31
+++ usr.sbin/httpd/server_file.c        18 Nov 2014 15:02:55 -0000
@@ -46,42 +46,36 @@
 #include "httpd.h"
 #include "http.h"
 
-int     server_file_access(struct client *, char *, size_t,
+int     server_file_access(struct httpd *, struct client *, char *, size_t);
+int     server_file_request(struct httpd *, struct client *, char *,
            struct stat *);
-int     server_file_index(struct httpd *, struct client *);
+int     server_file_index(struct httpd *, struct client *, struct stat *);
+int     server_file_method(struct client *);
 
 int
-server_file_access(struct client *clt, char *path, size_t len,
-    struct stat *st)
+server_file_access(struct httpd *env, struct client *clt,
+    char *path, size_t len)
 {
-       struct http_descriptor  *desc = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
        struct server_config    *srv_conf = clt->clt_srv_conf;
-       struct stat              stb;
+       struct stat              st;
        char                    *newpath;
+       int                      ret;
 
        errno = 0;
 
-       switch (desc->http_method) {
-       case HTTP_METHOD_GET:
-       case HTTP_METHOD_HEAD:
-               break;
-       default:
-               /* Other methods are not allowed */
-               return (405);
-       }
-
        if (access(path, R_OK) == -1) {
                goto fail;
-       } else if (stat(path, st) == -1) {
+       } else if (stat(path, &st) == -1) {
                goto fail;
-       } else if (S_ISDIR(st->st_mode)) {
+       } else if (S_ISDIR(st.st_mode)) {
                /* Deny access if directory indexing is disabled */
                if (srv_conf->flags & SRVFLAG_NO_INDEX) {
                        errno = EACCES;
                        goto fail;
                }
 
-               if (!len) {
+               if (desc->http_path_alias != NULL) {
                        /* Recursion - the index "file" is a directory? */
                        errno = EINVAL;
                        goto fail;
@@ -93,21 +87,31 @@ server_file_access(struct client *clt, c
                            srv_conf->flags & SRVFLAG_SSL ? "s" : "",
                            desc->http_host, desc->http_path) == -1)
                                return (500);
-                       free(desc->http_path);
-                       desc->http_path = newpath;
+                       /* Path alias will be used for the redirection */
+                       desc->http_path_alias = newpath;
 
                        /* Indicate that the file has been moved */
                        return (301);
                }
 
-               /* Otherwise append the default index file */
+               /* Append the default index file to the location */
+               if (asprintf(&newpath, "%s%s", desc->http_path,
+                   srv_conf->index) == -1)
+                       return (500);
+               desc->http_path_alias = newpath;
+               if (server_getlocation(clt, newpath) != srv_conf) {
+                       /* The location has changed */
+                       return (server_file(env, clt));
+               }
+
+               /* Otherwise append the default index file to the path */
                if (strlcat(path, srv_conf->index, len) >= len) {
                        errno = EACCES;
                        goto fail;
                }
 
-               /* Check again but set len to 0 to avoid recursion */
-               if (server_file_access(clt, path, 0, &stb) == 404) {
+               ret = server_file_access(env, clt, path, len);
+               if (ret == 404) {
                        /*
                         * Index file not found; fail if auto-indexing is
                         * not enabled, otherwise return success but
@@ -118,17 +122,17 @@ server_file_access(struct client *clt, c
                                errno = EACCES;
                                goto fail;
                        }
-               } else {
-                       /* return updated stat from index file */
-                       memcpy(st, &stb, sizeof(*st));
+
+                       return (server_file_index(env, clt, &st));
                }
-       } else if (!S_ISREG(st->st_mode)) {
+               return (ret);
+       } else if (!S_ISREG(st.st_mode)) {
                /* Don't follow symlinks and ignore special files */
                errno = EACCES;
                goto fail;
        }
 
-       return (0);
+       return (server_file_request(env, clt, path, &st));
 
  fail:
        switch (errno) {
@@ -146,31 +150,69 @@ server_file_access(struct client *clt, c
 int
 server_file(struct httpd *env, struct client *clt)
 {
-       struct http_descriptor  *desc = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
        struct server_config    *srv_conf = clt->clt_srv_conf;
-       struct media_type       *media;
-       const char              *errstr = NULL;
-       int                      fd = -1, ret, code = 500;
        char                     path[MAXPATHLEN];
-       struct stat              st;
+       const char              *errstr = NULL;
+       int                      ret = 500;
+
+       if (srv_conf->flags & SRVFLAG_FCGI)
+               return (server_fcgi(env, clt));
 
        /* Request path is already canonicalized */
        if ((size_t)snprintf(path, sizeof(path), "%s%s",
-           srv_conf->root, desc->http_path) >= sizeof(path)) {
+           srv_conf->root,
+           desc->http_path_alias != NULL ?
+           desc->http_path_alias : desc->http_path) >= sizeof(path)) {
                errstr = desc->http_path;
                goto abort;
        }
 
        /* Returns HTTP status code on error */
-       if ((ret = server_file_access(clt, path, sizeof(path), &st)) != 0) {
-               code = ret;
-               errstr = desc->http_path;
+       if ((ret = server_file_access(env, clt, path, sizeof(path))) > 0) {
+               errstr = desc->http_path_alias != NULL ?
+                   desc->http_path_alias : desc->http_path;
                goto abort;
        }
 
-       if (S_ISDIR(st.st_mode)) {
-               /* List directory index */
-               return (server_file_index(env, clt));
+       return (ret);
+
+ abort:
+       if (errstr == NULL)
+               errstr = strerror(errno);
+       server_abort_http(clt, ret, errstr);
+       return (-1);
+}
+
+int
+server_file_method(struct client *clt)
+{
+       struct http_descriptor  *desc = clt->clt_descreq;
+
+       switch (desc->http_method) {
+       case HTTP_METHOD_GET:
+       case HTTP_METHOD_HEAD:
+               return (0);
+       default:
+               /* Other methods are not allowed */
+               errno = EACCES;
+               return (405);
+       }
+       /* NOTREACHED */
+}
+
+int
+server_file_request(struct httpd *env, struct client *clt, char *path,
+    struct stat *st)
+{
+       struct server_config    *srv_conf = clt->clt_srv_conf;
+       struct media_type       *media;
+       const char              *errstr = NULL;
+       int                      fd = -1, ret, code = 500;
+
+       if ((ret = server_file_method(clt)) != 0) {
+               code = ret;
+               goto abort;
        }
 
        /* Now open the file, should be readable or we have another problem */
@@ -178,7 +220,8 @@ server_file(struct httpd *env, struct cl
                goto abort;
 
        media = media_find(env->sc_mediatypes, path);
-       ret = server_response_http(clt, 200, media, st.st_size);
+       ret = server_response_http(clt, 200, media, st->st_size,
+           MIN(time(NULL), st->st_mtim.tv_sec));
        switch (ret) {
        case -1:
                goto fail;
@@ -225,20 +268,25 @@ server_file(struct httpd *env, struct cl
 }
 
 int
-server_file_index(struct httpd *env, struct client *clt)
+server_file_index(struct httpd *env, struct client *clt, struct stat *st)
 {
        char                      path[MAXPATHLEN];
        char                      tmstr[21];
-       struct http_descriptor   *desc = clt->clt_desc;
+       struct http_descriptor   *desc = clt->clt_descreq;
        struct server_config     *srv_conf = clt->clt_srv_conf;
        struct dirent           **namelist, *dp;
        int                       namesize, i, ret, fd = -1, namewidth, skip;
+       int                       code = 500;
        struct evbuffer          *evb = NULL;
        struct media_type        *media;
        const char               *style;
-       struct stat               st;
        struct tm                 tm;
-       time_t                    t;
+       time_t                    t, dir_mtime;
+
+       if ((ret = server_file_method(clt)) != 0) {
+               code = ret;
+               goto abort;
+       }
 
        /* Request path is already canonicalized */
        if ((size_t)snprintf(path, sizeof(path), "%s%s",
@@ -249,6 +297,9 @@ server_file_index(struct httpd *env, str
        if ((fd = open(path, O_RDONLY)) == -1)
                goto abort;
 
+       /* Save last modification time */
+       dir_mtime = MIN(time(NULL), st->st_mtim.tv_sec);
+
        if ((evb = evbuffer_new()) == NULL)
                goto abort;
 
@@ -260,7 +311,7 @@ server_file_index(struct httpd *env, str
 
        /* A CSS stylesheet allows minimal customization by the user */
        style = "body { background-color: white; color: black; font-family: "
-           "sans-serif; }";
+           "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n";
        /* Generate simple HTML index document */
        if (evbuffer_add_printf(evb,
            "<!DOCTYPE HTML PUBLIC "
@@ -280,12 +331,12 @@ server_file_index(struct httpd *env, str
                dp = namelist[i];
 
                if (skip ||
-                   fstatat(fd, dp->d_name, &st, 0) == -1) {
+                   fstatat(fd, dp->d_name, st, 0) == -1) {
                        free(dp);
                        continue;
                }
 
-               t = st.st_mtime;
+               t = st->st_mtime;
                localtime_r(&t, &tm);
                strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
                namewidth = 51 - strlen(dp->d_name);
@@ -293,18 +344,18 @@ server_file_index(struct httpd *env, str
                if (dp->d_name[0] == '.' &&
                    !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
                        /* ignore hidden files starting with a dot */
-               } else if (S_ISDIR(st.st_mode)) {
+               } else if (S_ISDIR(st->st_mode)) {
                        namewidth -= 1; /* trailing slash */
                        if (evbuffer_add_printf(evb,
                            "<a href=\"%s\">%s/</a>%*s%s%20s\n",
                            dp->d_name, dp->d_name,
                            MAX(namewidth, 0), " ", tmstr, "-") == -1)
                                skip = 1;
-               } else if (S_ISREG(st.st_mode)) {
+               } else if (S_ISREG(st->st_mode)) {
                        if (evbuffer_add_printf(evb,
                            "<a href=\"%s\">%s</a>%*s%s%20llu\n",
                            dp->d_name, dp->d_name,
-                           MAX(namewidth, 0), " ", tmstr, st.st_size) == -1)
+                           MAX(namewidth, 0), " ", tmstr, st->st_size) == -1)
                                skip = 1;
                }
                free(dp);
@@ -320,7 +371,8 @@ server_file_index(struct httpd *env, str
        fd = -1;
 
        media = media_find(env->sc_mediatypes, "index.html");
-       ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb));
+       ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb),
+           dir_mtime);
        switch (ret) {
        case -1:
                goto fail;
@@ -356,7 +408,7 @@ server_file_index(struct httpd *env, str
                close(fd);
        if (evb != NULL)
                evbuffer_free(evb);
-       server_abort_http(clt, 500, desc->http_path);
+       server_abort_http(clt, code, desc->http_path);
        return (-1);
 }
 
@@ -370,8 +422,16 @@ server_file_error(struct bufferevent *be
                server_close(clt, "buffer event timeout");
                return;
        }
+       if (error & EVBUFFER_ERROR) {
+               if (errno == EFBIG) {
+                       bufferevent_enable(bev, EV_READ);
+                       return;
+               }
+               server_close(clt, "buffer event error");
+               return;
+       }
        if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
-               bufferevent_disable(bev, EV_READ);
+               bufferevent_disable(bev, EV_READ|EV_WRITE);
 
                clt->clt_done = 1;
 
@@ -396,10 +456,6 @@ server_file_error(struct bufferevent *be
                server_close(clt, "done");
                return;
        }
-       if (error & EVBUFFER_ERROR && errno == EFBIG) {
-               bufferevent_enable(bev, EV_READ);
-               return;
-       }
-       server_close(clt, "buffer event error");
+       server_close(clt, "unknown event error");
        return;
 }
Index: usr.sbin/httpd/server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.42
diff -u -p -r1.42 server_http.c
--- usr.sbin/httpd/server_http.c        6 Aug 2014 18:21:14 -0000       1.42
+++ usr.sbin/httpd/server_http.c        18 Nov 2014 15:02:55 -0000
@@ -86,9 +86,15 @@ server_httpdesc_init(struct client *clt)
 
        if ((desc = calloc(1, sizeof(*desc))) == NULL)
                return (-1);
+       RB_INIT(&desc->http_headers);
+       clt->clt_descreq = desc;
 
+       if ((desc = calloc(1, sizeof(*desc))) == NULL) {
+               /* req will be cleaned up later */
+               return (-1);
+       }
        RB_INIT(&desc->http_headers);
-       clt->clt_desc = desc;
+       clt->clt_descresp = desc;
 
        return (0);
 }
@@ -96,10 +102,16 @@ server_httpdesc_init(struct client *clt)
 void
 server_httpdesc_free(struct http_descriptor *desc)
 {
+       if (desc == NULL)
+               return;
        if (desc->http_path != NULL) {
                free(desc->http_path);
                desc->http_path = NULL;
        }
+       if (desc->http_path_alias != NULL) {
+               free(desc->http_path_alias);
+               desc->http_path_alias = NULL;
+       }
        if (desc->http_query != NULL) {
                free(desc->http_query);
                desc->http_query = NULL;
@@ -114,6 +126,8 @@ server_httpdesc_free(struct http_descrip
        }
        kv_purge(&desc->http_headers);
        desc->http_lastheader = NULL;
+       desc->http_method = 0;
+       desc->http_chunked = 0;
 }
 
 void
@@ -121,7 +135,7 @@ server_read_http(struct bufferevent *bev
 {
        struct client           *clt = arg;
        struct server_config    *srv_conf = clt->clt_srv_conf;
-       struct http_descriptor  *desc = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
        struct evbuffer         *src = EVBUFFER_INPUT(bev);
        char                    *line = NULL, *key, *value;
        const char              *errstr;
@@ -215,18 +229,20 @@ server_read_http(struct bufferevent *bev
                                goto fail;
                        }
                        desc->http_version = strchr(desc->http_path, ' ');
-                       if (desc->http_version != NULL)
-                               *desc->http_version++ = '\0';
+                       if (desc->http_version == NULL) {
+                               free(line);
+                               goto fail;
+                       }
+                       *desc->http_version++ = '\0';
                        desc->http_query = strchr(desc->http_path, '?');
                        if (desc->http_query != NULL)
                                *desc->http_query++ = '\0';
 
                        /*
                         * Have to allocate the strings because they could
-                        * be changed independetly by the filters later.
+                        * be changed independently by the filters later.
                         */
-                       if (desc->http_version != NULL &&
-                           (desc->http_version =
+                       if ((desc->http_version =
                            strdup(desc->http_version)) == NULL) {
                                free(line);
                                goto fail;
@@ -300,11 +316,36 @@ server_read_http(struct bufferevent *bev
                case HTTP_METHOD_GET:
                case HTTP_METHOD_HEAD:
                case HTTP_METHOD_OPTIONS:
+               /* WebDAV methods */
+               case HTTP_METHOD_COPY:
                        clt->clt_toread = 0;
                        break;
                case HTTP_METHOD_POST:
                case HTTP_METHOD_PUT:
                case HTTP_METHOD_RESPONSE:
+               /* WebDAV methods */
+               case HTTP_METHOD_PROPFIND:
+               case HTTP_METHOD_PROPPATCH:
+               case HTTP_METHOD_MKCOL:
+               case HTTP_METHOD_LOCK:
+               case HTTP_METHOD_UNLOCK:
+               case HTTP_METHOD_VERSION_CONTROL:
+               case HTTP_METHOD_REPORT:
+               case HTTP_METHOD_CHECKOUT:
+               case HTTP_METHOD_CHECKIN:
+               case HTTP_METHOD_UNCHECKOUT:
+               case HTTP_METHOD_MKWORKSPACE:
+               case HTTP_METHOD_UPDATE:
+               case HTTP_METHOD_LABEL:
+               case HTTP_METHOD_MERGE:
+               case HTTP_METHOD_BASELINE_CONTROL:
+               case HTTP_METHOD_MKACTIVITY:
+               case HTTP_METHOD_ORDERPATCH:
+               case HTTP_METHOD_ACL:
+               case HTTP_METHOD_MKREDIRECTREF:
+               case HTTP_METHOD_UPDATEREDIRECTREF:
+               case HTTP_METHOD_SEARCH:
+               case HTTP_METHOD_PATCH:
                        /* HTTP request payload */
                        if (clt->clt_toread > 0)
                                bev->readcb = server_read_httpcontent;
@@ -316,10 +357,8 @@ server_read_http(struct bufferevent *bev
                        }
                        break;
                default:
-                       /* HTTP handler */
-                       clt->clt_toread = TOREAD_HTTP_HEADER;
-                       bev->readcb = server_read_http;
-                       break;
+                       server_abort_http(clt, 405, "method not allowed");
+                       return;
                }
                if (desc->http_chunked) {
                        /* Chunked transfer encoding */
@@ -514,12 +553,10 @@ server_read_httpchunks(struct buffereven
 void
 server_reset_http(struct client *clt)
 {
-       struct http_descriptor  *desc = clt->clt_desc;
        struct server           *srv = clt->clt_srv;
 
-       server_httpdesc_free(desc);
-       desc->http_method = 0;
-       desc->http_chunked = 0;
+       server_httpdesc_free(clt->clt_descreq);
+       server_httpdesc_free(clt->clt_descresp);
        clt->clt_headerlen = 0;
        clt->clt_line = 0;
        clt->clt_done = 0;
@@ -530,16 +567,16 @@ server_reset_http(struct client *clt)
        server_log(clt, NULL);
 }
 
-void
-server_http_date(char *tmbuf, size_t len)
+ssize_t
+server_http_time(time_t t, char *tmbuf, size_t len)
 {
-       time_t                   t;
        struct tm                tm;
 
        /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
-       time(&t);
-       gmtime_r(&t, &tm);
-       strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm);
+       if (t == -1 || gmtime_r(&t, &tm) == NULL)
+               return (-1);
+       else
+               return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
 }
 
 const char *
@@ -574,6 +611,55 @@ server_http_host(struct sockaddr_storage
        return (buf);
 }
 
+char *
+server_http_parsehost(char *host, char *buf, size_t len, int *portval)
+{
+       char            *start, *end, *port;
+       const char      *errstr = NULL;
+
+       if (strlcpy(buf, host, len) >= len) {
+               log_debug("%s: host name too long", __func__);
+               return (NULL);
+       }
+
+       start = buf;
+       end = port = NULL;
+
+       if (*start == '[' && (end = strchr(start, ']')) != NULL) {
+               /* Address enclosed in [] with port, eg. [2001:db8::1]:80 */
+               start++;
+               *end++ = '\0';
+               if ((port = strchr(end, ':')) == NULL || *port == '\0')
+                       port = NULL;
+               else
+                       port++;
+               memmove(buf, start, strlen(start) + 1);
+       } else if ((end = strchr(start, ':')) != NULL) {
+               /* Name or address with port, eg. www.example.com:80 */
+               *end++ = '\0';
+               port = end;
+       } else {
+               /* Name or address with default port, eg. www.example.com */
+               port = NULL;
+       }
+
+       if (port != NULL) {
+               /* Save the requested port */
+               *portval = strtonum(port, 0, 0xffff, &errstr);
+               if (errstr != NULL) {
+                       log_debug("%s: invalid port: %s", __func__,
+                           strerror(errno));
+                       return (NULL);
+               }
+               *portval = htons(*portval);
+       } else {
+               /* Port not given, indicate the default port */
+               *portval = -1;
+       }
+
+       return (start);
+}
+
 void
 server_abort_http(struct client *clt, u_int code, const char *msg)
 {
@@ -598,13 +684,11 @@ server_abort_http(struct client *clt, u_
        if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
                goto done;
 
-       server_http_date(tmbuf, sizeof(tmbuf));
+       if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
+               goto done;
 
        /* Do not send details of the Internal Server Error */
        switch (code) {
-       case 500:
-               /* Do not send details of the Internal Server Error */
-               break;
        case 301:
        case 302:
                if (asprintf(&extraheader, "Location: %s\r\n", msg) == -1) {
@@ -613,7 +697,6 @@ server_abort_http(struct client *clt, u_
                }
                break;
        default:
-               text = msg;
                break;
        }
 
@@ -665,12 +748,17 @@ server_abort_http(struct client *clt, u_
 void
 server_close_http(struct client *clt)
 {
-       struct http_descriptor *desc    = clt->clt_desc;
+       struct http_descriptor *desc;
 
-       if (desc == NULL)
-               return;
+       desc = clt->clt_descreq;
+       server_httpdesc_free(desc);
+       free(desc);
+       clt->clt_descreq = NULL;
+
+       desc = clt->clt_descresp;
        server_httpdesc_free(desc);
        free(desc);
+       clt->clt_descresp = NULL;
 }
 
 int
@@ -678,13 +766,17 @@ server_response(struct httpd *httpd, str
 {
        char                     path[MAXPATHLEN];
        char                     hostname[MAXHOSTNAMELEN];
-       struct http_descriptor  *desc   = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
+       struct http_descriptor  *resp = clt->clt_descresp;
        struct server           *srv = clt->clt_srv;
-       struct server_config    *srv_conf = &srv->srv_conf, *location;
+       struct server_config    *srv_conf = &srv->srv_conf;
        struct kv               *kv, key, *host;
+       int                      portval = -1;
+       char                    *hostval;
 
        /* Canonicalize the request path */
        if (desc->http_path == NULL ||
+           url_decode(desc->http_path) == NULL ||
            canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
                goto fail;
        free(desc->http_path);
@@ -726,11 +818,16 @@ server_response(struct httpd *httpd, str
         * XXX the Host can also appear in the URL path.
         */
        if (host != NULL) {
-               /* XXX maybe better to turn srv_hosts into a tree */
+               if ((hostval = server_http_parsehost(host->kv_value,
+                   hostname, sizeof(hostname), &portval)) == NULL)
+                       goto fail;
+
                TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
                        if ((srv_conf->flags & SRVFLAG_LOCATION) == 0 &&
-                           fnmatch(srv_conf->name, host->kv_value,
-                           FNM_CASEFOLD) == 0) {
+                           fnmatch(srv_conf->name, hostname,
+                           FNM_CASEFOLD) == 0 &&
+                           (portval == -1 ||
+                           (portval != -1 && portval == srv_conf->port))) {
                                /* Replace host configuration */
                                clt->clt_srv_conf = srv_conf;
                                srv_conf = NULL;
@@ -755,31 +852,46 @@ server_response(struct httpd *httpd, str
        if ((desc->http_host = strdup(hostname)) == NULL)
                goto fail;
 
+       /* Now fill in the mandatory parts of the response descriptor */
+       resp->http_method = desc->http_method;
+       if ((resp->http_version = strdup(desc->http_version)) == NULL)
+               goto fail;
+
+       /* Now search for the location */
+       srv_conf = server_getlocation(clt, desc->http_path);
+
+       return (server_file(httpd, clt));
+ fail:
+       server_abort_http(clt, 400, "bad request");
+       return (-1);
+}
+
+struct server_config *
+server_getlocation(struct client *clt, const char *path)
+{
+       struct server           *srv = clt->clt_srv;
+       struct server_config    *srv_conf = clt->clt_srv_conf, *location;
+
        /* Now search for the location */
        TAILQ_FOREACH(location, &srv->srv_hosts, entry) {
                if ((location->flags & SRVFLAG_LOCATION) &&
                    location->id == srv_conf->id &&
-                   fnmatch(location->location, desc->http_path,
-                   FNM_CASEFOLD) == 0) {
+                   fnmatch(location->location, path, FNM_CASEFOLD) == 0) {
                        /* Replace host configuration */
                        clt->clt_srv_conf = srv_conf = location;
                        break;
                }
        }
 
-       if (srv_conf->flags & SRVFLAG_FCGI)
-               return (server_fcgi(httpd, clt));
-       return (server_file(httpd, clt));
- fail:
-       server_abort_http(clt, 400, "bad request");
-       return (-1);
+       return (srv_conf);
 }
 
 int
 server_response_http(struct client *clt, u_int code,
-    struct media_type *media, size_t size)
+    struct media_type *media, size_t size, time_t mtime)
 {
-       struct http_descriptor  *desc = clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descreq;
+       struct http_descriptor  *resp = clt->clt_descresp;
        const char              *error;
        struct kv               *ct, *cl;
        char                     tmbuf[32];
@@ -790,51 +902,54 @@ server_response_http(struct client *clt,
        if (server_log_http(clt, code, size) == -1)
                return (-1);
 
-       kv_purge(&desc->http_headers);
-
        /* Add error codes */
-       if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 ||
-           kv_set(&desc->http_pathquery, "%s", error) == -1)
+       if (kv_setkey(&resp->http_pathquery, "%lu", code) == -1 ||
+           kv_set(&resp->http_pathquery, "%s", error) == -1)
                return (-1);
 
        /* Add headers */
-       if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+       if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
                return (-1);
 
        /* Is it a persistent connection? */
        if (clt->clt_persist) {
-               if (kv_add(&desc->http_headers,
+               if (kv_add(&resp->http_headers,
                    "Connection", "keep-alive") == NULL)
                        return (-1);
-       } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+       } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
                return (-1);
 
        /* Set media type */
-       if ((ct = kv_add(&desc->http_headers, "Content-Type", NULL)) == NULL ||
+       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)
                return (-1);
 
        /* Set content length, if specified */
-       if (size && ((cl =
-           kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL ||
-           kv_set(cl, "%ld", size) == -1))
+       if ((cl =
+           kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL ||
+           kv_set(cl, "%ld", size) == -1)
+               return (-1);
+
+       /* Set last modification time */
+       if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 ||
+           kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL)
                return (-1);
 
-       /* Date header is mandatory and should be added last */
-       server_http_date(tmbuf, sizeof(tmbuf));
-       if (kv_add(&desc->http_headers, "Date", tmbuf) == NULL)
+       /* Date header is mandatory and should be added as late as possible */
+       if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
+           kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
                return (-1);
 
        /* Write completed header */
        if (server_writeresponse_http(clt) == -1 ||
            server_bufferevent_print(clt, "\r\n") == -1 ||
-           server_headers(clt, server_writeheader_http, NULL) == -1 ||
+           server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
            server_bufferevent_print(clt, "\r\n") == -1)
                return (-1);
 
-       if (desc->http_method == HTTP_METHOD_HEAD) {
+       if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) {
                bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
                if (clt->clt_persist)
                        clt->clt_toread = TOREAD_HTTP_HEADER;
@@ -850,7 +965,7 @@ server_response_http(struct client *clt,
 int
 server_writeresponse_http(struct client *clt)
 {
-       struct http_descriptor  *desc = (struct http_descriptor *)clt->clt_desc;
+       struct http_descriptor  *desc = clt->clt_descresp;
 
        DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
            desc->http_rescode, desc->http_resmesg);
@@ -894,11 +1009,11 @@ server_writeheader_http(struct client *c
 }
 
 int
-server_headers(struct client *clt,
+server_headers(struct client *clt, void *descp,
     int (*hdr_cb)(struct client *, struct kv *, void *), void *arg)
 {
        struct kv               *hdr, *kv;
-       struct http_descriptor  *desc = (struct http_descriptor *)clt->clt_desc;
+       struct http_descriptor  *desc = descp;
 
        RB_FOREACH(hdr, kvtree, &desc->http_headers) {
                if ((hdr_cb)(clt, hdr, arg) == -1)
@@ -932,7 +1047,7 @@ server_httpmethod_byname(const char *nam
 const char *
 server_httpmethod_byid(u_int id)
 {
-       const char      *name = NULL;
+       const char      *name = "<UNKNOWN>";
        int              i;
 
        for (i = 0; http_methods[i].method_name != NULL; i++) {
@@ -996,7 +1111,7 @@ server_log_http(struct client *clt, u_in
                return (-1);
        if ((srv_conf->flags & SRVFLAG_LOG) == 0)
                return (0);
-       if ((desc = clt->clt_desc) == NULL)
+       if ((desc = clt->clt_descreq) == NULL)
                return (-1);
 
        if ((t = time(NULL)) == -1)

Reply via email to