summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2014-09-02 16:20:42 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2014-09-02 16:20:42 +0000
commit5b2a45069ddcae89c762de426e50fd4438b8de9d (patch)
tree5755a1d564546abb5c861999ed19d9065d265931 /usr.sbin
parent4dda34114bab14072ae8b36eda33724c7e92a96a (diff)
FastCGI did not support persistent connections. Add initial support
for persistent connections with FastCGI by implementing chunked Transfer-Encoding. This only works with HTTP/1.1. With input and help from florian@ who found some FastCGI edge cases. OK florian@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/httpd/httpd.h6
-rw-r--r--usr.sbin/httpd/server.c22
-rw-r--r--usr.sbin/httpd/server_fcgi.c100
3 files changed, 107 insertions, 21 deletions
diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h
index 586ec09a192..82f90be10b9 100644
--- a/usr.sbin/httpd/httpd.h
+++ b/usr.sbin/httpd/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.56 2014/09/01 09:32:43 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.57 2014/09/02 16:20:41 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -295,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;
@@ -478,6 +480,8 @@ void server_sendlog(struct server_config *, int, const char *, ...)
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 *);
diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c
index a7051f79a85..ed321af5752 100644
--- a/usr.sbin/httpd/server.c
+++ b/usr.sbin/httpd/server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.40 2014/08/27 09:51:53 reyk Exp $ */
+/* $OpenBSD: server.c,v 1.41 2014/09/02 16:20:41 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -1121,6 +1121,26 @@ server_bufferevent_add(struct event *ev, int timeout)
}
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
server_bufferevent_print(struct client *clt, const char *str)
{
if (clt->clt_bev == NULL)
diff --git a/usr.sbin/httpd/server_fcgi.c b/usr.sbin/httpd/server_fcgi.c
index e4771b9af31..c51d4b5516d 100644
--- a/usr.sbin/httpd/server_fcgi.c
+++ b/usr.sbin/httpd/server_fcgi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_fcgi.c,v 1.37 2014/09/01 12:28:11 reyk Exp $ */
+/* $OpenBSD: server_fcgi.c,v 1.38 2014/09/02 16:20:41 reyk Exp $ */
/*
* Copyright (c) 2014 Florian Obser <florian@openbsd.org>
@@ -87,6 +87,7 @@ 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 *);
@@ -347,11 +348,14 @@ server_fcgi(struct httpd *env, struct client *clt)
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);
@@ -458,8 +462,9 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
/* XXX error handling */
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);
+ DPRINTF("%s: len: %lu toread: %d state: %d type: %d",
+ __func__, len, clt->clt_fcgi_toread,
+ clt->clt_fcgi_state, clt->clt_fcgi_type);
if (clt->clt_fcgi_toread != 0)
return;
@@ -488,9 +493,10 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
/* 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) {
@@ -498,14 +504,27 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
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,
- server_fcgi_getheaders(clt));
- 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));
@@ -537,6 +556,7 @@ server_fcgi_header(struct client *clt, u_int code)
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);
@@ -553,6 +573,22 @@ server_fcgi_header(struct client *clt, u_int code)
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(&resp->http_headers,
@@ -618,6 +654,32 @@ server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg)
}
int
+server_fcgi_writechunk(struct client *clt)
+{
+ 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;