/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2018 Andy Green * * 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 core/private.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]; }; struct lws_vhost_role_h2 { struct http2_settings set; }; 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);