libwebsockets/lib/roles/h2/private.h
Andy Green 3f683351b3 refactor: split out private role header content
Private header stuff specific to roles should go in the
role dir and only be included if the role is enabled for
build.

Only definitions related to lws core should go in the actual
private-libwebsockets.h
2018-04-20 07:13:05 +08:00

401 lines
11 KiB
C

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* 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 of the License.
*
* 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
*
* This is included from private-libwebsockets.h if LWS_ROLE_H2
*/
extern struct lws_role_ops role_ops_h2;
#define lwsi_role_h2(wsi) (wsi->role_ops == &role_ops_h2)
enum lws_h2_settings {
H2SET_HEADER_TABLE_SIZE = 1,
H2SET_ENABLE_PUSH,
H2SET_MAX_CONCURRENT_STREAMS,
H2SET_INITIAL_WINDOW_SIZE,
H2SET_MAX_FRAME_SIZE,
H2SET_MAX_HEADER_LIST_SIZE,
H2SET_RESERVED7,
H2SET_ENABLE_CONNECT_PROTOCOL, /* defined in mcmanus-httpbis-h2-ws-02 */
H2SET_COUNT /* always last */
};
struct http2_settings {
uint32_t s[H2SET_COUNT];
};
enum lws_h2_wellknown_frame_types {
LWS_H2_FRAME_TYPE_DATA,
LWS_H2_FRAME_TYPE_HEADERS,
LWS_H2_FRAME_TYPE_PRIORITY,
LWS_H2_FRAME_TYPE_RST_STREAM,
LWS_H2_FRAME_TYPE_SETTINGS,
LWS_H2_FRAME_TYPE_PUSH_PROMISE,
LWS_H2_FRAME_TYPE_PING,
LWS_H2_FRAME_TYPE_GOAWAY,
LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
LWS_H2_FRAME_TYPE_CONTINUATION,
LWS_H2_FRAME_TYPE_COUNT /* always last */
};
enum lws_h2_flags {
LWS_H2_FLAG_END_STREAM = 1,
LWS_H2_FLAG_END_HEADERS = 4,
LWS_H2_FLAG_PADDED = 8,
LWS_H2_FLAG_PRIORITY = 0x20,
LWS_H2_FLAG_SETTINGS_ACK = 1,
};
enum lws_h2_errors {
H2_ERR_NO_ERROR, /* Graceful shutdown */
H2_ERR_PROTOCOL_ERROR, /* Protocol error detected */
H2_ERR_INTERNAL_ERROR, /* Implementation fault */
H2_ERR_FLOW_CONTROL_ERROR, /* Flow-control limits exceeded */
H2_ERR_SETTINGS_TIMEOUT, /* Settings not acknowledged */
H2_ERR_STREAM_CLOSED, /* Frame received for closed stream */
H2_ERR_FRAME_SIZE_ERROR, /* Frame size incorrect */
H2_ERR_REFUSED_STREAM, /* Stream not processed */
H2_ERR_CANCEL, /* Stream cancelled */
H2_ERR_COMPRESSION_ERROR, /* Compression state not updated */
H2_ERR_CONNECT_ERROR, /* TCP connection error for CONNECT method */
H2_ERR_ENHANCE_YOUR_CALM, /* Processing capacity exceeded */
H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */
H2_ERR_HTTP_1_1_REQUIRED, /* Use HTTP/1.1 for the request */
};
enum lws_h2_states {
LWS_H2_STATE_IDLE,
/*
* Send PUSH_PROMISE -> LWS_H2_STATE_RESERVED_LOCAL
* Recv PUSH_PROMISE -> LWS_H2_STATE_RESERVED_REMOTE
* Send HEADERS -> LWS_H2_STATE_OPEN
* Recv HEADERS -> LWS_H2_STATE_OPEN
*
* - Only PUSH_PROMISE + HEADERS valid to send
* - Only HEADERS or PRIORITY valid to receive
*/
LWS_H2_STATE_RESERVED_LOCAL,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send HEADERS -> LWS_H2_STATE_HALF_CLOSED_REMOTE
*
* - Only HEADERS, RST_STREAM, or PRIORITY valid to send
* - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive
*/
LWS_H2_STATE_RESERVED_REMOTE,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv HEADERS -> LWS_H2_STATE_HALF_CLOSED_LOCAL
*
* - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send
* - Only HEADERS, RST_STREAM, or PRIORITY valid to receive
*/
LWS_H2_STATE_OPEN,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL
* Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE
*/
LWS_H2_STATE_HALF_CLOSED_REMOTE,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send END_STREAM flag -> LWS_H2_STATE_CLOSED
*
* - Any frame valid to send
* - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive
*/
LWS_H2_STATE_HALF_CLOSED_LOCAL,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv END_STREAM flag -> LWS_H2_STATE_CLOSED
*
* - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send
* - Any frame valid to receive
*/
LWS_H2_STATE_CLOSED,
/*
* - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE)
* may be received
*
* - Only PRIORITY valid to send
*/
};
void
lws_h2_state(struct lws *wsi, enum lws_h2_states s);
#define LWS_H2_STREAM_ID_MASTER 0
#define LWS_H2_SETTINGS_LEN 6
#define LWS_H2_FLAG_SETTINGS_ACK 1
enum http2_hpack_state {
HPKS_TYPE,
HPKS_IDX_EXT,
HPKS_HLEN,
HPKS_HLEN_EXT,
HPKS_DATA,
};
/*
* lws general parsimonious header strategy is only store values from known
* headers, and refer to them by index.
*
* That means if we can't map the peer header name to one that lws knows, we
* will drop the content but track the indexing with associated_lws_hdr_idx =
* LWS_HPACK_IGNORE_ENTRY.
*/
enum http2_hpack_type {
HPKT_INDEXED_HDR_7, /* 1xxxxxxx: just "header field" */
HPKT_INDEXED_HDR_6_VALUE_INCR, /* 01xxxxxx: NEW indexed hdr with value */
HPKT_LITERAL_HDR_VALUE_INCR, /* 01000000: NEW literal hdr with value */
HPKT_INDEXED_HDR_4_VALUE, /* 0000xxxx: indexed hdr with value */
HPKT_INDEXED_HDR_4_VALUE_NEVER, /* 0001xxxx: indexed hdr with value NEVER NEW */
HPKT_LITERAL_HDR_VALUE, /* 00000000: literal hdr with value */
HPKT_LITERAL_HDR_VALUE_NEVER, /* 00010000: literal hdr with value NEVER NEW */
HPKT_SIZE_5
};
#define LWS_HPACK_IGNORE_ENTRY 0xffff
struct hpack_dt_entry {
char *value; /* malloc'd */
uint16_t value_len;
uint16_t hdr_len; /* virtual, for accounting */
uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */
};
struct hpack_dynamic_table {
struct hpack_dt_entry *entries; /* malloc'd */
uint32_t virtual_payload_usage;
uint32_t virtual_payload_max;
uint16_t pos;
uint16_t used_entries;
uint16_t num_entries;
};
enum lws_h2_protocol_send_type {
LWS_PPS_NONE,
LWS_H2_PPS_MY_SETTINGS,
LWS_H2_PPS_ACK_SETTINGS,
LWS_H2_PPS_PONG,
LWS_H2_PPS_GOAWAY,
LWS_H2_PPS_RST_STREAM,
LWS_H2_PPS_UPDATE_WINDOW,
};
struct lws_h2_protocol_send {
struct lws_h2_protocol_send *next; /* linked list */
enum lws_h2_protocol_send_type type;
union uu {
struct {
char str[32];
uint32_t highest_sid;
uint32_t err;
} ga;
struct {
uint32_t sid;
uint32_t err;
} rs;
struct {
uint8_t ping_payload[8];
} ping;
struct {
uint32_t sid;
uint32_t credit;
} update_window;
} u;
};
struct lws_h2_ghost_sid {
struct lws_h2_ghost_sid *next;
uint32_t sid;
};
/*
* http/2 connection info that is only used by the root connection that has
* the network connection.
*
* h2 tends to spawn many child connections from one network connection, so
* it's necessary to make members only needed by the network connection
* distinct and only malloc'd on network connections.
*
* There's only one HPACK parser per network connection.
*
* But there is an ah per logical child connection... the network connection
* fills it but it belongs to the logical child.
*/
struct lws_h2_netconn {
struct http2_settings set;
struct hpack_dynamic_table hpack_dyn_table;
uint8_t ping_payload[8];
uint8_t one_setting[LWS_H2_SETTINGS_LEN];
char goaway_str[32]; /* for rx */
struct lws *swsi;
struct lws_h2_protocol_send *pps; /* linked list */
enum http2_hpack_state hpack;
enum http2_hpack_type hpack_type;
unsigned int huff:1;
unsigned int value:1;
unsigned int unknown_header:1;
unsigned int cont_exp:1;
unsigned int cont_exp_headers:1;
unsigned int we_told_goaway:1;
unsigned int pad_length:1;
unsigned int collected_priority:1;
unsigned int is_first_header_char:1;
unsigned int zero_huff_padding:1;
unsigned int last_action_dyntable_resize:1;
uint32_t hdr_idx;
uint32_t hpack_len;
uint32_t hpack_e_dep;
uint32_t count;
uint32_t preamble;
uint32_t length;
uint32_t sid;
uint32_t inside;
uint32_t highest_sid;
uint32_t highest_sid_opened;
uint32_t cont_exp_sid;
uint32_t dep;
uint32_t goaway_last_sid;
uint32_t goaway_err;
uint32_t hpack_hdr_len;
uint16_t hpack_pos;
uint8_t frame_state;
uint8_t type;
uint8_t flags;
uint8_t padding;
uint8_t weight_temp;
uint8_t huff_pad;
char first_hdr_char;
uint8_t hpack_m;
uint8_t ext_count;
};
struct _lws_h2_related {
struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
struct lws *parent_wsi;
struct lws *child_list;
struct lws *sibling_list;
char *pending_status_body;
int tx_cr;
int peer_tx_cr_est;
unsigned int my_sid;
unsigned int child_count;
int my_priority;
uint32_t dependent_on;
unsigned int END_STREAM:1;
unsigned int END_HEADERS:1;
unsigned int send_END_STREAM:1;
unsigned int GOING_AWAY;
unsigned int requested_POLLOUT:1;
unsigned int skint:1;
uint16_t round_robin_POLLOUT;
uint16_t count_POLLOUT_children;
uint8_t h2_state; /* the RFC7540 state of the connection */
uint8_t weight;
uint8_t initialized;
};
#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->h2.parent_wsi)
int
lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason);
struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
LWS_EXTERN void lws_h2_init(struct lws *wsi);
LWS_EXTERN int
lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
unsigned char *buf, int len);
LWS_EXTERN int
lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
lws_filepos_t *inused);
LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi);
LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags,
unsigned int sid, unsigned int len,
unsigned char *buf);
LWS_EXTERN struct lws *
lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid);
LWS_EXTERN int lws_hpack_interpret(struct lws *wsi,
unsigned char c);
LWS_EXTERN int
lws_add_http2_header_by_name(struct lws *wsi,
const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end);
LWS_EXTERN int
lws_add_http2_header_by_token(struct lws *wsi,
enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end);
LWS_EXTERN int
lws_add_http2_header_status(struct lws *wsi,
unsigned int code, unsigned char **p,
unsigned char *end);
LWS_EXTERN void
lws_hpack_destroy_dynamic_header(struct lws *wsi);
LWS_EXTERN int
lws_hpack_dynamic_size(struct lws *wsi, int size);
LWS_EXTERN int
lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
LWS_EXTERN int
lws_h2_tx_cr_get(struct lws *wsi);
LWS_EXTERN void
lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
LWS_EXTERN int
lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
LWS_EXTERN void
lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
LWS_EXTERN const struct http2_settings lws_h2_defaults;
LWS_EXTERN int
lws_h2_ws_handshake(struct lws *wsi);
LWS_EXTERN int lws_h2_issue_preface(struct lws *wsi);
LWS_EXTERN int
lws_h2_client_handshake(struct lws *wsi);
LWS_EXTERN struct lws *
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi);
int
lws_handle_POLLOUT_event_h2(struct lws *wsi);
int
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len);