From 9ca51aa288c4ddd7ecc5dba48a8517b29eb313a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=E9d=E9ric=20L=E9caille?= Date: Mon, 12 Nov 2018 10:06:54 +0100 Subject: [PATCH] MINOR: http: Implement "early-hint" http request rules. This patch implements http_apply_early_hint_rule() function is responsible of building HTTP 103 Early Hint responses each time a "early-hint" rule is matched. --- include/common/http.h | 1 + src/http.c | 2 ++ src/proto_http.c | 63 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/include/common/http.h b/include/common/http.h index f87689115..3b1620df9 100644 --- a/include/common/http.h +++ b/include/common/http.h @@ -124,6 +124,7 @@ const struct ist http_known_methods[HTTP_METH_OTHER]; extern const uint8_t http_char_classes[256]; const struct ist HTTP_100; +const struct ist HTTP_103; extern const char *HTTP_301; extern const char *HTTP_302; extern const char *HTTP_303; diff --git a/src/http.c b/src/http.c index 932f3cf70..30d349a4b 100644 --- a/src/http.c +++ b/src/http.c @@ -162,6 +162,8 @@ struct buffer http_err_chunks[HTTP_ERR_SIZE]; const struct ist HTTP_100 = IST("HTTP/1.1 100 Continue\r\n\r\n"); +const struct ist HTTP_103 = IST("HTTP/1.1 103 Early Hints\r\n"); + /* Warning: no "connection" header is provided with the 3xx messages below */ const char *HTTP_301 = "HTTP/1.1 301 Moved Permanently\r\n" diff --git a/src/proto_http.c b/src/proto_http.c index a8a1728a8..c5d35437f 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1719,6 +1719,48 @@ static int http_transform_header(struct stream* s, struct http_msg *msg, return ret; } +/* + * Build an HTTP Early Hint HTTP 103 response header with as name and with a value + * built according to log line format. + * If is false the HTTP 103 response first line is inserted before + * the header. + */ +static int http_apply_early_hint_rule(struct stream* s, struct channel *resp, int early_hint, + const char* name, unsigned int name_len, struct list *fmt) +{ + int ret; + size_t data; + struct buffer *chunk; + char *cur_ptr = ci_head(resp); + + ret = 0; + data = co_data(resp); + + chunk = alloc_trash_chunk(); + if (!chunk) + goto leave; + + if (!early_hint && !chunk_memcat(chunk, HTTP_103.ptr, HTTP_103.len)) + goto leave; + + if (!chunk_memcat(chunk, name, name_len) || !chunk_memcat(chunk, ": ", 2)) + goto leave; + + chunk->data += build_logline(s, chunk->area + chunk->data, chunk->size - chunk->data, fmt); + if (!chunk_memcat(chunk, "\r\n", 2)) + goto leave; + + ret = b_rep_blk(&resp->buf, cur_ptr, cur_ptr, chunk->area, chunk->data); + c_adv(resp, ret); + + leave: + if (!ret) + co_set_data(resp, data); + free_trash_chunk(chunk); + + return ret; +} + /* Executes the http-request rules for stream , proxy and * transaction . Returns the verdict of the first rule that prevents * further processing of the request (auth, deny, ...), and defaults to @@ -1739,6 +1781,7 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct stream const char *auth_realm; int act_flags = 0; int len; + int early_hint = 0; /* If "the current_rule_list" match the executed rule list, we are in * resume condition. If a resume is needed it is always in the action @@ -2006,6 +2049,17 @@ resume_execution: break; } + case ACT_HTTP_EARLY_HINT: + if (!(txn->req.flags & HTTP_MSGF_VER_11)) + break; + + if (!http_apply_early_hint_rule(s, txn->rsp.chn, early_hint, + rule->arg.early_hint.name, + rule->arg.early_hint.name_len, + &rule->arg.early_hint.fmt)) + return HTTP_RULE_RES_DONE; + early_hint = 1; + break; case ACT_CUSTOM: if ((s->req.flags & CF_READ_ERROR) || ((s->req.flags & (CF_SHUTR|CF_READ_NULL)) && @@ -2074,6 +2128,15 @@ resume_execution: } } + if (early_hint) { + struct channel *chn = s->txn->rsp.chn; + char *cur_ptr = ci_head(chn); + + /* Add an empty line after Early Hint informational response headers */ + b_rep_blk(&chn->buf, cur_ptr, cur_ptr, "\r\n", 2); + c_adv(chn, 2); + } + /* we reached the end of the rules, nothing to report */ return HTTP_RULE_RES_CONT; }