/*****************************************************************************/
/*
                                  http2.h

*/
/*****************************************************************************/

#ifndef HTTP2_H_LOADED
#define HTTP2_H_LOADED 1

#include "hpack.h"
#include "wasd.h"
#include "types.h"

/**********/
/* config */
/**********/

/* send a ping every this many seconds */
#define HTTP2_PING_SECONDS_DEFAULT 300

/* seconds maximum between pings */
#define HTTP2_PING_SECONDS_MAX 60 * 60  /* ONE HOUR! */

/* seconds maximum to respond to a ping */
#define HTTP2_PING_RESPONSE_MAX 60

/* i.e. default of fifteen minutes */
#define HTTP2_TIMEOUT_IDLE_SECONDS 60 * 15

/* i.e. this many seconds after a goaway has been received or sent */
#define HTTP2_TIMEOUT_GOAWAY_SECONDS 60

/* keep completed streams for this many seconds */
#define HTTP2_DROP_SECONDS 5

/* see Http2FlowCheck() */
#define HTTP2_FLOW_CHECK 2

/* mitigate potential DoS attacks - all per second */
#define HTTP2_EMPTY_FRAME_LIMIT_COUNT   16
#define HTTP2_PING_LIMIT_COUNT          16
#define HTTP2_SETTINGS_LIMIT_COUNT      32
#define HTTP2_STREAM_RESET_LIMIT_COUNT  32
#define HTTP2_ZERO_HEADER_LIMIT_COUNT   16

/* minimum concurrent requests before Rapid Reset mitigation CVE-2023-44487 */
#define HTTP2_RST_STREAM_MIN            20

/**********/
/* macros */
/**********/

/* 8 bit frame type codes (RFC7540 6.n) */
#define HTTP2_FRAME_DATA           0x00  /* */
#define HTTP2_FRAME_HEADERS        0x01  /* */
#define HTTP2_FRAME_PRIORITY       0x02  /* */
#define HTTP2_FRAME_RST_STREAM     0x03  /* */
#define HTTP2_FRAME_SETTINGS       0x04  /* */
#define HTTP2_FRAME_PUSH_PROMISE   0x05  /* */
#define HTTP2_FRAME_PING           0x06  /* */
#define HTTP2_FRAME_GOAWAY         0x07  /* */
#define HTTP2_FRAME_WINDOW_UPDATE  0x08  /* */
#define HTTP2_FRAME_CONTINUATION   0x09  /* */

/* 8 bit flag bits */
#define HTTP2_FLAG_ACK             0x01  /* */
#define HTTP2_FLAG_END_STR         0x01  /* */
#define HTTP2_FLAG_END_HEAD        0x04  /* */
#define HTTP2_FLAG_PADDED          0x08  /* */
#define HTTP2_FLAG_PRIORITY        0x20  /* */

/* 16 bit setting codes (RFC7540 6.5.1) */
#define HTTP2_SETTING_MAX_HEAD_TABLE_SIZE 0x1  /* header table size */
#define HTTP2_SETTING_ENABLE_PUSH         0x2  /* server push enabled */
#define HTTP2_SETTING_MAX_CONC_STREAMS    0x3  /* max concurrent streams */
#define HTTP2_SETTING_INIT_WIN_SIZE       0x4  /* initial window size */
#define HTTP2_SETTING_MAX_FRAME_SIZE      0x5  /* max frame size */
#define HTTP2_SETTING_MAX_HEAD_LIST_SIZE  0x6  /* max header list size */

/* initial setting values (RFC7540 11.3) */
#define HTTP2_INITIAL_HEAD_TAB_SIZE      4096  /* bytes */
#define HTTP2_MAX_HEAD_TAB_SIZE         65535  /* bytes */
#define HTTP2_INITIAL_ENABLE_PUSH           0  /* disabled */
#define HTTP2_INITIAL_MAX_CONC_STREAMS    100  /* RFC recommended */
#define HTTP2_INITIAL_MAX_FRAME_SIZE    16384  /* bytes */
#define HTTP2_MIN_INITIAL_MAX_CONC_STREAMS  8  /* silly going lower */
#define HTTP2_MAX_CONC_STREAMS           1024  /* seems enormous */
#define HTTP2_MIN_FRAME_SIZE            16384  /* bytes */
#define HTTP2_MAX_FRAME_SIZE         16777215  /* bytes */
#define HTTP2_INITIAL_WINDOW_SIZE       65535  /* bytes */
#define HTTP2_DEFAULT_WINDOW_SIZE      131070  /* bytes */
#define HTTP2_MAX_WINDOW_SIZE         4194303  /* bytes */
#define HTTP2_DISABLE_WINDOW_SIZE  0x7fffffff  /* bytes */
#define HTTP2_INITIAL_HEAD_LIST_SIZE    65535  /* bytes */
#define HTTP2_MAX_HEAD_LIST_SIZE        65535  /* bytes */

/* 32 bit error code values (RFC7540 11.4) */
#define HTTP2_ERROR_NONE      0x0  /* graceful shutdown */
#define HTTP2_ERROR_PROTOCOL  0x1  /* protocol error detected */
#define HTTP2_ERROR_INTERNAL  0x2  /* implementation fault */
#define HTTP2_ERROR_FLOW      0x3  /* flow-control limits exceeded */
#define HTTP2_ERROR_TIMEOUT   0x4  /* settings not acknowledged */
#define HTTP2_ERROR_CLOSED    0x5  /* frame received for closed stream */
#define HTTP2_ERROR_SIZE      0x6  /* frame size incorrect */
#define HTTP2_ERROR_REFUSED   0x7  /* stream not processed */
#define HTTP2_ERROR_CANCEL    0x8  /* stream cancelled */
#define HTTP2_ERROR_COMPRESS  0x9  /* compression state not updated */
#define HTTP2_ERROR_CONNECT   0xa  /* TCP connection error for CONNECT */
#define HTTP2_ERROR_CALM      0xb  /* processing capacity exceeded */
#define HTTP2_ERROR_SECURITY  0xc  /* negotiated TLS params not acceptable */
#define HTTP2_ERROR_HTTP11    0xd  /* use HTTP/1.1 for the request */
#define HTTP2_ERROR_COUNT     14   /* number of HTTP/2 errors */

/* stream states (RFC7540 5.1) */
#define HTTP2_STATE_IDLE          0x1 
#define HTTP2_STATE_RESERVED_LOC  0x2
#define HTTP2_STATE_RESERVED_REM  0x3
#define HTTP2_STATE_OPEN          0x4
#define HTTP2_STATE_CLOSED_LOC    0x5
#define HTTP2_STATE_CLOSED_REM    0x6
#define HTTP2_STATE_CLOSED        0x7

/* number of octets in the frame header */
#define HTTP2_FRAME_HEADER_SIZE 9

/* queue indices and path SETings */
#define HTTP2_WRITE_QUEUE_MAX     0  /* for connection management */
#define HTTP2_WRITE_QUEUE_HIGH    1
#define HTTP2_WRITE_QUEUE_NORMAL  2  /* (default) */
#define HTTP2_WRITE_QUEUE_LOW     3

/* sentinal tick seconds when dropping stream */
#define HTTP2_STREAM_DROPPED  1
#define HTTP2_STREAM_DROP_NOW 2

/*********************/
/* functional macros */
/*********************/

#define HTTP2_REQUEST(ptr) ((ptr) && (ptr)->Http2Ptr)
                           
#define HTTP2_PEEK_32(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 24); \
  val += (uint)(((uchar*)ptr)[1] << 16); \
  val += (uint)(((uchar*)ptr)[2] << 8); \
  val += (uint)(((uchar*)ptr)[3]); }

#define HTTP2_GET_32(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 24); \
  val += (uint)(((uchar*)ptr)[1] << 16); \
  val += (uint)(((uchar*)ptr)[2] << 8); \
  val += (uint)(((uchar*)ptr)[3]); \
  (uchar*)(ptr) += 4; }

#define HTTP2_PEEK_24(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 16); \
  val += (uint)(((uchar*)ptr)[1] << 8); \
  val += (uint)(((uchar*)ptr)[2]); }

#define HTTP2_GET_24(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 16); \
  val += (uint)(((uchar*)ptr)[1] << 8); \
  val += (uint)(((uchar*)ptr)[2]); \
  (uchar*)(ptr) += 3; }

#define HTTP2_PEEK_16(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 8); \
  val += (uint)(((uchar*)ptr)[1]); }

#define HTTP2_GET_16(ptr,val) \
{ val =  (uint)(((uchar*)ptr)[0] << 8); \
  val += (uint)(((uchar*)ptr)[1]); \
  (uchar*)(ptr) += 2; }

#define HTTP2_PEEK_8(ptr,val) \
{ val = (uint)(*((uchar*)ptr)); }

#define HTTP2_GET_8(ptr,val) \
{ val = (uint)(*((uchar*)ptr)); (uchar*)(ptr) += 1; }

#define HTTP2_PLACE_32(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff000000) >> 24; \
  ((uchar*)ptr)[1] = (val & 0xff0000) >> 16; \
  ((uchar*)ptr)[2] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[3] = val & 0xff; }

#define HTTP2_PUT_32(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff000000) >> 24; \
  ((uchar*)ptr)[1] = (val & 0xff0000) >> 16; \
  ((uchar*)ptr)[2] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[3] = val & 0xff; \
  (uchar*)(ptr) += 4; }

#define HTTP2_PLACE_24(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff0000) >> 16; \
  ((uchar*)ptr)[1] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[2] = val & 0xff; }

#define HTTP2_PUT_24(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff0000) >> 16; \
  ((uchar*)ptr)[1] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[2] = val & 0xff; \
  (uchar*)(ptr) += 3; }

#define HTTP2_PLACE_16(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[1] = val & 0xff; }

#define HTTP2_PUT_16(ptr,val) \
{ ((uchar*)ptr)[0] = (val & 0xff00) >> 8; \
  ((uchar*)ptr)[1] = val & 0xff; \
  (uchar*)(ptr) += 2; }

#define HTTP2_PLACE_8(ptr,val) \
{ *((uchar*)ptr) = val & 0xff; }

#define HTTP2_PUT_8(ptr,val) \
{ *((uchar*)ptr) = val & 0xff; (uchar*)(ptr)++; }

/*********************/
/* HTTP/2 structures */
/*********************/

#pragma member_alignment __save
#pragma member_alignment

typedef struct Http2Struct HTTP2_STRUCT;
typedef struct Http2HeaderStruct HTTP2_HEADER_STRUCT;
typedef struct Http2StreamStruct HTTP2_STREAM_STRUCT;
typedef struct Http2WriteStruct HTTP2_WRITE_STRUCT;

/* default space for payload without allocating extra */
#define HTTP2_WRITE_PAYLOAD 48

struct Http2HeaderStruct
{
   union frame_header2
   {
      uchar header [9];
      struct
      { 
         uchar  length [3];
         uchar  type   [1];
         uchar  flags  [1];
         uchar  ident  [4];
      };
   };
};

struct Http2WriteStruct
{
   LIST_ENTRY  ListEntry;

   HTTP2_STRUCT  *Http2Ptr;
   HTTP2_WRITE_STRUCT  *Write2Ptr;
   REQUEST_STRUCT  *RequestPtr;

   BOOL  WriteInProgress;

   uint  DataLength,
         DataSize,
         WriteCount,
         WriteQueue;

   void  *AstFunction,
         *AstParam,
         *DataPtr,
         *HeaderPtr;

   /* fill to longword boundary */
   uchar  filler [3];

   /* header fields */
   union header2
   {
      uchar header [9];
      struct
      { 
         uchar  length [3];
         uchar  type   [1];
         uchar  flags  [1];
         uchar  ident  [4];
      };
   };

   /* this chunk is pre-allocated for small control frames and the like */
   uchar  payload [HTTP2_WRITE_PAYLOAD];
   /* for appropriately allocated buffers further payload from here */
};

struct Http2StreamStruct
{
   /* for maintaining the list of HTTP/2 stream structures */
   LIST_ENTRY  ListEntry;

   HTTP2_STRUCT  *Http2Ptr;
   REQUEST_STRUCT  *RequestPtr;

   /* connection stream count number (1..n) */
   uint  StreamNumber;

   /* tick seconds at drop */
   uint  DropTickSecond;

   /* stream characteristics */
   uint  Depend,
         Ident,
         Priority,
         Weight;

   /* stream flow control */
   int  ReadWindowSize,
        WriteWindowSize;

   /* headers management */
   uint  ContinSize;
   uchar  *ContinPtr;

   /* I/O outstanding */
   int  QueuedWriteCount;

   /* tick seconds flow control stalled */
   uint  FlowStallCount,
         FlowStallTally,
         FlowStallTickSecond;

   /* buffered data from client */
   uchar  *DataReadPtr;
   int   DataReadLength,
         DataReadSize;

   /* HTTP/2 stream management */
   BOOL  RequestEnd,
         ResponseSent,
         StreamCancel,
         StreamClosed,
         StreamDataEnd,
         StreamEnd,
         StreamError,
         StreamHeaderEnd,
         StreamHeaderStart,
         StreamOpen;

   BOOL  WatchTrigger,
         WatchTriggerPlusRx,
         WatchTriggerTx;
};

struct  Http2Struct
{
   /* for maintaining the list of HTTP/2 structures */
   LIST_ENTRY  ListEntry;

   /* VM zone for this HTTP/2 structure (keep at the top) */
   ulong  VmHeapZoneId;

   NETIO_STRUCT  /* pointer to network I/O structure */
                 *NetIoPtr;

   int  /* this HTTP/2 connection is being WATCHed */
        WatchItem,
        /* Http2NetClientReadAst() in use (should only ever be one!) */
        ReadInUseCount,
        /* number HTTP2_WRITE_STRUCT currently in use (issue if negative) */
        WriteInUseCount;

   /* more general fields */

   BOOL  /* connection is closing */
         ConnectClosing,
         /* if HTTP/2 over http: */
         ExpectingH2cPreface;

   int  /* connection flow-control */
        ReadWindowSize,
        WriteWindowSize;

   uint  /* length of client's host name / IP address */
         ClientHostNameLength,
         /* setting - initial windows size */
         ClientInitialWindowSize,
         /* setting - maximum concurrent streams */
         ClientMaxConcStreams,
         /* setting - maximum frame size */
         ClientMaxFrameSize,
         /* setting - maximum header list size */
         ClientMaxHeaderListSize,
         /* setting - client header table size */
         ClientMaxHeaderTableSize,
         /* setting - client push promise */
         ClientPushPromise,
         /* propogated from original (upgrade) request */
         ConnectNumber,
         /* limit empty frames per second (DoS?) */
         EmptyFrameLimitCount,
         /* number of frames streams subject to flow control */
         FlowFrameCount,
         /* number of frames streams subject to flow control */
         FlowFrameTally,
         /* number of frames streams had flow control stalled */
         FlowStallCount,
         /* number of frames streams had flow control stalled */
         FlowStallTally,
         /* tick seconds flow control stalled */
         FlowStallTickSecond,
         /* number of frames received */
         FrameCountRx,
         /* number of frames transmitted */
         FrameCountTx,
         /* number of frames already added into the accounting */
         FrameTallyRx,
         /* number of frames already added into the accounting */
         FrameTallyTx,
         /* number of request (headers plus data) frames received */
         FrameRequestCountRx,
         /* number of request (headers plus data) frames transmitted */
         FrameRequestCountTx,
         /* number of frames already added into the accounting */
         FrameRequestTallyRx,
         /* number of frames already added into the accounting */
         FrameRequestTallyTx,
         /* server sent goaway with last client stream identifier */
         GoAwayIdent,
         /* most client's go-away last stream identifier */
         GoAwayLastStreamIdent,
         /* the (Unix) second the connection should be terminated */
         GoAwayTickSecond,
         /* last header ident processed */
         HeaderLastIdent,
         /* this header ident processed */
         HeaderThisIdent,
         /* byte count before decompression */
         HpackClientInputCount,
         /* bytes count after decompression */
         HpackClientOutputCount,
         /* byte count before compression */
         HpackServerInputCount,
         /* bytes count after compression */
         HpackServerOutputCount,
         /* the (Unix) second the connection becomes idle */
         IdleTickSecond,
         /* most recent stream identifier from client */
         LastStreamIdent,
         /* most recent ping tick second (for timeout purposes) */
         PingBackTickSecond,
         /* limit number of pings per second (DoS?) */
         PingLimitCount,
         /* most recent ping round-trip in microseconds */
         PingMicroSeconds,
         /* send a ping after this tick second (essentially a timeout) */
         PingSendTickSecond,
         /* peak number of writes queued */
         QueuedWritePeak,
         /* read buffer count */
         ReadBufferCount,
         /* read buffer size */
         ReadBufferSize,
         /* total number of requests processed on this connection */
         RequestCount,
         /* current number of concurrent requests */
         RequestCurrent,
         /* maximum number of concurrent requests */
         RequestPeak,
         /* there have been this many RST_STREAM in the last second */
         RstStreamCount,
         /* setting - server header table size */
         ServerHeaderTableSize,
         /* setting - initial windows size */
         ServerInitialWindowSize,
         /* setting - maximum concurrent streams */
         ServerMaxConcStreams,
         /* setting - maximum frame size */
         ServerMaxFrameSize,
         /* maximum number of bytes for HPACK dynamic table */
         ServerMaxHeaderTableSize,
         /* setting - maximum header list size */
         ServerMaxHeaderListSize,
         /* setting - server push promise */
         ServerPushPromise,
         /* limit number of settings per second (DoS?) */
         SettingsLimitCount,
         /* number of streams (so far) */
         StreamCount,
         /* limit number of stream resets per second (DoS?) */
         StreamResetLimitCount,
         /* limit number of zero-length headers per second (DoS?) */
         ZeroHeaderLimitCount;

   int64  /* bytes received on the connection */
          BytesRawRx64,
          /* bytes transmitted on the connection */
          BytesRawTx64,
          /* bytes received since last accumulated */
          BytesRawTallyRx64,
          /* bytes transmitted since last accumulated */
          BytesRawTallyTx64,
          /* 64 bit time when client connected */
          ConnectTime64,
          /* 64 bit time when ping originated */
          PingTime64;

   uchar  *ReadBufferPtr;

   HPACK_TABLE_STRUCT  HpackClientTable,
                       HpackServerTable;

   /* a list of associated streams (which are requests) */
   LIST_HEAD  StreamList;

   LIST_HEAD  QueuedWriteList [HTTP2_WRITE_QUEUE_LOW+1];

   CLIENT_STRUCT  *ClientPtr;
   SERVICE_STRUCT  *ServicePtr;
};

#pragma member_alignment __restore

/***********************/
/* function prototypes */
/***********************/

void Http2Begin (REQUEST_STRUCT*);
void Http2ConnectClose (HTTP2_STRUCT*);
HTTP2_STRUCT* Http2ConnectGet (REQUEST_STRUCT*);
int Http2DoError (HTTP2_STRUCT*, uint, uint, char*, int);
int Http2Error (HTTP2_STRUCT*, int, char*, int);
char* Http2ErrorString (int);
void Http2FlowControl (HTTP2_STRUCT*, uint);
void Http2FlowStallNoticed (char*, int,
                            HTTP2_STRUCT*, HTTP2_STREAM_STRUCT*, int);
int Http2GoAway (HTTP2_STRUCT*, uint, uint, uchar*, uint);
void Http2Init ();
void Http2Report (REQUEST_STRUCT*);
int Http2Ping (HTTP2_STRUCT*, uint, uint, uchar*, uint);
BOOL Http2Preface (REQUEST_STRUCT*);
int Http2Priority (HTTP2_STRUCT*, uint, uint, uchar*, uint);
int Http2Settings (HTTP2_STRUCT*, uint, uint, uchar*, uint);
void Http2SetWatch (HTTP2_STRUCT*, int);
HTTP2_STREAM_STRUCT* Http2StreamDrop (HTTP2_STREAM_STRUCT*);
void Http2StreamEnd (HTTP2_STREAM_STRUCT*);
HTTP2_STREAM_STRUCT* Http2StreamGet (HTTP2_STRUCT*, uint);
void Http2StreamFlow (HTTP2_STRUCT*);
void Http2StreamPurge (HTTP2_STRUCT*);
int Http2StreamReset (HTTP2_STRUCT*, HTTP2_STREAM_STRUCT*, uint, uchar*, uint);
BOOL Http2SwitchResponse (REQUEST_STRUCT*);
BOOL Http2Supervisor ();
void Http2TestBreak (int);
int Http2WindowUpdate (HTTP2_STRUCT*, HTTP2_STREAM_STRUCT*,
                       uint, uint, uchar*, uint);
HTTP2_WRITE_STRUCT* Http2GetWriteStruct (HTTP2_STRUCT*, int, char*, int);
void Http2FreeWriteStruct (HTTP2_STRUCT*, HTTP2_WRITE_STRUCT*, char*, int);
void Http2ZeroAccounting ();

BOOL Http2NetCancelWrite (REQUEST_STRUCT*);
int Http2NetControl (int);
int Http2NetClientRead (HTTP2_STRUCT*);
void Http2NetClientReadAst (HTTP2_STRUCT*);
void Http2NetClientEnd (HTTP2_STRUCT*, uint);
int Http2NetIoRead (NETIO_STRUCT*);
int Http2NetIoWrite (NETIO_STRUCT*, VOID_AST, void*, void*, uint);
BOOL Http2NetIoInProgress (NETIO_STRUCT*);
void Http2NetQueueWrite (HTTP2_STRUCT*, HTTP2_WRITE_STRUCT*);
void Http2NetWriteDataAst (HTTP2_WRITE_STRUCT*);
void Http2NetWriteEnd (REQUEST_STRUCT*);
void Http2NetWriteHeaderAst (HTTP2_WRITE_STRUCT*);
void Http2Net_WRITE_NO_AST (void*);

REQUEST_STRUCT* Http2RequestBegin (HTTP2_STREAM_STRUCT*);
void Http2RequestCancel (REQUEST_STRUCT*);
int Http2RequestData (HTTP2_STRUCT*, HTTP2_STREAM_STRUCT*, uint, uchar*, uint);
void Http2RequestDataClose (REQUEST_STRUCT*);
void Http2RequestEnd2 (REQUEST_STRUCT*);
BOOL Http2RequestEnd4 (REQUEST_STRUCT*);
void Http2RequestEnd5 (REQUEST_STRUCT*);
void Http2RequestProcess (REQUEST_STRUCT*);
BOOL Http2RequestQuiescent (REQUEST_STRUCT*);
int Http2RequestResetStream (REQUEST_STRUCT*);
int Http2RequestResponse (REQUEST_STRUCT*, REQUEST_AST, char*, int);
int Http2ResponseDictHeader (REQUEST_STRUCT*, REQUEST_AST);

void Http2WatchData (HTTP2_STRUCT*, uint, uint, uchar*, uint, uint);
void Http2WatchError (HTTP2_STRUCT*, uint);
void Http2WatchFrame (HTTP2_STRUCT*, uchar*, uchar*, uint, BOOL);
void Http2WatchGoAway (HTTP2_STRUCT*, uchar*, uint);
void Http2WatchHeaders (HTTP2_STRUCT*, uint, uint, uchar*, uint, uint);
void Http2WatchPing (HTTP2_STRUCT*, uchar*, uint);
void Http2WatchPriority (HTTP2_STRUCT*, uint, uchar*, uint);
void Http2WatchPushPromise (HTTP2_STRUCT*, uint, uint, uchar*, uint);
void Http2WatchRstStream (HTTP2_STRUCT*, uint, uchar*, uint);
void Http2WatchSettings (HTTP2_STRUCT*, uchar*, uchar*, uint);
void Http2WatchWindowUpdate (HTTP2_STRUCT*, uint, uchar*, uint);

#endif /* HTTP2_H_LOADED */

/*****************************************************************************/

