mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-23 06:41:32 +02:00
MINOR: h2: add new functions to produce an HTX message from an H2 response
The new function h2_prepare_htx_stsline() produces an HTX response message from an H2 response presented as a list of header fields.
This commit is contained in:
parent
a40782bb24
commit
1329b5be71
@ -156,6 +156,7 @@ enum h2_err {
|
|||||||
|
|
||||||
int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf);
|
int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf);
|
||||||
int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf);
|
int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf);
|
||||||
|
int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some helpful debugging functions.
|
* Some helpful debugging functions.
|
||||||
|
165
src/h2.c
165
src/h2.c
@ -573,3 +573,168 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms
|
|||||||
fail:
|
fail:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepare the status line into <htx> from pseudo headers stored in <phdr[]>.
|
||||||
|
* <fields> indicates what was found so far. This should be called once at the
|
||||||
|
* detection of the first general header field or at the end of the message if
|
||||||
|
* no general header field was found yet. Returns the created start line on
|
||||||
|
* success, or NULL on failure. Upon success, <msgf> is updated with a few
|
||||||
|
* H2_MSGF_* flags indicating what was found while parsing.
|
||||||
|
*/
|
||||||
|
static struct htx_sl *h2_prepare_htx_stsline(uint32_t fields, struct ist *phdr, struct htx *htx, unsigned int *msgf)
|
||||||
|
{
|
||||||
|
unsigned int flags = HTX_SL_F_NONE;
|
||||||
|
struct htx_sl *sl;
|
||||||
|
unsigned char h, t, u;
|
||||||
|
|
||||||
|
/* only :status is allowed as a pseudo header */
|
||||||
|
if (!(fields & H2_PHDR_FND_STAT))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (phdr[H2_PHDR_IDX_STAT].len != 3)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Set HTX start-line flags */
|
||||||
|
flags |= HTX_SL_F_VER_11; // V2 in fact
|
||||||
|
flags |= HTX_SL_F_XFER_LEN; // xfer len always known with H2
|
||||||
|
|
||||||
|
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/2.0"), phdr[H2_PHDR_IDX_STAT], ist(""));
|
||||||
|
if (!sl)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
h = phdr[H2_PHDR_IDX_STAT].ptr[0] - '0';
|
||||||
|
t = phdr[H2_PHDR_IDX_STAT].ptr[1] - '0';
|
||||||
|
u = phdr[H2_PHDR_IDX_STAT].ptr[2] - '0';
|
||||||
|
if (h > 9 || t > 9 || u > 9)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
sl->info.res.status = h * 100 + t * 10 + u;
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
fail:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes an H2 response present in the headers list <list> terminated by a name
|
||||||
|
* being <NULL,0> and emits the equivalent HTX response according to the rules
|
||||||
|
* documented in RFC7540 #8.1.2. The output contents are emitted in <htx>, and
|
||||||
|
* a positive value is returned if some bytes were emitted. In case of error, a
|
||||||
|
* negative error code is returned.
|
||||||
|
*
|
||||||
|
* Upon success, <msgf> is filled with a few H2_MSGF_* flags indicating what
|
||||||
|
* was found while parsing. The caller must set it to zero in or H2_MSGF_BODY
|
||||||
|
* if a body is detected (!ES).
|
||||||
|
*
|
||||||
|
* The headers list <list> must be composed of :
|
||||||
|
* - n.name != NULL, n.len > 0 : literal header name
|
||||||
|
* - n.name == NULL, n.len > 0 : indexed pseudo header name number <n.len>
|
||||||
|
* among H2_PHDR_IDX_*
|
||||||
|
* - n.name ignored, n.len == 0 : end of list
|
||||||
|
* - in all cases except the end of list, v.name and v.len must designate a
|
||||||
|
* valid value.
|
||||||
|
*/
|
||||||
|
int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf)
|
||||||
|
{
|
||||||
|
struct ist phdr_val[H2_PHDR_NUM_ENTRIES];
|
||||||
|
uint32_t fields; /* bit mask of H2_PHDR_FND_* */
|
||||||
|
uint32_t idx;
|
||||||
|
int phdr;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
struct htx_sl *sl = NULL;
|
||||||
|
unsigned int sl_flags = 0;
|
||||||
|
|
||||||
|
fields = 0;
|
||||||
|
for (idx = 0; list[idx].n.len != 0; idx++) {
|
||||||
|
if (!list[idx].n.ptr) {
|
||||||
|
/* this is an indexed pseudo-header */
|
||||||
|
phdr = list[idx].n.len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* this can be any type of header */
|
||||||
|
/* RFC7540#8.1.2: upper case not allowed in header field names */
|
||||||
|
for (i = 0; i < list[idx].n.len; i++)
|
||||||
|
if ((uint8_t)(list[idx].n.ptr[i] - 'A') < 'Z' - 'A')
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
phdr = h2_str_to_phdr(list[idx].n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phdr > 0 && phdr < H2_PHDR_NUM_ENTRIES) {
|
||||||
|
/* insert a pseudo header by its index (in phdr) and value (in value) */
|
||||||
|
if (fields & ((1 << phdr) | H2_PHDR_FND_NONE)) {
|
||||||
|
if (fields & H2_PHDR_FND_NONE) {
|
||||||
|
/* pseudo header field after regular headers */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* repeated pseudo header field */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields |= 1 << phdr;
|
||||||
|
phdr_val[phdr] = list[idx].v;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (phdr != 0) {
|
||||||
|
/* invalid pseudo header -- should never happen here */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* regular header field in (name,value) */
|
||||||
|
if (!(fields & H2_PHDR_FND_NONE)) {
|
||||||
|
/* no more pseudo-headers, time to build the status line */
|
||||||
|
sl = h2_prepare_htx_stsline(fields, phdr_val, htx, msgf);
|
||||||
|
if (!sl)
|
||||||
|
goto fail;
|
||||||
|
fields |= H2_PHDR_FND_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY &&
|
||||||
|
isteq(list[idx].n, ist("content-length"))) {
|
||||||
|
*msgf |= H2_MSGF_BODY_CL;
|
||||||
|
sl_flags |= HTX_SL_F_CLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* these ones are forbidden in responses (RFC7540#8.1.2.2) */
|
||||||
|
if (isteq(list[idx].n, ist("connection")) ||
|
||||||
|
isteq(list[idx].n, ist("proxy-connection")) ||
|
||||||
|
isteq(list[idx].n, ist("keep-alive")) ||
|
||||||
|
isteq(list[idx].n, ist("upgrade")) ||
|
||||||
|
isteq(list[idx].n, ist("transfer-encoding")))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!htx_add_header(htx, list[idx].n, list[idx].v))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC7540#8.1.2.1 mandates to reject request pseudo-headers */
|
||||||
|
if (fields & (H2_PHDR_FND_AUTH|H2_PHDR_FND_METH|H2_PHDR_FND_PATH|H2_PHDR_FND_SCHM))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Let's dump the request now if not yet emitted. */
|
||||||
|
if (!(fields & H2_PHDR_FND_NONE)) {
|
||||||
|
sl = h2_prepare_htx_stsline(fields, phdr_val, htx, msgf);
|
||||||
|
if (!sl)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the start line with last detected header info */
|
||||||
|
sl->flags |= sl_flags;
|
||||||
|
|
||||||
|
if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY) {
|
||||||
|
/* FIXME: Do we need to signal anything when we have a body and
|
||||||
|
* no content-length, to have the equivalent of H1's chunked
|
||||||
|
* encoding?
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now send the end of headers marker */
|
||||||
|
htx_add_endof(htx, HTX_BLK_EOH);
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user