Commit Diff


commit - e7fdc47f24e824d2de7075deabc5e0280465b8e9
commit + 94c8a65e1db2bd36b1efb39bcb8ddb0634b49c8b
blob - e7688165275acaeee3017b4030e868d459697240
blob + b9784ad55cebc1ffcf856cd7b86fe9799aa5be93
--- http.h
+++ http.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: http.h,v 1.11 2019/05/08 23:22:19 reyk Exp $	*/
+/*	$OpenBSD: http.h,v 1.12 2021/03/24 20:59:53 benno Exp $	*/
 
 /*
  * Copyright (c) 2012 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -19,6 +19,8 @@
 #ifndef HTTP_H
 #define HTTP_H
 
+#include <sys/queue.h>
+
 #define HTTP_PORT	80
 #define HTTPS_PORT	443
 
@@ -251,4 +253,13 @@ struct http_descriptor {
 	struct kvtree		 http_headers;
 };
 
+struct http_method_node {
+	enum httpmethod			hmn_method;
+	SIMPLEQ_ENTRY(http_method_node)	hmn_entry;
+};
+
+struct http_session {
+	SIMPLEQ_HEAD(, http_method_node) hs_methods;
+};
+
 #endif /* HTTP_H */
blob - 02324f516c2f43c8e3394f4d2f5366c28ca6bc33
blob + da4a1aa0cc1158b22506c6d81e4d36b8810c025c
--- relay.c
+++ relay.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay.c,v 1.253 2021/01/27 20:33:05 eric Exp $	*/
+/*	$OpenBSD: relay.c,v 1.254 2021/03/24 20:59:53 benno Exp $	*/
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -800,7 +800,7 @@ relay_input(struct rsession *con)
 
 	switch (rlay->rl_proto->type) {
 	case RELAY_PROTO_HTTP:
-		if (relay_httpdesc_init(&con->se_in) == -1) {
+		if (relay_http_priv_init(con) == -1) {
 			relay_close(con,
 			    "failed to allocate http descriptor", 1);
 			return;
blob - 69b4869742723fb72806e66d15dd48937aff02cd
blob + 29e401e0935bd33dc81523b0d6baeed62dabf6bb
--- relay_http.c
+++ relay_http.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay_http.c,v 1.80 2021/01/09 08:53:58 denis Exp $	*/
+/*	$OpenBSD: relay_http.c,v 1.81 2021/03/24 20:59:54 benno Exp $	*/
 
 /*
  * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
@@ -113,6 +113,21 @@ relay_http_init(struct relay *rlay)
 }
 
 int
+relay_http_priv_init(struct rsession *con)
+{
+
+	struct http_session	*hs;
+
+	if ((hs = calloc(1, sizeof(*hs))) == NULL)
+		return (-1);
+	SIMPLEQ_INIT(&hs->hs_methods);
+	DPRINTF("%s: session %d http_session %p", __func__,
+		con->se_id, hs);
+	con->se_priv = hs;
+	return (relay_httpdesc_init(&con->se_in));
+}
+
+int
 relay_httpdesc_init(struct ctl_relay_event *cre)
 {
 	struct http_descriptor	*desc;
@@ -162,6 +177,9 @@ relay_read_http(struct bufferevent *bev, void *arg)
 	size_t			 size, linelen;
 	struct kv		*hdr = NULL;
 	struct kv		*upgrade = NULL, *upgrade_ws = NULL;
+	struct http_method_node	*hmn;
+	struct http_session	*hs;
+	enum httpmethod		 request_method;
 
 	getmonotime(&con->se_tv_last);
 	cre->timedout = 0;
@@ -239,12 +257,35 @@ relay_read_http(struct bufferevent *bev, void *arg)
 		DPRINTF("%s: session %d: header '%s: %s'", __func__,
 		    con->se_id, key, value);
 
+		hs = con->se_priv;
+		DPRINTF("%s: session %d http_session %p", __func__,
+			con->se_id, hs);
+
 		/*
 		 * Identify and handle specific HTTP request methods
 		 */
 		if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) {
 			desc->http_method = HTTP_METHOD_RESPONSE;
+			hmn = SIMPLEQ_FIRST(&hs->hs_methods);
+
 			/*
+			 * There is nothing preventing the relay to send
+			 * an unbalanced response.  Be prepared.
+			 */
+			if (hmn == NULL) {
+				request_method = HTTP_METHOD_NONE;
+				DPRINTF("%s: session %d unbalanced response",
+				    __func__, con->se_id);
+			} else {
+				SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry);
+				request_method = hmn->hmn_method;
+				DPRINTF("%s: session %d dequeing %s", __func__,
+				    con->se_id,
+				    relay_httpmethod_byid(request_method));
+				free(hmn);
+			}
+
+			/*
 			 * Decode response path and query
 			 */
 			desc->http_version = strdup(line);
@@ -287,6 +328,15 @@ relay_read_http(struct bufferevent *bev, void *arg)
 				free(line);
 				goto fail;
 			}
+			if ((hmn = calloc(1, sizeof *hmn)) == NULL) {
+				free(line);
+				goto fail;
+			}
+			hmn->hmn_method = desc->http_method;
+			DPRINTF("%s: session %d enqueing %s", __func__,
+			    con->se_id,
+			    relay_httpmethod_byid(hmn->hmn_method));
+			SIMPLEQ_INSERT_TAIL(&hs->hs_methods, hmn, hmn_entry);
 			/*
 			 * Decode request path and query
 			 */
@@ -332,16 +382,26 @@ relay_read_http(struct bufferevent *bev, void *arg)
 				goto abort;
 			}
 			/*
-			 * Need to read data from the client after the
-			 * HTTP header.
-			 * XXX What about non-standard clients not using
-			 * the carriage return? And some browsers seem to
-			 * include the line length in the content-length.
+			 * HEAD responses may provide a Content-Length header,
+			 * but if so it should just be ignored, since there is
+			 * no actual payload in the response.
 			 */
-			cre->toread = strtonum(value, 0, LLONG_MAX, &errstr);
-			if (errstr) {
-				relay_abort_http(con, 500, errstr, 0);
-				goto abort;
+			if (desc->http_method != HTTP_METHOD_RESPONSE
+			    || request_method != HTTP_METHOD_HEAD) {
+				/*
+				 * Need to read data from the client after the
+				 * HTTP header.
+				 * XXX What about non-standard clients not
+				 * using the carriage return? And some browsers
+				 * seem to include the line length in the
+				 * content-length.
+				 */
+				cre->toread = strtonum(value, 0, LLONG_MAX,
+				    &errstr);
+				if (errstr) {
+					relay_abort_http(con, 500, errstr, 0);
+					goto abort;
+				}
 			}
 			/*
 			 * response with a status code of 1xx
@@ -502,8 +562,9 @@ relay_read_http(struct bufferevent *bev, void *arg)
 		case HTTP_METHOD_SEARCH:
 		case HTTP_METHOD_PATCH:
 			/* HTTP request payload */
-			if (cre->toread > 0)
+			if (cre->toread > 0) {
 				bev->readcb = relay_read_httpcontent;
+			}
 
 			/* Single-pass HTTP body */
 			if (cre->toread < 0) {
@@ -1117,6 +1178,19 @@ relay_abort_http(struct rsession *con, u_int code, con
 void
 relay_close_http(struct rsession *con)
 {
+	struct http_session	*hs = con->se_priv;
+	struct http_method_node	*hmn;
+
+	DPRINTF("%s: session %d http_session %p", __func__,
+		con->se_id, hs);
+	if (hs != NULL)
+		while (!SIMPLEQ_EMPTY(&hs->hs_methods)) {
+			hmn = SIMPLEQ_FIRST(&hs->hs_methods);
+			SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry);
+			DPRINTF("%s: session %d freeing %s", __func__,
+			    con->se_id, relay_httpmethod_byid(hmn->hmn_method));
+			free(hmn);
+		}
 	relay_httpdesc_free(con->se_in.desc);
 	free(con->se_in.desc);
 	relay_httpdesc_free(con->se_out.desc);