From 6c0fadfb7df3a923a9765beac9a38e5d0ceccd1d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 12 Sep 2022 19:07:51 +0200 Subject: [PATCH] REORG: mux-h2: extract flags and enums into mux_h2-t.h Originally in 1.8 we wanted to have an independent mux that could possibly be disabled and would not impose dependencies on the outside. Everything would fit into a single C file and that was fine. Nowadays muxes are unavoidable, and not being able to easily inspect them from outside is sometimes a bit of a pain. In particular, the flags utility still cannot be used to decode their flags. As a first step towards this, this patch moves the flags and enums to mux_h2-t.h, as well as the two state decoding inline functions. It also dropped the H2_SS_*_BIT defines that nobody uses. The mux_h2.c file remains the only one to include that for now. --- include/haproxy/mux_h2-t.h | 170 +++++++++++++++++++++++++++++++++++++ src/mux_h2.c | 149 +------------------------------- 2 files changed, 172 insertions(+), 147 deletions(-) create mode 100644 include/haproxy/mux_h2-t.h diff --git a/include/haproxy/mux_h2-t.h b/include/haproxy/mux_h2-t.h new file mode 100644 index 000000000..fb2c78cdf --- /dev/null +++ b/include/haproxy/mux_h2-t.h @@ -0,0 +1,170 @@ +/* + * include/haproxy/mux_h2-t.h + * Definitions for basic H2 mux internal types, constants and flags. + * + * Copyright 2017-2022 Willy Tarreau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _HAPROXY_MUX_H2_T_H +#define _HAPROXY_MUX_H2_T_H + +#include + +/**** Connection flags (32 bit), in h2c->flags ****/ + +#define H2_CF_NONE 0x00000000 + +/* Flags indicating why writing to the mux is blocked. */ +#define H2_CF_MUX_MALLOC 0x00000001 // mux blocked on lack of connection's mux buffer +#define H2_CF_MUX_MFULL 0x00000002 // mux blocked on connection's mux buffer full +#define H2_CF_MUX_BLOCK_ANY 0x00000003 // aggregate of the mux flags above + +/* Flags indicating why writing to the demux is blocked. + * The first two ones directly affect the ability for the mux to receive data + * from the connection. The other ones affect the mux's ability to demux + * received data. + */ +#define H2_CF_DEM_DALLOC 0x00000004 // demux blocked on lack of connection's demux buffer +#define H2_CF_DEM_DFULL 0x00000008 // demux blocked on connection's demux buffer full + +#define H2_CF_DEM_MBUSY 0x00000010 // demux blocked on connection's mux side busy +#define H2_CF_DEM_MROOM 0x00000020 // demux blocked on lack of room in mux buffer +#define H2_CF_DEM_SALLOC 0x00000040 // demux blocked on lack of stream's request buffer +#define H2_CF_DEM_SFULL 0x00000080 // demux blocked on stream request buffer full +#define H2_CF_DEM_TOOMANY 0x00000100 // demux blocked waiting for some stream connectors to leave +#define H2_CF_DEM_BLOCK_ANY 0x000001F0 // aggregate of the demux flags above except DALLOC/DFULL + // (SHORT_READ is also excluded) + +#define H2_CF_DEM_SHORT_READ 0x00000200 // demux blocked on incomplete frame +#define H2_CF_DEM_IN_PROGRESS 0x00000400 // demux in progress (dsi,dfl,dft are valid) + +/* other flags */ +#define H2_CF_GOAWAY_SENT 0x00001000 // a GOAWAY frame was successfully sent +#define H2_CF_GOAWAY_FAILED 0x00002000 // a GOAWAY frame failed to be sent +#define H2_CF_WAIT_FOR_HS 0x00004000 // We did check that at least a stream was waiting for handshake +#define H2_CF_IS_BACK 0x00008000 // this is an outgoing connection +#define H2_CF_WINDOW_OPENED 0x00010000 // demux increased window already advertised +#define H2_CF_RCVD_SHUT 0x00020000 // a recv() attempt already failed on a shutdown +#define H2_CF_END_REACHED 0x00040000 // pending data too short with RCVD_SHUT present + +#define H2_CF_RCVD_RFC8441 0x00100000 // settings from RFC8441 has been received indicating support for Extended CONNECT +#define H2_CF_SHTS_UPDATED 0x00200000 // SETTINGS_HEADER_TABLE_SIZE updated +#define H2_CF_DTSU_EMITTED 0x00400000 // HPACK Dynamic Table Size Update opcode emitted + + +/**** HTTP/2 stream flags (32 bit), in h2s->flags ****/ + +#define H2_SF_NONE 0x00000000 +#define H2_SF_ES_RCVD 0x00000001 +#define H2_SF_ES_SENT 0x00000002 + +#define H2_SF_RST_RCVD 0x00000004 // received RST_STREAM +#define H2_SF_RST_SENT 0x00000008 // sent RST_STREAM + +/* stream flags indicating the reason the stream is blocked */ +#define H2_SF_BLK_MBUSY 0x00000010 // blocked waiting for mux access (transient) +#define H2_SF_BLK_MROOM 0x00000020 // blocked waiting for room in the mux (must be in send list) +#define H2_SF_BLK_MFCTL 0x00000040 // blocked due to mux fctl (must be in fctl list) +#define H2_SF_BLK_SFCTL 0x00000080 // blocked due to stream fctl (must be in blocked list) +#define H2_SF_BLK_ANY 0x000000F0 // any of the reasons above + +/* stream flags indicating how data is supposed to be sent */ +#define H2_SF_DATA_CLEN 0x00000100 // data sent using content-length +#define H2_SF_BODYLESS_RESP 0x00000200 /* Bodyless response message */ +#define H2_SF_BODY_TUNNEL 0x00000400 // Attempt to establish a Tunnelled stream (the result depends on the status code) + +#define H2_SF_NOTIFIED 0x00000800 // a paused stream was notified to try to send again +#define H2_SF_HEADERS_SENT 0x00001000 // a HEADERS frame was sent for this stream +#define H2_SF_OUTGOING_DATA 0x00002000 // set whenever we've seen outgoing data + +#define H2_SF_HEADERS_RCVD 0x00004000 // a HEADERS frame was received for this stream + +#define H2_SF_WANT_SHUTR 0x00008000 // a stream couldn't shutr() (mux full/busy) +#define H2_SF_WANT_SHUTW 0x00010000 // a stream couldn't shutw() (mux full/busy) + +#define H2_SF_EXT_CONNECT_SENT 0x00040000 // rfc 8441 an Extended CONNECT has been sent +#define H2_SF_EXT_CONNECT_RCVD 0x00080000 // rfc 8441 an Extended CONNECT has been received and parsed + +#define H2_SF_TUNNEL_ABRT 0x00100000 // A tunnel attempt was aborted +#define H2_SF_MORE_HTX_DATA 0x00200000 // more data expected from HTX + + +/* H2 connection state, in h2c->st0 */ +enum h2_cs { + H2_CS_PREFACE, // init done, waiting for connection preface + H2_CS_SETTINGS1, // preface OK, waiting for first settings frame + H2_CS_FRAME_H, // first settings frame ok, waiting for frame header + H2_CS_FRAME_P, // frame header OK, waiting for frame payload + H2_CS_FRAME_A, // frame payload OK, trying to send ACK frame + H2_CS_FRAME_E, // frame payload OK, trying to send RST frame + H2_CS_ERROR, // send GOAWAY(errcode) and close the connection ASAP + H2_CS_ERROR2, // GOAWAY(errcode) sent, close the connection ASAP + H2_CS_ENTRIES // must be last +} __attribute__((packed)); + +/* H2 stream state, in h2s->st */ +enum h2_ss { + H2_SS_IDLE = 0, // idle + H2_SS_RLOC, // reserved(local) + H2_SS_RREM, // reserved(remote) + H2_SS_OPEN, // open + H2_SS_HREM, // half-closed(remote) + H2_SS_HLOC, // half-closed(local) + H2_SS_ERROR, // an error needs to be sent using RST_STREAM + H2_SS_CLOSED, // closed + H2_SS_ENTRIES // must be last +} __attribute__((packed)); + + +/* 32 buffers: one for the ring's root, rest for the mbuf itself */ +#define H2C_MBUF_CNT 32 + +/**** tiny state decoding functions for debug helpers ****/ + +/* returns a h2c state as an abbreviated 3-letter string, or "???" if unknown */ +static inline const char *h2c_st_to_str(enum h2_cs st) +{ + switch (st) { + case H2_CS_PREFACE: return "PRF"; + case H2_CS_SETTINGS1: return "STG"; + case H2_CS_FRAME_H: return "FRH"; + case H2_CS_FRAME_P: return "FRP"; + case H2_CS_FRAME_A: return "FRA"; + case H2_CS_FRAME_E: return "FRE"; + case H2_CS_ERROR: return "ERR"; + case H2_CS_ERROR2: return "ER2"; + default: return "???"; + } +} + +/* returns a h2s state as an abbreviated 3-letter string, or "???" if unknown */ +static inline const char *h2s_st_to_str(enum h2_ss st) +{ + switch (st) { + case H2_SS_IDLE: return "IDL"; // idle + case H2_SS_RLOC: return "RSL"; // reserved local + case H2_SS_RREM: return "RSR"; // reserved remote + case H2_SS_OPEN: return "OPN"; // open + case H2_SS_HREM: return "HCR"; // half-closed remote + case H2_SS_HLOC: return "HCL"; // half-closed local + case H2_SS_ERROR : return "ERR"; // error + case H2_SS_CLOSED: return "CLO"; // closed + default: return "???"; + } +} + +#endif /* _HAPROXY_MUX_H2_T_H */ diff --git a/src/mux_h2.c b/src/mux_h2.c index 6ee361568..b0fb03678 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -38,64 +39,8 @@ static const struct h2s *h2_error_stream; static const struct h2s *h2_refused_stream; static const struct h2s *h2_idle_stream; -/* Connection flags (32 bit), in h2c->flags */ -#define H2_CF_NONE 0x00000000 -/* Flags indicating why writing to the mux is blocked. */ -#define H2_CF_MUX_MALLOC 0x00000001 // mux blocked on lack of connection's mux buffer -#define H2_CF_MUX_MFULL 0x00000002 // mux blocked on connection's mux buffer full -#define H2_CF_MUX_BLOCK_ANY 0x00000003 // aggregate of the mux flags above - -/* Flags indicating why writing to the demux is blocked. - * The first two ones directly affect the ability for the mux to receive data - * from the connection. The other ones affect the mux's ability to demux - * received data. - */ -#define H2_CF_DEM_DALLOC 0x00000004 // demux blocked on lack of connection's demux buffer -#define H2_CF_DEM_DFULL 0x00000008 // demux blocked on connection's demux buffer full - -#define H2_CF_DEM_MBUSY 0x00000010 // demux blocked on connection's mux side busy -#define H2_CF_DEM_MROOM 0x00000020 // demux blocked on lack of room in mux buffer -#define H2_CF_DEM_SALLOC 0x00000040 // demux blocked on lack of stream's request buffer -#define H2_CF_DEM_SFULL 0x00000080 // demux blocked on stream request buffer full -#define H2_CF_DEM_TOOMANY 0x00000100 // demux blocked waiting for some stream connectors to leave -#define H2_CF_DEM_BLOCK_ANY 0x000001F0 // aggregate of the demux flags above except DALLOC/DFULL - // (SHORT_READ is also excluded) - -#define H2_CF_DEM_SHORT_READ 0x00000200 // demux blocked on incomplete frame -#define H2_CF_DEM_IN_PROGRESS 0x00000400 // demux in progress (dsi,dfl,dft are valid) - -/* other flags */ -#define H2_CF_GOAWAY_SENT 0x00001000 // a GOAWAY frame was successfully sent -#define H2_CF_GOAWAY_FAILED 0x00002000 // a GOAWAY frame failed to be sent -#define H2_CF_WAIT_FOR_HS 0x00004000 // We did check that at least a stream was waiting for handshake -#define H2_CF_IS_BACK 0x00008000 // this is an outgoing connection -#define H2_CF_WINDOW_OPENED 0x00010000 // demux increased window already advertised -#define H2_CF_RCVD_SHUT 0x00020000 // a recv() attempt already failed on a shutdown -#define H2_CF_END_REACHED 0x00040000 // pending data too short with RCVD_SHUT present - -#define H2_CF_RCVD_RFC8441 0x00100000 // settings from RFC8441 has been received indicating support for Extended CONNECT -#define H2_CF_SHTS_UPDATED 0x00200000 // SETTINGS_HEADER_TABLE_SIZE updated -#define H2_CF_DTSU_EMITTED 0x00400000 // HPACK Dynamic Table Size Update opcode emitted - -/* H2 connection state, in h2c->st0 */ -enum h2_cs { - H2_CS_PREFACE, // init done, waiting for connection preface - H2_CS_SETTINGS1, // preface OK, waiting for first settings frame - H2_CS_FRAME_H, // first settings frame ok, waiting for frame header - H2_CS_FRAME_P, // frame header OK, waiting for frame payload - H2_CS_FRAME_A, // frame payload OK, trying to send ACK frame - H2_CS_FRAME_E, // frame payload OK, trying to send RST frame - H2_CS_ERROR, // send GOAWAY(errcode) and close the connection ASAP - H2_CS_ERROR2, // GOAWAY(errcode) sent, close the connection ASAP - H2_CS_ENTRIES // must be last -} __attribute__((packed)); - - -/* 32 buffers: one for the ring's root, rest for the mbuf itself */ -#define H2C_MBUF_CNT 32 - -/* H2 connection descriptor */ +/**** H2 connection descriptor ****/ struct h2c { struct connection *conn; @@ -151,64 +96,6 @@ struct h2c { struct wait_event wait_event; /* To be used if we're waiting for I/Os */ }; -/* H2 stream state, in h2s->st */ -enum h2_ss { - H2_SS_IDLE = 0, // idle - H2_SS_RLOC, // reserved(local) - H2_SS_RREM, // reserved(remote) - H2_SS_OPEN, // open - H2_SS_HREM, // half-closed(remote) - H2_SS_HLOC, // half-closed(local) - H2_SS_ERROR, // an error needs to be sent using RST_STREAM - H2_SS_CLOSED, // closed - H2_SS_ENTRIES // must be last -} __attribute__((packed)); - -#define H2_SS_MASK(state) (1UL << (state)) -#define H2_SS_IDLE_BIT (1UL << H2_SS_IDLE) -#define H2_SS_RLOC_BIT (1UL << H2_SS_RLOC) -#define H2_SS_RREM_BIT (1UL << H2_SS_RREM) -#define H2_SS_OPEN_BIT (1UL << H2_SS_OPEN) -#define H2_SS_HREM_BIT (1UL << H2_SS_HREM) -#define H2_SS_HLOC_BIT (1UL << H2_SS_HLOC) -#define H2_SS_ERROR_BIT (1UL << H2_SS_ERROR) -#define H2_SS_CLOSED_BIT (1UL << H2_SS_CLOSED) - -/* HTTP/2 stream flags (32 bit), in h2s->flags */ -#define H2_SF_NONE 0x00000000 -#define H2_SF_ES_RCVD 0x00000001 -#define H2_SF_ES_SENT 0x00000002 - -#define H2_SF_RST_RCVD 0x00000004 // received RST_STREAM -#define H2_SF_RST_SENT 0x00000008 // sent RST_STREAM - -/* stream flags indicating the reason the stream is blocked */ -#define H2_SF_BLK_MBUSY 0x00000010 // blocked waiting for mux access (transient) -#define H2_SF_BLK_MROOM 0x00000020 // blocked waiting for room in the mux (must be in send list) -#define H2_SF_BLK_MFCTL 0x00000040 // blocked due to mux fctl (must be in fctl list) -#define H2_SF_BLK_SFCTL 0x00000080 // blocked due to stream fctl (must be in blocked list) -#define H2_SF_BLK_ANY 0x000000F0 // any of the reasons above - -/* stream flags indicating how data is supposed to be sent */ -#define H2_SF_DATA_CLEN 0x00000100 // data sent using content-length -#define H2_SF_BODYLESS_RESP 0x00000200 /* Bodyless response message */ -#define H2_SF_BODY_TUNNEL 0x00000400 // Attempt to establish a Tunnelled stream (the result depends on the status code) - - -#define H2_SF_NOTIFIED 0x00000800 // a paused stream was notified to try to send again -#define H2_SF_HEADERS_SENT 0x00001000 // a HEADERS frame was sent for this stream -#define H2_SF_OUTGOING_DATA 0x00002000 // set whenever we've seen outgoing data - -#define H2_SF_HEADERS_RCVD 0x00004000 // a HEADERS frame was received for this stream - -#define H2_SF_WANT_SHUTR 0x00008000 // a stream couldn't shutr() (mux full/busy) -#define H2_SF_WANT_SHUTW 0x00010000 // a stream couldn't shutw() (mux full/busy) - -#define H2_SF_EXT_CONNECT_SENT 0x00040000 // rfc 8441 an Extended CONNECT has been sent -#define H2_SF_EXT_CONNECT_RCVD 0x00080000 // rfc 8441 an Extended CONNECT has been received and parsed - -#define H2_SF_TUNNEL_ABRT 0x00100000 // A tunnel attempt was aborted -#define H2_SF_MORE_HTX_DATA 0x00200000 // more data expected from HTX /* H2 stream descriptor, describing the stream as it appears in the H2C, and as * it is being processed in the internal HTTP representation (HTX). @@ -582,38 +469,6 @@ struct task *h2_deferred_shut(struct task *t, void *ctx, unsigned int state); static struct h2s *h2c_bck_stream_new(struct h2c *h2c, struct stconn *sc, struct session *sess); static void h2s_alert(struct h2s *h2s); -/* returns a h2c state as an abbreviated 3-letter string, or "???" if unknown */ -static inline const char *h2c_st_to_str(enum h2_cs st) -{ - switch (st) { - case H2_CS_PREFACE: return "PRF"; - case H2_CS_SETTINGS1: return "STG"; - case H2_CS_FRAME_H: return "FRH"; - case H2_CS_FRAME_P: return "FRP"; - case H2_CS_FRAME_A: return "FRA"; - case H2_CS_FRAME_E: return "FRE"; - case H2_CS_ERROR: return "ERR"; - case H2_CS_ERROR2: return "ER2"; - default: return "???"; - } -} - -/* returns a h2s state as an abbreviated 3-letter string, or "???" if unknown */ -static inline const char *h2s_st_to_str(enum h2_ss st) -{ - switch (st) { - case H2_SS_IDLE: return "IDL"; // idle - case H2_SS_RLOC: return "RSL"; // reserved local - case H2_SS_RREM: return "RSR"; // reserved remote - case H2_SS_OPEN: return "OPN"; // open - case H2_SS_HREM: return "HCR"; // half-closed remote - case H2_SS_HLOC: return "HCL"; // half-closed local - case H2_SS_ERROR : return "ERR"; // error - case H2_SS_CLOSED: return "CLO"; // closed - default: return "???"; - } -} - /* returns the stconn associated to the H2 stream */ static forceinline struct stconn *h2s_sc(const struct h2s *h2s) {