first step towards keep-alive/persistent connections support
authorreyk <reyk@openbsd.org>
Mon, 14 Jul 2014 00:19:48 +0000 (00:19 +0000)
committerreyk <reyk@openbsd.org>
Mon, 14 Jul 2014 00:19:48 +0000 (00:19 +0000)
usr.sbin/httpd/httpd.h
usr.sbin/httpd/server.c
usr.sbin/httpd/server_file.c
usr.sbin/httpd/server_http.c

index fc1f9b8..3834e80 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.2 2014/07/13 14:17:37 reyk Exp $  */
+/*     $OpenBSD: httpd.h,v 1.3 2014/07/14 00:19:48 reyk Exp $  */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -68,7 +68,8 @@ enum httpchunk {
        TOREAD_UNLIMITED                = -1,
        TOREAD_HTTP_HEADER              = -2,
        TOREAD_HTTP_CHUNK_LENGTH        = -3,
-       TOREAD_HTTP_CHUNK_TRAILER       = -4
+       TOREAD_HTTP_CHUNK_TRAILER       = -4,
+       TOREAD_HTTP_NONE                = -5
 };
 
 #if DEBUG > 1
@@ -264,6 +265,7 @@ struct client {
        struct bufferevent      *clt_file;
 
        off_t                    clt_toread;
+       int                      clt_persist;
        int                      clt_line;
        size_t                   clt_headerlen;
        int                      clt_done;
@@ -394,11 +396,14 @@ void       server_read_httpchunks(struct bufferevent *, void *);
 int     server_writeheader_kv(struct client *, struct kv *);
 int     server_writeheader_http(struct client *);
 int     server_writeresponse_http(struct client *);
+int     server_response_http(struct client *, u_int, struct media_type *,
+           size_t);
 void    server_reset_http(struct client *);
 void    server_close_http(struct client *);
+int     server_response(struct httpd *, struct client *);
 
 /* server_file.c */
-int     server_response(struct httpd *, struct client *);
+int     server_file(struct httpd *, struct client *);
 
 /* httpd.c */
 void            event_again(struct event *, int, short,
index f75f49e..b72d556 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.4 2014/07/13 15:07:50 reyk Exp $ */
+/*     $OpenBSD: server.c,v 1.5 2014/07/14 00:19:48 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -326,6 +326,11 @@ void
 server_write(struct bufferevent *bev, void *arg)
 {
        struct client           *clt = arg;
+       struct evbuffer         *dst = EVBUFFER_OUTPUT(bev);
+
+       if (EVBUFFER_LENGTH(dst) == 0 &&
+           clt->clt_toread == TOREAD_HTTP_NONE)
+               goto done;
 
        getmonotime(&clt->clt_tv_last);
 
@@ -406,7 +411,12 @@ server_error(struct bufferevent *bev, short error, void *arg)
                } else
                        return;
 
-               server_close(clt, "done");
+               if (clt->clt_persist) {
+                       server_reset_http(clt);
+                       bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+                       return;
+               } else
+                       server_close(clt, "done");
                return;
        }
        server_close(clt, "buffer event error");
index 20cb156..e8db8a7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_file.c,v 1.3 2014/07/13 15:07:50 reyk Exp $    */
+/*     $OpenBSD: server_file.c,v 1.4 2014/07/14 00:19:48 reyk Exp $    */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
 #include "http.h"
 
 int
-server_response(struct httpd *env, struct client *clt)
+server_file(struct httpd *env, struct client *clt)
 {
        struct http_descriptor  *desc = clt->clt_desc;
        struct server           *srv = clt->clt_server;
-       struct kv               *ct, *cl;
        struct media_type       *media;
        const char              *errstr = NULL;
-       int                      fd = -1;
+       int                      fd = -1, ret;
        char                     path[MAXPATHLEN];
        struct stat              st;
 
-       if (desc->http_path == NULL)
-               goto fail;
-
        /* 
         * XXX This is not ready XXX
         * XXX Don't expect anything from this code yet,
@@ -91,39 +87,22 @@ server_response(struct httpd *env, struct client *clt)
        if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1)
                goto fail;
 
-       kv_purge(&desc->http_headers);
-
-       /* 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 ||
-           server_writeheader_http(clt) == -1 ||
-           server_bufferevent_print(clt, "\r\n") == -1)
+       ret = server_response_http(clt, 200, media, st.st_size);
+       switch (ret) {
+       case -1:
                goto fail;
+       case 0:
+               /* Connection is already finished */
+               close(fd);
+               return (0);
+       default:
+               break;
+       }
 
        clt->clt_fd = fd;
+       if (clt->clt_file != NULL)
+               bufferevent_free(clt->clt_file);
        clt->clt_file = bufferevent_new(clt->clt_fd, server_read,
            server_write, server_error, clt);
        if (clt->clt_file == NULL) {
index 7615d42..16de7da 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.5 2014/07/13 15:39:01 reyk Exp $    */
+/*     $OpenBSD: server_http.c,v 1.6 2014/07/14 00:19:48 reyk Exp $    */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -322,8 +322,6 @@ server_read_http(struct bufferevent *bev, void *arg)
                        if (server_response(env, clt) == -1)
                                return;
                }
-
-               server_reset_http(clt);
        }
        if (clt->clt_done) {
                server_close(clt, "done");
@@ -513,6 +511,8 @@ server_reset_http(struct client *clt)
        clt->clt_headerlen = 0;
        clt->clt_line = 0;
        clt->clt_done = 0;
+       clt->clt_toread = TOREAD_HTTP_HEADER;
+       clt->clt_bev->readcb = server_read_http;
 }
 
 void
@@ -608,6 +608,109 @@ server_close_http(struct client *clt)
        free(desc);
 }
 
+int
+server_response(struct httpd *httpd, struct client *clt)
+{
+       struct http_descriptor  *desc   = clt->clt_desc;
+       struct kv               *kv, key;
+       int                      ret;
+
+       if (desc->http_path == NULL)
+               goto fail;
+
+       if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
+               /* Host header is mandatory */
+               key.kv_key = "Host";
+               if ((kv = kv_find(&desc->http_headers, &key)) == NULL)
+                       goto fail;
+
+               /* Is the connection persistent? */             
+               if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
+                   strcasecmp("close", kv->kv_value) == 0)
+                       clt->clt_persist = 0;
+               else
+                       clt->clt_persist = 1;
+       } else {
+               /* Keep-Alive with HTTP/1.0 not supported */
+               clt->clt_persist = 0;
+       }
+
+       if ((ret = server_file(httpd, clt)) == -1)
+               return (-1);
+
+       /* XXX */
+       if (!(desc->http_method == HTTP_METHOD_HEAD && clt->clt_persist == 0))
+               server_reset_http(clt);
+
+       return (ret);
+ fail:
+       server_abort_http(clt, 400, "bad request");
+       return (-1);
+}
+
+int
+server_response_http(struct client *clt, u_int code,
+    struct media_type *media, size_t size)
+{
+       struct http_descriptor  *desc = clt->clt_desc;
+       const char              *error;
+       struct kv               *ct, *cl;
+
+       if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
+               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)
+               return (-1);
+
+       /* Add headers */
+       if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
+               return (-1);
+
+       /* Is it a persistent connection? */
+       if (clt->clt_persist) {
+               if (kv_add(&desc->http_headers,
+                   "Connection", "keep-alive") == NULL)
+                       return (-1);
+       } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
+               return (-1);
+
+       /* Set media type */
+       if ((ct = kv_add(&desc->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))
+               return (-1);
+
+       /* Write completed header */
+       if (server_writeresponse_http(clt) == -1 ||
+           server_bufferevent_print(clt, "\r\n") == -1 ||
+           server_writeheader_http(clt) == -1 ||
+           server_bufferevent_print(clt, "\r\n") == -1)
+               return (-1);
+
+       if (desc->http_method == HTTP_METHOD_HEAD) {
+               bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+               if (clt->clt_persist)
+                       clt->clt_toread = TOREAD_HTTP_HEADER;
+               else
+                       clt->clt_toread = TOREAD_HTTP_NONE;
+               clt->clt_done = 0;
+               return (0);
+       }
+
+       return (1);
+}
+
 int
 server_writeresponse_http(struct client *clt)
 {