2021-11-12 15:09:54 +00:00
|
|
|
#include <haproxy/hq_interop.h>
|
|
|
|
|
|
|
|
#include <import/ist.h>
|
|
|
|
#include <haproxy/buf.h>
|
|
|
|
#include <haproxy/connection.h>
|
|
|
|
#include <haproxy/dynbuf.h>
|
|
|
|
#include <haproxy/htx.h>
|
|
|
|
#include <haproxy/http.h>
|
2022-04-04 14:13:44 +00:00
|
|
|
#include <haproxy/mux_quic.h>
|
2023-02-17 08:51:20 +00:00
|
|
|
#include <haproxy/qmux_http.h>
|
2021-11-12 15:09:54 +00:00
|
|
|
|
2022-06-07 15:30:55 +00:00
|
|
|
static ssize_t hq_interop_decode_qcs(struct qcs *qcs, struct buffer *b, int fin)
|
2021-11-12 15:09:54 +00:00
|
|
|
{
|
|
|
|
struct htx *htx;
|
|
|
|
struct htx_sl *sl;
|
|
|
|
struct buffer htx_buf = BUF_NULL;
|
|
|
|
struct ist path;
|
2022-06-03 14:40:34 +00:00
|
|
|
char *ptr = b_head(b);
|
|
|
|
size_t data = b_data(b);
|
2021-11-12 15:09:54 +00:00
|
|
|
|
2023-10-04 13:46:06 +00:00
|
|
|
/* hq-interop parser does not support buffer wrapping. */
|
|
|
|
BUG_ON(b_data(b) != b_contig_data(b, 0));
|
2023-05-11 14:49:28 +00:00
|
|
|
|
2023-10-04 13:46:06 +00:00
|
|
|
/* hq-interop parser is only done once full message is received. */
|
|
|
|
if (!fin)
|
2023-02-17 08:51:20 +00:00
|
|
|
return 0;
|
|
|
|
|
2021-11-12 15:09:54 +00:00
|
|
|
b_alloc(&htx_buf);
|
|
|
|
htx = htx_from_buf(&htx_buf);
|
|
|
|
|
|
|
|
/* skip method */
|
2021-12-15 21:38:48 +00:00
|
|
|
while (data && HTTP_IS_TOKEN(*ptr)) {
|
2023-10-04 13:46:06 +00:00
|
|
|
ptr++;
|
2021-12-15 21:38:48 +00:00
|
|
|
data--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data || !HTTP_IS_SPHT(*ptr)) {
|
|
|
|
fprintf(stderr, "truncated stream\n");
|
2023-10-04 13:46:06 +00:00
|
|
|
return -1;
|
2021-12-15 21:38:48 +00:00
|
|
|
}
|
|
|
|
|
2023-10-04 13:46:06 +00:00
|
|
|
ptr++;
|
2021-12-15 21:38:48 +00:00
|
|
|
if (!--data) {
|
|
|
|
fprintf(stderr, "truncated stream\n");
|
2023-10-04 13:46:06 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HTTP_IS_LWS(*ptr)) {
|
|
|
|
fprintf(stderr, "malformed stream\n");
|
|
|
|
return -1;
|
2021-12-15 21:38:48 +00:00
|
|
|
}
|
2021-11-12 15:09:54 +00:00
|
|
|
|
|
|
|
/* extract path */
|
|
|
|
path.ptr = ptr;
|
2021-12-15 21:38:48 +00:00
|
|
|
while (data && !HTTP_IS_LWS(*ptr)) {
|
2023-10-04 13:46:06 +00:00
|
|
|
ptr++;
|
2021-12-15 21:38:48 +00:00
|
|
|
data--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
fprintf(stderr, "truncated stream\n");
|
2023-10-04 13:46:06 +00:00
|
|
|
return -1;
|
2021-12-15 21:38:48 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 15:09:54 +00:00
|
|
|
path.len = ptr - path.ptr;
|
|
|
|
|
|
|
|
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, 0, ist("GET"), path, ist("HTTP/1.0"));
|
2021-11-18 13:40:26 +00:00
|
|
|
if (!sl)
|
2022-06-07 15:30:55 +00:00
|
|
|
return -1;
|
2021-11-18 13:40:26 +00:00
|
|
|
|
2021-11-12 15:09:54 +00:00
|
|
|
sl->flags |= HTX_SL_F_BODYLESS;
|
|
|
|
sl->info.req.meth = find_http_meth("GET", 3);
|
|
|
|
|
|
|
|
htx_add_endof(htx, HTX_BLK_EOH);
|
2023-10-04 13:46:06 +00:00
|
|
|
htx->flags |= HTX_FL_EOM;
|
2021-11-12 15:09:54 +00:00
|
|
|
htx_to_buf(htx, &htx_buf);
|
|
|
|
|
2023-05-30 13:04:46 +00:00
|
|
|
if (!qcs_attach_sc(qcs, &htx_buf, fin))
|
2022-06-07 15:30:55 +00:00
|
|
|
return -1;
|
2021-11-12 15:09:54 +00:00
|
|
|
|
|
|
|
b_free(&htx_buf);
|
|
|
|
|
2022-06-07 15:30:55 +00:00
|
|
|
return b_data(b);
|
2021-11-12 15:09:54 +00:00
|
|
|
}
|
|
|
|
|
2023-10-27 14:41:58 +00:00
|
|
|
static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
2022-09-19 15:14:27 +00:00
|
|
|
size_t count)
|
2021-11-12 15:09:54 +00:00
|
|
|
{
|
|
|
|
enum htx_blk_type btype;
|
2023-10-27 14:41:58 +00:00
|
|
|
struct htx *htx;
|
2021-11-12 15:09:54 +00:00
|
|
|
struct htx_blk *blk;
|
|
|
|
int32_t idx;
|
|
|
|
uint32_t bsize, fsize;
|
|
|
|
struct buffer *res, outbuf;
|
|
|
|
size_t total = 0;
|
|
|
|
|
2023-11-13 13:57:28 +00:00
|
|
|
res = qcc_get_stream_txbuf(qcs);
|
2021-11-12 15:09:54 +00:00
|
|
|
outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
|
|
|
|
|
2023-10-27 14:41:58 +00:00
|
|
|
htx = htx_from_buf(buf);
|
|
|
|
|
|
|
|
if (htx->extra && htx->extra == HTX_UNKOWN_PAYLOAD_LENGTH)
|
|
|
|
qcs->flags |= QC_SF_UNKNOWN_PL_LENGTH;
|
|
|
|
|
2021-12-03 13:40:01 +00:00
|
|
|
while (count && !htx_is_empty(htx) && !(qcs->flags & QC_SF_BLK_MROOM)) {
|
2021-11-12 15:09:54 +00:00
|
|
|
/* Not implemented : QUIC on backend side */
|
|
|
|
idx = htx_get_head(htx);
|
|
|
|
blk = htx_get_blk(htx, idx);
|
|
|
|
btype = htx_get_blk_type(blk);
|
|
|
|
fsize = bsize = htx_get_blksz(blk);
|
|
|
|
|
|
|
|
BUG_ON(btype == HTX_BLK_REQ_SL);
|
|
|
|
|
|
|
|
switch (btype) {
|
|
|
|
case HTX_BLK_DATA:
|
|
|
|
if (fsize > count)
|
|
|
|
fsize = count;
|
2021-12-07 15:19:03 +00:00
|
|
|
|
2021-12-09 09:07:23 +00:00
|
|
|
if (b_room(&outbuf) < fsize)
|
|
|
|
fsize = b_room(&outbuf);
|
2021-12-07 15:19:03 +00:00
|
|
|
|
|
|
|
if (!fsize) {
|
|
|
|
qcs->flags |= QC_SF_BLK_MROOM;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2021-11-12 15:09:54 +00:00
|
|
|
b_putblk(&outbuf, htx_get_blk_ptr(htx, blk), fsize);
|
|
|
|
total += fsize;
|
|
|
|
count -= fsize;
|
|
|
|
|
|
|
|
if (fsize == bsize)
|
|
|
|
htx_remove_blk(htx, blk);
|
|
|
|
else
|
|
|
|
htx_cut_data_blk(htx, blk, fsize);
|
|
|
|
break;
|
|
|
|
|
2021-12-25 06:45:52 +00:00
|
|
|
/* only body is transferred on HTTP/0.9 */
|
2021-11-12 15:09:54 +00:00
|
|
|
case HTX_BLK_RES_SL:
|
|
|
|
case HTX_BLK_TLR:
|
|
|
|
case HTX_BLK_EOT:
|
|
|
|
default:
|
|
|
|
htx_remove_blk(htx, blk);
|
|
|
|
total += bsize;
|
|
|
|
count -= bsize;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-07 15:19:03 +00:00
|
|
|
end:
|
2021-11-12 15:09:54 +00:00
|
|
|
b_add(res, b_data(&outbuf));
|
2023-10-27 14:41:58 +00:00
|
|
|
htx_to_buf(htx, buf);
|
2021-11-12 15:09:54 +00:00
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2023-12-07 09:36:36 +00:00
|
|
|
static size_t hq_interop_nego_ff(struct qcs *qcs, size_t count)
|
|
|
|
{
|
2023-11-13 13:57:28 +00:00
|
|
|
struct buffer *res = qcc_get_stream_txbuf(qcs);
|
2023-12-07 09:36:36 +00:00
|
|
|
|
|
|
|
if (!b_room(res)) {
|
|
|
|
qcs->flags |= QC_SF_BLK_MROOM;
|
|
|
|
qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No header required for HTTP/0.9, no need to reserve an offset. */
|
|
|
|
qcs->sd->iobuf.buf = res;
|
|
|
|
qcs->sd->iobuf.offset = 0;
|
|
|
|
qcs->sd->iobuf.data = 0;
|
|
|
|
|
|
|
|
end:
|
|
|
|
return MIN(b_contig_space(res), count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t hq_interop_done_ff(struct qcs *qcs)
|
|
|
|
{
|
|
|
|
const size_t ret = qcs->sd->iobuf.data;
|
|
|
|
|
|
|
|
/* No header required for HTTP/0.9, simply mark ff as done. */
|
|
|
|
qcs->sd->iobuf.buf = NULL;
|
|
|
|
qcs->sd->iobuf.offset = 0;
|
|
|
|
qcs->sd->iobuf.data = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-08-03 09:17:57 +00:00
|
|
|
static int hq_interop_attach(struct qcs *qcs, void *conn_ctx)
|
|
|
|
{
|
|
|
|
qcs_wait_http_req(qcs);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-11-12 15:09:54 +00:00
|
|
|
const struct qcc_app_ops hq_interop_ops = {
|
|
|
|
.decode_qcs = hq_interop_decode_qcs,
|
|
|
|
.snd_buf = hq_interop_snd_buf,
|
2023-12-07 09:36:36 +00:00
|
|
|
.nego_ff = hq_interop_nego_ff,
|
|
|
|
.done_ff = hq_interop_done_ff,
|
2022-08-03 09:17:57 +00:00
|
|
|
.attach = hq_interop_attach,
|
2021-11-12 15:09:54 +00:00
|
|
|
};
|