/*****************************************************************************/
/*
                                  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 3600  /* ONE HOUR! */

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

/* i.e. default of one hour */
#define HTTP2_TIMEOUT_IDLE_SECONDS 60 * 60

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

/* 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

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

/* 8 bit frame type codes (RFC7540 6.n) */
#define HTTP2_FRAME_DATA           0x0  /* */
#define HTTP2_FRAME_HEADERS        0x1  /* */
#define HTTP2_FRAME_PRIORITY       0x2  /* */
#define HTTP2_FRAME_RST_STREAM     0x3  /* */
#define HTTP2_FRAME_SETTINGS       0x4  /* */
#define HTTP2_FRAME_PUSH_PROMISE   0x5  /* */
#define HTTP2_FRAME_PING           0x6  /* */
#define HTTP2_FRAME_GOAWAY         0x7  /* */
#define HTTP2_FRAME_WINDOW_UPDATE  0x8  /* */
#define HTTP2_FRAME_CONTINUATION   0x9  /* */

/* 8 bit flag bits */
#define HTTP2_FLAG_CONT_END_HEAD  0x04  /* */
#define HTTP2_FLAG_DATA_END_STR   0x01  /* */
#define HTTP2_FLAG_DATA_PADDED    0x08  /* */
#define HTTP2_FLAG_HEAD_END_STR   0x01  /* */
#define HTTP2_FLAG_HEAD_END_HEAD  0x04  /* */
#define HTTP2_FLAG_HEAD_PADDED    0x08  /* */
#define HTTP2_FLAG_HEAD_PRIORITY  0x20  /* */
#define HTTP2_FLAG_PING_ACK       0x01  /* */
#define HTTP2_FLAG_PUSH_END_HEAD  0x04  /* */
#define HTTP2_FLAG_PUSH_PADDED    0x08  /* */
#define HTTP2_FLAG_SETTINGS_ACK   0x01  /* */

/* 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_MIN_INITIAL_MAX_CONC_STREAMS  8  /* silly going lower */
#define HTTP2_MAX_CONC_STREAMS           1024  /* seems enormous */
#define HTTP2_INITIAL_MAX_FRAME_SIZE    16384  /* bytes */
#define HTTP2_MAX_FRAME_SIZE            65535  /* max <= I/O status block */
#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

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

#define HTTP2_REQUEST(ptr) ((ptr) != NULL && \
                            (ptr)->Http2Stream.Http2Ptr != NULL)
                           
#define NOT_HTTP2_REQUEST(ptr) ((ptr) != NULL && \
                                (ptr)->Http2Stream.Http2Ptr == NULL)

#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 Http2DataReadStruct HTTP2_READ_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 Http2DataReadStruct
{
   LIST_ENTRY  ListEntry;
   uint  DataLength;
   uchar  DataPtr[];  /* bit of a fudge */
};

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

   HTTP2_STRUCT  *Http2Ptr;
   REQUEST_STRUCT  *RequestPtr;

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

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

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

   /* I/O outstanding */
   int  QueuedWriteCount;

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

   /* HTTP/2 request management */
   BOOL  HeaderSent,
         ReadEndStr,
         RequestEnd;
};

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  /* is this HTTP/2 connection being WATCHed */
        WatchItem;

   /* more general fields */

   BOOL  /* 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 had flow control halt transmission */
         FlowControlCount,
         /* current number of streams being flow controlled */
         FlowControlCurrent,
         /* number of frames streams had flow control halt transmission */
         FlowControlTally,
         /* number of frames streams subject to flow control */
         FlowFrameCount,
         /* number of frames streams subject to flow control */
         FlowFrameTally,
         /* 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 */
         GoAwaySecond,
         /* 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 */
         IdleSecond,
         /* 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,
         /* 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,
         /* limit number of stream resets per second (DoS?) */
         StreamResetLimitCount,
         /* limit number of zero-length headers per second (DoS?) */
         ZeroHeaderLimitCount;

   ulong  /* bytes received on the connection */
          BytesRawRx [QUAD2],
          /* bytes transmitted on the connection */
          BytesRawTx [QUAD2],
          /* bytes received since last accumulated */
          BytesRawTallyRx [QUAD2],
          /* bytes transmitted since last accumulated */
          BytesRawTallyTx [QUAD2],
          /* 64 bit time when client connected */
          ConnectTime64 [QUAD2],
          /* 64 bit time when ping originated */
          PingTime64 [QUAD2];

   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 Http2CloseConnection (HTTP2_STRUCT*);
HTTP2_STRUCT* Http2Create (REQUEST_STRUCT*);
int Http2Error (HTTP2_STRUCT*, uint, uint);
char* Http2ErrorString (int);
void Http2FlowControl (HTTP2_STRUCT*, uint);
int Http2GoAway (HTTP2_STRUCT*, uint, uchar*, uint);
void Http2Init ();
void Http2Report (REQUEST_STRUCT*);
int Http2Ping (HTTP2_STRUCT*, uint, uchar*, uint);
BOOL Http2Preface (REQUEST_STRUCT*);
int Http2Priority (HTTP2_STRUCT*, uint, uchar*, uint);
int Http2ResetStream (HTTP2_STRUCT*, uint, uint, uchar*, uint);
int Http2Settings (HTTP2_STRUCT*, uint, uchar*, uint);
void Http2SetWatch (HTTP2_STRUCT*, int);
BOOL Http2SwitchResponse (REQUEST_STRUCT*);
BOOL Http2Supervisor ();
void Http2TestBreak (int);
int Http2WindowUpdate (HTTP2_STRUCT*, uint, uint, uchar*, uint);

int Http2NetControl (int);
int Http2NetClientRead (HTTP2_STRUCT*);
void Http2NetClientReadAst (HTTP2_STRUCT*);
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 Http2Net_REQUEST_END_AST (void*);
void Http2NetWriteHeaderAst (HTTP2_WRITE_STRUCT*);
void Http2Net_WRITE_NO_AST (void*);

REQUEST_STRUCT* Http2RequestBegin (HTTP2_STRUCT*, uint);
void Http2RequestBegin2 (REQUEST_STRUCT*, HTTP2_STRUCT*, uint);
void Http2RequestCancel (REQUEST_STRUCT*);
int Http2RequestData (HTTP2_STRUCT*, uint, uint, uchar*, uint);
void Http2RequestDataClose (REQUEST_STRUCT*);
void Http2RequestEnd2 (REQUEST_STRUCT*);
void Http2RequestEnd5 (REQUEST_STRUCT*);
void Http2RequestProcess (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 */

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

