1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 07:27:59 -08:00

Responses to HEAD requests must not have a message body (even though they have

a Content-Length header).  HTTP RFC 7231 section 4.3.2.
found by niklas@, claudio@ agrees.
This commit is contained in:
benno 2021-03-24 20:59:53 +00:00
parent d8ed2d1233
commit 53e8df0d32
3 changed files with 99 additions and 14 deletions

View File

@ -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 */

View File

@ -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;

View File

@ -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>
@ -112,6 +112,21 @@ relay_http_init(struct relay *rlay)
relay_calc_skip_steps(&rlay->rl_proto->rules);
}
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)
{
@ -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,11 +257,34 @@ 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
*/
@ -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, const char *msg,
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);