diff --git a/Makefile.am b/Makefile.am index c1775e4c..ab475a1f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = lib src tests examples doc +SUBDIRS = lib src tests doc ACLOCAL_AMFLAGS = -I m4 diff --git a/lib/Makefile.am b/lib/Makefile.am index 719f26a4..a0f0a0cd 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -33,16 +33,16 @@ lib_LTLIBRARIES = libnghttp2.la OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_buffer.c nghttp2_frame.c nghttp2_zlib.c \ - nghttp2_session.c nghttp2_helper.c nghttp2_stream.c nghttp2_npn.c \ - nghttp2_submit.c nghttp2_outbound_item.c \ - nghttp2_client_cert_vector.c nghttp2_gzip.c + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c nghttp2_gzip.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_buffer.h nghttp2_frame.h nghttp2_zlib.h \ nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ nghttp2_npn.h nghttp2_gzip.h \ nghttp2_submit.h nghttp2_outbound_item.h \ - nghttp2_client_cert_vector.h \ nghttp2_net.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 59987d86..e50df7e7 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -1,7 +1,7 @@ /* * nghttp2 - HTTP/2.0 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -39,7 +39,7 @@ struct nghttp2_session; /** * @struct * - * The primary structure to hold the resources needed for a SPDY + * The primary structure to hold the resources needed for a HTTP/2.0 * session. The details of this structure are intentionally hidden * from the public API. */ @@ -48,24 +48,8 @@ typedef struct nghttp2_session nghttp2_session; /** * @enum * - * The SPDY protocol version. - */ -typedef enum { - /** - * SPDY protocol version 2 - */ - NGHTTP2_PROTO_SPDY2 = 2, - /** - * SPDY protocol version 3 - */ - NGHTTP2_PROTO_SPDY3 = 3 -} nghttp2_proto_version; - -/** - * @enum - * - * Error codes used in the Spdylay library. The code range is [-999, - * -500], inclusive. The following values are defined: + * Error codes used in this library. The code range is [-999, -500], + * inclusive. The following values are defined: */ typedef enum { /** @@ -138,10 +122,10 @@ typedef enum { */ NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, /** - * SYN_STREAM is not allowed. (e.g., GOAWAY has been sent and/or - * received. + * Starting new stream is not allowed. (e.g., GOAWAY has been sent + * and/or received. */ - NGHTTP2_ERR_SYN_STREAM_NOT_ALLOWED = -516, + NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, /** * GOAWAY has already been sent. */ @@ -193,162 +177,105 @@ typedef enum { /** * @enum - * The control frame types in SPDY protocol. + * The control frame types in HTTP/2.0. */ typedef enum { /** - * The SYN_STREAM control frame. + * The DATA frame. */ - NGHTTP2_SYN_STREAM = 1, + NGHTTP2_DATA = 0, /** - * The SYN_REPLY control frame. + * The HEADERS frame. */ - NGHTTP2_SYN_REPLY = 2, + NGHTTP2_HEADERS = 1, /** - * The RST_STREAM control frame. + * The PRIORITY frame. + */ + NGHTTP2_PRIORITY = 2, + /** + * The RST_STREAM frame. */ NGHTTP2_RST_STREAM = 3, /** - * The SETTINGS control frame. + * The SETTINGS frame. */ NGHTTP2_SETTINGS = 4, /** - * The NOOP control frame. This was deprecated in SPDY/3. + * The PUSH_PROMISE frame. */ - NGHTTP2_NOOP = 5, + NGHTTP2_PUSH_PROMISE = 5, /** - * The PING control frame. + * The PING frame. */ NGHTTP2_PING = 6, /** - * The GOAWAY control frame. + * The GOAWAY frame. */ NGHTTP2_GOAWAY = 7, /** - * The HEADERS control frame. + * The WINDOW_UPDATE frame. */ - NGHTTP2_HEADERS = 8, - /** - * The WINDOW_UPDATE control frame. This first appeared in SPDY/3. - */ - NGHTTP2_WINDOW_UPDATE = 9, - /** - * The CREDENTIAL control frame. This first appeared in SPDY/3. - */ - NGHTTP2_CREDENTIAL = 10 + NGHTTP2_WINDOW_UPDATE = 8 } nghttp2_frame_type; /** * @enum * - * The flags for a control frame. + * The flags for HTTP/2.0 frames. This enum defines all flags for + * frames, assuming that the same flag name has the same mask. */ typedef enum { /** * No flag set. */ - NGHTTP2_CTRL_FLAG_NONE = 0, + NGHTTP2_FLAG_NONE = 0, /** - * FLAG_FIN flag. + * The END_STREAM flag. */ - NGHTTP2_CTRL_FLAG_FIN = 0x1, + NGHTTP2_FLAG_END_STREAM = 0x1, /** - * FLAG_UNIDIRECTIONAL flag. + * The END_HEADERS flag. */ - NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL = 0x2 -} nghttp2_ctrl_flag; - -/** - * @enum - * The flags for a DATA frame. - */ -typedef enum { + NGHTTP2_FLAG_END_HEADERS = 0x4, /** - * No flag set. + * The PRIORITY flag. */ - NGHTTP2_DATA_FLAG_NONE = 0, + NGHTTP2_FLAG_PRIORITY = 0x8, /** - * FLAG_FIN flag. + * The END_PUSH_PROMISE flag. */ - NGHTTP2_DATA_FLAG_FIN = 0x1 -} nghttp2_data_flag; - -/** - * @enum - * The flags for the SETTINGS control frame. - */ -typedef enum { + NGHTTP2_FLAG_END_PUSH_PROMISE = 0x1, /** - * No flag set. + * The PONG flag. */ - NGHTTP2_FLAG_SETTINGS_NONE = 0, + NGHTTP2_FLAG_PONG = 0x1, /** - * SETTINGS_CLEAR_SETTINGS flag. + * The END_FLOW_CONTROL flag. */ - NGHTTP2_FLAG_SETTINGS_CLEAR_SETTINGS = 1 -} nghttp2_settings_flag; - -/** - * @enum - * The flags for SETTINGS ID/value pair. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_ID_FLAG_SETTINGS_NONE = 0, - /** - * FLAG_SETTINGS_PERSIST_VALUE flag. - */ - NGHTTP2_ID_FLAG_SETTINGS_PERSIST_VALUE = 1, - /** - * FLAG_SETTINGS_PERSISTED flag. - */ - NGHTTP2_ID_FLAG_SETTINGS_PERSISTED = 2 -} nghttp2_settings_id_flag; + NGHTTP2_FLAG_END_FLOW_CONTROL = 0x1 +} nghttp2_flag; /** * @enum * The SETTINGS ID. */ typedef enum { - /** - * SETTINGS_UPLOAD_BANDWIDTH - */ - NGHTTP2_SETTINGS_UPLOAD_BANDWIDTH = 1, - /** - * SETTINGS_DOWNLOAD_BANDWIDTH - */ - NGHTTP2_SETTINGS_DOWNLOAD_BANDWIDTH = 2, - /** - * SETTINGS_ROUND_TRIP_TIME - */ - NGHTTP2_SETTINGS_ROUND_TRIP_TIME = 3, /** * SETTINGS_MAX_CONCURRENT_STREAMS */ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 4, - /** - * SETTINGS_CURRENT_CWND - */ - NGHTTP2_SETTINGS_CURRENT_CWND = 5, - /** - * SETTINGS_DOWNLOAD_RETRANS_RATE - */ - NGHTTP2_SETTINGS_DOWNLOAD_RETRANS_RATE = 6, /** * SETTINGS_INITIAL_WINDOW_SIZE */ NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 7, /** - * SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE. This first appeared in - * SPDY/3. + * SETTINGS_FLOW_CONTROL_OPTIONS */ - NGHTTP2_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8, + NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS = 10, /** * Maximum ID of :type:`nghttp2_settings_id`. */ - NGHTTP2_SETTINGS_MAX = 8 + NGHTTP2_SETTINGS_MAX = 10 } nghttp2_settings_id; /** @@ -359,359 +286,70 @@ typedef enum { /** * @enum - * The status codes for the RST_STREAM control frame. + * The status codes for the RST_STREAM and GOAWAY frames. */ typedef enum { /** - * NGHTTP2_OK is not valid status code for RST_STREAM. It is defined - * just for nghttp2 library use. + * No errors. */ - NGHTTP2_OK = 0, + NGHTTP2_NO_ERROR = 0, /** * PROTOCOL_ERROR */ NGHTTP2_PROTOCOL_ERROR = 1, - /** - * INVALID_STREAM - */ - NGHTTP2_INVALID_STREAM = 2, - /** - * REFUSED_STREAM - */ - NGHTTP2_REFUSED_STREAM = 3, - /** - * UNSUPPORTED_VERSION - */ - NGHTTP2_UNSUPPORTED_VERSION = 4, - /** - * CANCEL - */ - NGHTTP2_CANCEL = 5, /** * INTERNAL_ERROR */ - NGHTTP2_INTERNAL_ERROR = 6, + NGHTTP2_INTERNAL_ERROR = 2, /** * FLOW_CONTROL_ERROR */ - NGHTTP2_FLOW_CONTROL_ERROR = 7, - /* Following status codes were introduced in SPDY/3 */ + NGHTTP2_FLOW_CONTROL_ERROR = 3, /** - * STREAM_IN_USE + * STREAM_CLOSED */ - NGHTTP2_STREAM_IN_USE = 8, - /** - * STREAM_ALREADY_CLOSED - */ - NGHTTP2_STREAM_ALREADY_CLOSED = 9, - /** - * INVALID_CREDENTIALS - */ - NGHTTP2_INVALID_CREDENTIALS = 10, + NGHTTP2_STREAM_CLOSED = 5, /** * FRAME_TOO_LARGE */ - NGHTTP2_FRAME_TOO_LARGE = 11 -} nghttp2_status_code; - -/** - * @enum - * The status codes for GOAWAY, introduced in SPDY/3. - */ -typedef enum { + NGHTTP2_FRAME_TOO_LARGE = 6, /** - * OK. This indicates a normal session teardown. + * REFUSED_STREAM */ - NGHTTP2_GOAWAY_OK = 0, + NGHTTP2_REFUSED_STREAM = 7, /** - * PROTOCOL_ERROR + * CANCEL */ - NGHTTP2_GOAWAY_PROTOCOL_ERROR = 1, + NGHTTP2_CANCEL = 8, /** - * INTERNAL_ERROR + * COMPRESSION_ERROR */ - NGHTTP2_GOAWAY_INTERNAL_ERROR = 2 -} nghttp2_goaway_status_code; + NGHTTP2_COMPRESSION_ERROR = 9 +} nghttp2_error_code; /** * @struct - * The control frame header. + * The frame header. */ typedef struct { /** - * SPDY protocol version. + * The length field of this frame, excluding frame header. */ - uint16_t version; + uint16_t length; /** - * The type of this control frame. + * The type of this frame. See `nghttp2_frame`. */ - uint16_t type; + uint8_t type; /** - * The control frame flags. + * The flags. */ uint8_t flags; /** - * The length field of this control frame. - */ - int32_t length; -} nghttp2_ctrl_hd; - -/** - * @struct - * The SYN_STREAM control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The stream ID. + * The stream identifier (aka, stream ID) */ int32_t stream_id; - /** - * The associated-to-stream ID. 0 if this frame has no - * associated-to-stream. - */ - int32_t assoc_stream_id; - /** - * The priority of this frame. 0 is the highest priority value. Use - * `nghttp2_session_get_pri_lowest()` to know the lowest priority - * value. - */ - uint8_t pri; - /** - * The index in server's CREDENTIAL vector of the client certificate. - * This was introduced in SPDY/3. - */ - uint8_t slot; - /** - * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer - * to the name string and ``nv[2*i+1]`` contains a pointer to the - * value string. The one beyond last value must be ``NULL``. That - * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. This member may be ``NULL``. - */ - char **nv; -} nghttp2_syn_stream; +} nghttp2_frame_hd; -/** - * @struct - * The SYN_REPLY control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The stream ID. - */ - int32_t stream_id; - /** - * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer - * to the name string and ``nv[2*i+1]`` contains a pointer to the - * value string. The one beyond last value must be ``NULL``. That - * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. This member may be ``NULL``. - */ - char **nv; -} nghttp2_syn_reply; - -/** - * @struct - * The HEADERS control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The stream ID. - */ - int32_t stream_id; - /** - * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer - * to the name string and ``nv[2*i+1]`` contains a pointer to the - * value string. The one beyond last value must be ``NULL``. That - * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. This member may be ``NULL``. - */ - char **nv; -} nghttp2_headers; - -/** - * @struct - * The RST_STREAM control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The stream ID. - */ - int32_t stream_id; - /** - * The status code. See :type:`nghttp2_status_code`. - */ - uint32_t status_code; -} nghttp2_rst_stream; - -/** - * @struct - * The SETTINGS ID/Value pair. It has the following members: - */ -typedef struct { - /** - * The SETTINGS ID. See :type:`nghttp2_settings_id`. - */ - int32_t settings_id; - /** - * The flags. See :type:`nghttp2_settings_id_flag`. - */ - uint8_t flags; - /** - * The value of this entry. - */ - uint32_t value; -} nghttp2_settings_entry; - -/** - * @struct - * The SETTINGS control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The number of SETTINGS ID/Value pairs in |iv|. - */ - size_t niv; - /** - * The pointer to the array of SETTINGS ID/Value pair. - */ - nghttp2_settings_entry *iv; -} nghttp2_settings; - -/** - * @struct - * The PING control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The unique ID. - */ - uint32_t unique_id; -} nghttp2_ping; - -/** - * @struct - * The GOAWAY control frame. It has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The last-good-stream ID. - */ - int32_t last_good_stream_id; - /** - * The status code. This first appeared in SPDY/3. See - * :type:`nghttp2_goaway_status_code`. - */ - uint32_t status_code; -} nghttp2_goaway; - -/** - * @struct - * - * The WINDOW_UPDATE control frame. This first appeared in SPDY/3. It - * has the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The stream ID. - */ - int32_t stream_id; - /** - * The delta-window-size. - */ - int32_t delta_window_size; -} nghttp2_window_update; - -/** - * @struct - * - * The structure to hold chunk of memory. - */ -typedef struct { - /** - * The pointer to the data. - */ - uint8_t *data; - /** - * The length of the data. - */ - size_t length; -} nghttp2_mem_chunk; - -/** - * @struct - * - * The CREDENTIAL control frame. This first appeared in SPDY/3. It has - * the following members: - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; - /** - * The index in the client certificate vector. - */ - uint16_t slot; - /** - * Cryptographic proof that the client has possession of the private - * key associated with the certificate. - */ - nghttp2_mem_chunk proof; - /** - * The certificate chain. The certs[0] is the leaf certificate. - */ - nghttp2_mem_chunk *certs; - /** - * The number of certificates in |certs|. - */ - size_t ncerts; -} nghttp2_credential; - -/** - * @struct - * - * Convenient structure to inspect control frame header. It is useful - * to get the frame type. - */ -typedef struct { - /** - * The control frame header. - */ - nghttp2_ctrl_hd hd; -} nghttp2_ctrl_frame; /** * @union @@ -772,53 +410,260 @@ typedef struct { nghttp2_data_source_read_callback read_callback; } nghttp2_data_provider; +/** + * @enum + * + * The category of HEADERS, which indicates the role of the frame. In + * HTTP/2.0 spec, request HEADERS and response HEADERS and other + * arbitrary sent HEADERS are all called just HEADERS. In SPDY days, + * they are called as SYN_STREAM, SYN_REPLY and HEADERS and which is + * self-explanatory and easy to code. To give the application the + * particular HEADERS frame is analogous to the SPDY terms, we define + * 3 categories for it. + */ +typedef enum { + /** + * The HEADERS frame is opening stream, which is analogous to SPDY + * SYN_STREAM. + */ + NGHTTP2_HCAT_START_STREAM, + /** + * The HEADERS frame is the first response headers, which is + * analogous to SPDY SYN_REPLY. + */ + NGHTTP2_HCAT_REPLY, + /** + * The HEADERS frame which does not apply for the above categories, + * which is analogous to SPDY HEADERS. + */ + NGHTTP2_HCAT_HEADERS, +} nghttp2_headers_category; + +/** + * @struct + * The HEADERS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority. + */ + int32_t pri; + /** + * TODO Need to support binary header block. + * + * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer + * to the name string and ``nv[2*i+1]`` contains a pointer to the + * value string. The one beyond last value must be ``NULL``. That + * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be + * ``NULL``. This member may be ``NULL``. + */ + char **nv; + nghttp2_headers_category cat; +} nghttp2_headers; + +/** + * @struct + * The PRIORITY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority. + */ + int32_t pri; +} nghttp2_priority; + +/** + * @struct + * The RST_STREAM frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; +} nghttp2_rst_stream; + +/** + * @struct + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`nghttp2_settings_id`. + */ + int32_t settings_id; + /** + * The value of this entry. + */ + uint32_t value; +} nghttp2_settings_entry; + +/** + * @struct + * The SETTINGS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + nghttp2_settings_entry *iv; +} nghttp2_settings; + +/** + * @struct + * The PUSH_PROMISE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The promised stream ID + */ + int32_t promised_stream_id; +} nghttp2_push_promise; + +/** + * @struct + * The PING frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The opaque data + */ + uint8_t opaque_data[8]; +} nghttp2_ping; + +/** + * @struct + * The GOAWAY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The last stream stream ID. + */ + int32_t last_stream_id; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; + /** + * The additional debug data + */ + uint8_t *opaque_data; + /** + * The length of |opaque_data| member. + */ + size_t opaque_data_len; +} nghttp2_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The window size increment. + */ + int32_t window_size_increment; +} nghttp2_window_update; + +/** + * @struct + * + * The structure to hold chunk of memory. + * + * TODO Drop this if it is not used anymore. + */ +typedef struct { + /** + * The pointer to the data. + */ + uint8_t *data; + /** + * The length of the data. + */ + size_t length; +} nghttp2_mem_chunk; + /** * @union * - * This union includes all control frames to pass them - * to various function calls as nghttp2_frame type. + * This union includes all frames to pass them to various function + * calls as nghttp2_frame type. The DATA frame is intentionally + * omitted from here. */ typedef union { /** - * Convenient structure to inspect control frame header. + * The frame header, which is convenient to inspect frame header. */ - nghttp2_ctrl_frame ctrl; + nghttp2_frame_hd hd; /** - * The SYN_STREAM control frame. - */ - nghttp2_syn_stream syn_stream; - /** - * The SYN_REPLY control frame. - */ - nghttp2_syn_reply syn_reply; - /** - * The RST_STREAM control frame. - */ - nghttp2_rst_stream rst_stream; - /** - * The SETTINGS control frame. - */ - nghttp2_settings settings; - /** - * The PING control frame. - */ - nghttp2_ping ping; - /** - * The GOAWAY control frame. - */ - nghttp2_goaway goaway; - /** - * The HEADERS control frame. + * The HEADERS frame. */ nghttp2_headers headers; /** - * The WINDOW_UPDATE control frame. + * The PRIORITY frame. + */ + nghttp2_priority priority; + /** + * The RST_STREAM frame. + */ + nghttp2_rst_stream rst_stream; + /** + * The SETTINGS frame. + */ + nghttp2_settings settings; + /** + * The PUSH_PROMISE frame. + */ + nghttp2_push_promise push_promise; + /** + * The PING frame. + */ + nghttp2_ping ping; + /** + * The GOAWAY frame. + */ + nghttp2_goaway goaway; + /** + * The WINDOW_UPDATE frame. */ nghttp2_window_update window_update; - /** - * The CREDENTIAL control frame. - */ - nghttp2_credential credential; } nghttp2_frame; /** @@ -857,24 +702,23 @@ typedef ssize_t (*nghttp2_recv_callback) * @functypedef * * Callback function invoked by `nghttp2_session_recv()` when a - * control frame is received. + * non-DATA frame is received. */ -typedef void (*nghttp2_on_ctrl_recv_callback) -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, - void *user_data); +typedef void (*nghttp2_on_frame_recv_callback) +(nghttp2_session *session, nghttp2_frame *frame, void *user_data); /** * @functypedef * * Callback function invoked by `nghttp2_session_recv()` when an - * invalid control frame is received. The |status_code| is one of the - * :enum:`nghttp2_status_code` and indicates the error. When this + * invalid non-DATA frame is received. The |error_code| is one of the + * :enum:`nghttp2_error_code` and indicates the error. When this * callback function is invoked, the library automatically submits * either RST_STREAM or GOAWAY frame. */ -typedef void (*nghttp2_on_invalid_ctrl_recv_callback) -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, - uint32_t status_code, void *user_data); +typedef void (*nghttp2_on_invalid_frame_recv_callback) +(nghttp2_session *session, nghttp2_frame *frame, nghttp2_error_code error_code, + void *user_data); /** * @functypedef @@ -905,37 +749,34 @@ typedef void (*nghttp2_on_data_recv_callback) /** * @functypedef * - * Callback function invoked before the control frame |frame| of type - * |type| is sent. This may be useful, for example, to know the stream - * ID of SYN_STREAM frame (see also + * Callback function invoked before the non-DATA frame |frame| is + * sent. This may be useful, for example, to know the stream ID of + * HEADERS and PUSH_PROMISE frame (see also * `nghttp2_session_get_stream_user_data()`), which is not assigned * when it was queued. */ -typedef void (*nghttp2_before_ctrl_send_callback) -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, - void *user_data); +typedef void (*nghttp2_before_frame_send_callback) +(nghttp2_session *session, nghttp2_frame *frame, void *user_data); /** * @functypedef * - * Callback function invoked after the control frame |frame| of type - * |type| is sent. + * Callback function invoked after the non-DATA frame |frame| is sent. */ -typedef void (*nghttp2_on_ctrl_send_callback) -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, - void *user_data); +typedef void (*nghttp2_on_frame_send_callback) +(nghttp2_session *session, nghttp2_frame *frame, void *user_data); /** * @functypedef * - * Callback function invoked after the control frame |frame| of type - * |type| is not sent because of the error. The error is indicated by - * the |error_code|, which is one of the values defined in + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in * :type:`nghttp2_error`. */ -typedef void (*nghttp2_on_ctrl_not_send_callback) -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, - int error_code, void *user_data); +typedef void (*nghttp2_on_frame_not_send_callback) +(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code, + void *user_data); /** * @functypedef @@ -951,21 +792,21 @@ typedef void (*nghttp2_on_data_send_callback) * * Callback function invoked when the stream |stream_id| is * closed. The reason of closure is indicated by the - * |status_code|. The stream_user_data, which was specified in - * `nghttp2_submit_request()` or `nghttp2_submit_syn_stream()`, is + * |error_code|. The stream_user_data, which was specified in + * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is * still available in this function. */ typedef void (*nghttp2_on_stream_close_callback) -(nghttp2_session *session, int32_t stream_id, nghttp2_status_code status_code, +(nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code, void *user_data); /** * @functypedef * * Callback function invoked when the request from the remote peer is - * received. In other words, the frame with FIN flag set is received. - * In HTTP, this means HTTP request, including request body, is fully - * received. + * received. In other words, the frame with END_STREAM flag set is + * received. In HTTP, this means HTTP request, including request + * body, is fully received. */ typedef void (*nghttp2_on_request_recv_callback) (nghttp2_session *session, int32_t stream_id, void *user_data); @@ -975,123 +816,38 @@ typedef void (*nghttp2_on_request_recv_callback) * * Callback function invoked when the received control frame octets * could not be parsed correctly. The |type| indicates the type of - * received control frame. The |head| is the pointer to the header of + * received non-DATA frame. The |head| is the pointer to the header of * the received frame. The |headlen| is the length of the - * |head|. According to the SPDY spec, the |headlen| is always 8. In - * other words, the |head| is the first 8 bytes of the received frame. - * The |payload| is the pointer to the data portion of the received - * frame. The |payloadlen| is the length of the |payload|. This is - * the data after the length field. The |error_code| is one of the - * error code defined in :enum:`nghttp2_error` and indicates the - * error. + * |head|. According to the spec, the |headlen| is always 8. In other + * words, the |head| is the first 8 bytes of the received frame. The + * |payload| is the pointer to the data portion of the received frame. + * The |payloadlen| is the length of the |payload|. This is the data + * after the length field. The |lib_error_code| is one of the error code + * defined in :enum:`nghttp2_error` and indicates the error. */ -typedef void (*nghttp2_on_ctrl_recv_parse_error_callback) +typedef void (*nghttp2_on_frame_recv_parse_error_callback) (nghttp2_session *session, nghttp2_frame_type type, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - int error_code, void *user_data); + int lib_error_code, void *user_data); /** * @functypedef * - * Callback function invoked when the received control frame type is + * Callback function invoked when the received frame type is * unknown. The |head| is the pointer to the header of the received * frame. The |headlen| is the length of the |head|. According to the - * SPDY spec, the |headlen| is always 8. In other words, the |head| is - * the first 8 bytes of the received frame. The |payload| is the - * pointer to the data portion of the received frame. The - * |payloadlen| is the length of the |payload|. This is the data after - * the length field. + * spec, the |headlen| is always 8. In other words, the |head| is the + * first 8 bytes of the received frame. The |payload| is the pointer + * to the data portion of the received frame. The |payloadlen| is the + * length of the |payload|. This is the data after the length field. */ -typedef void (*nghttp2_on_unknown_ctrl_recv_callback) +typedef void (*nghttp2_on_unknown_frame_recv_callback) (nghttp2_session *session, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, void *user_data); -#define NGHTTP2_MAX_SCHEME 255 -#define NGHTTP2_MAX_HOSTNAME 255 - -struct nghttp2_origin; - -/** - * @struct - * - * The Web origin structure. The origin is the tuple (scheme, host, - * port). The details of this structure is intentionally hidden. To - * access these members, use accessor functions below. - */ -typedef struct nghttp2_origin nghttp2_origin; - -/** - * @function - * - * Returns the scheme member of the |origin|. - */ -const char* nghttp2_origin_get_scheme(const nghttp2_origin *origin); - -/** - * @function - * - * Returns the host member of the |origin|. - */ -const char* nghttp2_origin_get_host(const nghttp2_origin *origin); - -/** - * @function - * - * Returns the port member of the |origin|. - */ -uint16_t nghttp2_origin_get_port(const nghttp2_origin *origin); - -/** - * @functypedef - * - * Callback function invoked when the library needs the cryptographic - * proof that the client has possession of the private key associated - * with the certificate for the given |origin|. If called with - * |prooflen| == 0, the implementation of this function must return - * the length of the proof in bytes. If called with |prooflen| > 0, - * write proof into |proof| exactly |prooflen| bytes and return 0. - * - * Because the client certificate vector has limited number of slots, - * the application code may be required to pass the same proof more - * than once. - */ -typedef ssize_t (*nghttp2_get_credential_proof) -(nghttp2_session *session, const nghttp2_origin *origin, - uint8_t *proof, size_t prooflen, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the library needs the length of the - * client certificate chain for the given |origin|. The - * implementation of this function must return the length of the - * client certificate chain. If no client certificate is required for - * the given |origin|, return 0. If positive integer is returned, - * :type:`nghttp2_get_credential_proof` and - * :type:`nghttp2_get_credential_cert` callback functions will be used - * to get the cryptographic proof and certificate respectively. - */ -typedef ssize_t (*nghttp2_get_credential_ncerts) -(nghttp2_session *session, const nghttp2_origin *origin, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the library needs the client - * certificate for the given |origin|. The |idx| is the index of the - * certificate chain and 0 means the leaf certificate of the chain. - * If called with |certlen| == 0, the implementation of this function - * must return the length of the certificate in bytes. If called with - * |certlen| > 0, write certificate into |cert| exactly |certlen| - * bytes and return 0. - */ -typedef ssize_t (*nghttp2_get_credential_cert) -(nghttp2_session *session, const nghttp2_origin *origin, size_t idx, - uint8_t *cert, size_t certlen, void *user_data); - /** * @struct * @@ -1110,14 +866,14 @@ typedef struct { nghttp2_recv_callback recv_callback; /** * Callback function invoked by `nghttp2_session_recv()` when a - * control frame is received. + * non-DATA frame is received. */ - nghttp2_on_ctrl_recv_callback on_ctrl_recv_callback; + nghttp2_on_frame_recv_callback on_frame_recv_callback; /** * Callback function invoked by `nghttp2_session_recv()` when an - * invalid control frame is received. + * invalid non-DATA frame is received. */ - nghttp2_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback; + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; /** * Callback function invoked when a chunk of data in DATA frame is * received. @@ -1128,18 +884,18 @@ typedef struct { */ nghttp2_on_data_recv_callback on_data_recv_callback; /** - * Callback function invoked before the control frame is sent. + * Callback function invoked before the non-DATA frame is sent. */ - nghttp2_before_ctrl_send_callback before_ctrl_send_callback; + nghttp2_before_frame_send_callback before_frame_send_callback; /** - * Callback function invoked after the control frame is sent. + * Callback function invoked after the non-DATA frame is sent. */ - nghttp2_on_ctrl_send_callback on_ctrl_send_callback; + nghttp2_on_frame_send_callback on_frame_send_callback; /** - * The callback function invoked when a control frame is not sent + * The callback function invoked when a non-DATA frame is not sent * because of an error. */ - nghttp2_on_ctrl_not_send_callback on_ctrl_not_send_callback; + nghttp2_on_frame_not_send_callback on_frame_not_send_callback; /** * Callback function invoked after DATA frame is sent. */ @@ -1154,76 +910,50 @@ typedef struct { */ nghttp2_on_request_recv_callback on_request_recv_callback; /** - * Callback function invoked when the library needs the - * cryptographic proof that the client has possession of the private - * key associated with the certificate. - */ - nghttp2_get_credential_proof get_credential_proof; - /** - * Callback function invoked when the library needs the length of the - * client certificate chain. - */ - nghttp2_get_credential_ncerts get_credential_ncerts; - /** - * Callback function invoked when the library needs the client - * certificate. - */ - nghttp2_get_credential_cert get_credential_cert; - /** - * Callback function invoked when the received control frame octets + * Callback function invoked when the received non-DATA frame octets * could not be parsed correctly. */ - nghttp2_on_ctrl_recv_parse_error_callback on_ctrl_recv_parse_error_callback; + nghttp2_on_frame_recv_parse_error_callback + on_frame_recv_parse_error_callback; /** - * Callback function invoked when the received control frame type is + * Callback function invoked when the received frame type is * unknown. */ - nghttp2_on_unknown_ctrl_recv_callback on_unknown_ctrl_recv_callback; + nghttp2_on_unknown_frame_recv_callback on_unknown_frame_recv_callback; } nghttp2_session_callbacks; /** * @function * - * Initializes |*session_ptr| for client use, using the protocol - * version |version|. The all members of |callbacks| are copied to - * |*session_ptr|. Therefore |*session_ptr| does not store - * |callbacks|. |user_data| is an arbitrary user supplied data, which - * will be passed to the callback functions. - * - * The :member:`nghttp2_session_callbacks.send_callback` must be - * specified. If the application code uses `nghttp2_session_recv()`, - * the :member:`nghttp2_session_callbacks.recv_callback` must be - * specified. The other members of |callbacks| can be ``NULL``. To - * use CREDENTIAL frame, specify :macro:`NGHTTP2_PROTO_SPDY3` in - * |version| and specify - * :member:`nghttp2_session_callbacks.get_credential_ncerts`, - * :member:`nghttp2_session_callbacks.get_credential_cert` and - * :member:`nghttp2_session_callbacks.get_credential_proof`. See also - * `nghttp2_session_set_initial_client_cert_origin()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_ZLIB` - * The z_stream initialization failed. - * :enum:`NGHTTP2_ERR_UNSUPPORTED_VERSION` - * The version is not supported. - */ -int nghttp2_session_client_new(nghttp2_session **session_ptr, - uint16_t version, - const nghttp2_session_callbacks *callbacks, - void *user_data); - -/** - * @function - * - * Initializes |*session_ptr| for server use, using the protocol - * version |version|. The all members of |callbacks| are copied to - * |*session_ptr|. Therefore |*session_ptr| does not store - * |callbacks|. |user_data| is an arbitrary user supplied data, which - * will be passed to the callback functions. + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :member:`nghttp2_session_callbacks.send_callback` must be + * specified. If the application code uses `nghttp2_session_recv()`, + * the :member:`nghttp2_session_callbacks.recv_callback` must be + * specified. The other members of |callbacks| can be ``NULL``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_ZLIB` + * The z_stream initialization failed. + */ +int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. * * The :member:`nghttp2_session_callbacks.send_callback` must be * specified. If the application code uses `nghttp2_session_recv()`, @@ -1237,11 +967,8 @@ int nghttp2_session_client_new(nghttp2_session **session_ptr, * Out of memory. * :enum:`NGHTTP2_ERR_ZLIB` * The z_stream initialization failed. - * :enum:`NGHTTP2_ERR_UNSUPPORTED_VERSION` - * The version is not supported. */ int nghttp2_session_server_new(nghttp2_session **session_ptr, - uint16_t version, const nghttp2_session_callbacks *callbacks, void *user_data); @@ -1306,49 +1033,6 @@ typedef enum { int nghttp2_session_set_option(nghttp2_session *session, int optname, void *optval, size_t optlen); -/** - * @function - * - * Sets the origin tuple (|scheme|, |host| and |port|) that the - * connection is made to and the client certificate is sent in the - * first TLS handshake. This function must be called before any call - * of `nghttp2_session_send()` and `nghttp2_session_recv()` and be - * called only once per session. This function must not be called if - * the |session| is initialized for server use. If the client did not - * provide the client certificate in the first TLS handshake, this - * function must not be called. - * - * This function stores the given origin at the slot 1 in the client - * certificate vector. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory - * :enum:`NGHTTP2_ERR_INVALID_STATE` - * The |session| is initialized for server use; or the client - * certificate vector size is 0. - */ -int nghttp2_session_set_initial_client_cert_origin(nghttp2_session *session, - const char *scheme, - const char *host, - uint16_t port); - -/** - * @function - * - * Returns the origin at the index |slot| in the client certificate - * vector. If there is no origin at the given |slot|, this function - * returns ``NULL``. - * - * This function must not be called if the |session| is initialized - * for server use. - */ -const nghttp2_origin* nghttp2_session_get_client_cert_origin -(nghttp2_session *session, - size_t slot); - /** * @function * @@ -1558,15 +1242,7 @@ size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session); /** * @function * - * Returns lowest priority value for the |session|. - */ -uint8_t nghttp2_session_get_pri_lowest(nghttp2_session *session); - -/** - * @function - * - * Submits GOAWAY frame. The status code |status_code| is ignored if - * the protocol version is :macro:`NGHTTP2_PROTO_SPDY2`. + * Submits GOAWAY frame with the given |error_code|. * * This function should be called when the connection should be * terminated after sending GOAWAY. If the remaining streams should be @@ -1579,15 +1255,15 @@ uint8_t nghttp2_session_get_pri_lowest(nghttp2_session *session); * Out of memory. */ int nghttp2_session_fail_session(nghttp2_session *session, - uint32_t status_code); + nghttp2_error_code error_code); /** * @function * - * Returns string describing the |error_code|. The |error_code| must - * be one of the :enum:`nghttp2_error`. + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. */ -const char* nghttp2_strerror(int error_code); +const char* nghttp2_strerror(int lib_error_code); /** * @function @@ -1662,7 +1338,7 @@ const char* nghttp2_strerror(int error_code); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_request(nghttp2_session *session, uint8_t pri, +int nghttp2_submit_request(nghttp2_session *session, int32_t pri, const char **nv, const nghttp2_data_provider *data_prd, void *stream_user_data); @@ -1717,19 +1393,19 @@ int nghttp2_submit_response(nghttp2_session *session, * Submits SYN_STREAM frame. The |flags| is bitwise OR of the * following values: * - * * :enum:`NGHTTP2_CTRL_FLAG_FIN` - * * :enum:`NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL` + * * :enum:`NGHTTP2_FLAG_END_STREAM` + * * :enum:`NGHTTP2_FLAG_END_HEADERS` + * * :enum:`NGHTTP2_FLAG_PRIORITY` * - * If |flags| includes :enum:`NGHTTP2_CTRL_FLAG_FIN`, this frame has - * FLAG_FIN flag set. + * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has + * END_STREAM flag set. * - * The |assoc_stream_id| is used for server-push. Specify 0 if this - * stream is not server-push. If |session| is initialized for client - * use, |assoc_stream_id| is ignored. + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * first HEADERS frame which opens new stream). In this case, the + * actual stream ID is assigned just before the frame is sent. For + * response, specify stream ID in |stream_id|. * - * The |pri| is priority of this request. 0 is the highest priority - * value. Use `nghttp2_session_get_pri_lowest()` to know the lowest - * priority value for this |session|. + * The |pri| is priority of this request. * * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]`` * contains a pointer to the name string and ``nv[2*i+1]`` contains a @@ -1756,85 +1432,17 @@ int nghttp2_submit_response(nghttp2_session *session, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_syn_stream(nghttp2_session *session, uint8_t flags, - int32_t assoc_stream_id, uint8_t pri, - const char **nv, void *stream_user_data); - -/** - * @function - * - * Submits SYN_REPLY frame. The |flags| is bitwise OR of the following - * values: - * - * * :enum:`NGHTTP2_CTRL_FLAG_FIN` - * - * If |flags| includes :enum:`NGHTTP2_CTRL_FLAG_FIN`, this frame has - * FLAG_FIN flag set. - * - * The stream which this frame belongs to is given in the - * |stream_id|. The |nv| is the name/value pairs in this frame. - * - * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]`` - * contains a pointer to the name string and ``nv[2*i+1]`` contains a - * pointer to the value string. The one beyond last value must be - * ``NULL``. That is, if the |nv| contains N name/value pairs, - * ``nv[2*N]`` must be ``NULL``. - * - * This function creates copies of all name/value pairs in |nv|. It - * also lower-cases all names in |nv|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |nv| includes empty name or NULL value. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -int nghttp2_submit_syn_reply(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const char **nv); - -/** - * @function - * - * Submits HEADERS frame. The |flags| is bitwise OR of the following - * values: - * - * * :enum:`NGHTTP2_CTRL_FLAG_FIN` - * - * If |flags| includes :enum:`NGHTTP2_CTRL_FLAG_FIN`, this frame has - * FLAG_FIN flag set. - * - * The stream which this frame belongs to is given in the - * |stream_id|. The |nv| is the name/value pairs in this frame. - * - * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]`` - * contains a pointer to the name string and ``nv[2*i+1]`` contains a - * pointer to the value string. The one beyond last value must be - * ``NULL``. That is, if the |nv| contains N name/value pairs, - * ``nv[2*N]`` must be ``NULL``. - * - * This function creates copies of all name/value pairs in |nv|. It - * also lower-cases all names in |nv|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |nv| includes empty name or NULL value. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const char **nv); + int32_t stream_id, int32_t pri, const char **nv, + void *stream_user_data); /** * @function * * Submits one or more DATA frames to the stream |stream_id|. The * data to be sent are provided by |data_prd|. If |flags| contains - * :enum:`NGHTTP2_DATA_FLAG_FIN`, the last DATA frame has FLAG_FIN - * set. + * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + * flag set. * * This function does not take ownership of the |data_prd|. The * function copies the members of the |data_prd|. @@ -1845,14 +1453,15 @@ int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_data(nghttp2_session *session, int32_t stream_id, - uint8_t flags, const nghttp2_data_provider *data_prd); +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); /** * @function * * Submits RST_STREAM frame to cancel/reject the stream |stream_id| - * with the status code |status_code|. + * with the error code |error_code|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -1861,36 +1470,7 @@ int nghttp2_submit_data(nghttp2_session *session, int32_t stream_id, * Out of memory. */ int nghttp2_submit_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t status_code); - -/** - * @function - * - * Submits PING frame. You don't have to send PING back when you - * received PING frame. The library automatically submits PING frame - * in this case. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -int nghttp2_submit_ping(nghttp2_session *session); - -/** - * @function - * - * Submits GOAWAY frame. The status code |status_code| is ignored if - * the protocol version is :macro:`NGHTTP2_PROTO_SPDY2`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -int nghttp2_submit_goaway(nghttp2_session *session, uint32_t status_code); + nghttp2_error_code error_code); /** * @function @@ -1912,14 +1492,56 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint32_t status_code); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, +int nghttp2_submit_settings(nghttp2_session *session, const nghttp2_settings_entry *iv, size_t niv); +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * If the |opaque_data| is non NULL, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is NULL, 8 zero bytes will be sent as + * opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_submit_ping(nghttp2_session *session, uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the error code |error_code|. + * + * If the |opaque_data| is not NULL and opaque_data_len is not zero, + * those data will be sent as additional debug data. The library + * makes a copy of the memory region pointed by |opaque_data| with the + * length |opaque_data_len|, so the caller does not need to keep this + * memory after the return of this function. If the |opaque_data_len| + * is 0, the |opaque_data| could be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_submit_goaway(nghttp2_session *session, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len); + /** * @function * * Submits WINDOW_UPDATE frame. The effective range of the - * |delta_window_size| is [1, (1 << 31)-1], inclusive. But the + * |window_size_increment| is [1, (1 << 31)-1], inclusive. But the * application must be responsible to keep the resulting window size * <= (1 << 31)-1. * @@ -1933,8 +1555,9 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_window_update(nghttp2_session *session, int32_t stream_id, - int32_t delta_window_size); +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); /** * @function @@ -1942,22 +1565,18 @@ int nghttp2_submit_window_update(nghttp2_session *session, int32_t stream_id, * A helper function for dealing with NPN in client side. The |in| * contains server's protocol in preferable order. The format of |in| * is length-prefixed and not null-terminated. For example, - * ``spdy/2`` and ``http/1.1`` stored in |in| like this:: + * ``HTTP-draft-04/2.0`` and ``http/1.1`` stored in |in| like this:: * - * in[0] = 6 - * in[1..6] = "spdy/2" - * in[7] = 8 - * in[8..15] = "http/1.1" - * inlen = 16 + * in[0] = 17 + * in[1..17] = "HTTP-draft-04/2.0" + * in[18] = 8 + * in[19..26] = "http/1.1" + * inlen = 27 * * The selection algorithm is as follows: * - * 1. If server's list contains SPDY versions the nghttp2 library - * supports, this function selects one of them and returns its SPDY - * protocol version which can be used directly with - * `nghttp2_session_client_new()` and - * `nghttp2_session_server_new()` . The following steps are not - * taken. + * 1. If server's list contains ``HTTP-draft-04/2.0``, it is selected + * and returns 1. The following step is not taken. * * 2. If server's list contains ``http/1.1``, this function selects * ``http/1.1`` and returns 0. The following step is not taken. @@ -1966,12 +1585,9 @@ int nghttp2_submit_window_update(nghttp2_session *session, int32_t stream_id, * non-overlap case). In this case, |out| and |outlen| are left * untouched. * - * When nghttp2 supports updated version of SPDY in the future, this - * function may select updated protocol and application code which - * relies on nghttp2 for SPDY stuff needs not be modified. - * - * Selecting ``spdy/2`` means that ``spdy/2`` is written into |*out| - * and length of ``spdy/2`` (which is 6) is assigned to |*outlen|. + * Selecting ``HTTP-draft-04/2.0`` means that ``HTTP-draft-04/2.0`` is + * written into |*out| and its length (which is 17) is + * assigned to |*outlen|. * * See http://technotes.googlecode.com/git/nextprotoneg.html for more * details about NPN. @@ -1985,66 +1601,24 @@ int nghttp2_submit_window_update(nghttp2_session *session, int32_t stream_id, * unsigned int inlen, * void *arg) * { - * int version; - * version = nghttp2_select_next_protocol(out, outlen, in, inlen); - * if(version > 0) { - * ((MyType*)arg)->spdy_version = version; + * int rv; + * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + * if(rv == 1) { + * ((MyType*)arg)->http2_selected = 1; * } * return SSL_TLSEXT_ERR_OK; * } * ... * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + * + * Note that the HTTP/2.0 spec does use ALPN instead of NPN. This + * function is provided for transitional period before ALPN is got + * implemented in major SSL/TLS libraries. + * */ int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen); -/** - * @struct - * - * This struct contains SPDY version information this library - * supports. - */ -typedef struct { - /** - * SPDY protocol version name which can be used as TLS NPN protocol - * string. - */ - const unsigned char *proto; - /** - * The length of proto member. - */ - uint8_t len; - /** - * The corresponding SPDY version constant which can be passed to - * `nghttp2_session_client_new()` and `nghttp2_session_server_new()` - * as version argument. - */ - uint16_t version; -} nghttp2_npn_proto; - -/** - * @function - * - * Returns a pointer to the supported SPDY version list. The number of - * elements in the list will be assigned to the |*len_ptr|. It - * contains all SPDY version information this library supports. The - * application can use this information to configure NPN protocol - * offerings/selection. - */ -const nghttp2_npn_proto* nghttp2_npn_get_proto_list(size_t *len_ptr); - -/** - * @function - * - * Returns spdy version which nghttp2 library supports from the given - * protocol name. The |proto| is the pointer to the protocol name and - * |protolen| is its length. Currently, ``spdy/2`` and ``spdy/3`` are - * supported. - * - * This function returns nonzero spdy version if it succeeds, or 0. - */ -uint16_t nghttp2_npn_get_version(const unsigned char *proto, size_t protolen); - struct nghttp2_gzip; /** diff --git a/lib/nghttp2_client_cert_vector.c b/lib/nghttp2_client_cert_vector.c deleted file mode 100644 index 0d4dda77..00000000 --- a/lib/nghttp2_client_cert_vector.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * nghttp2 - HTTP/2.0 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_client_cert_vector.h" - -#include - -#include "nghttp2_helper.h" - -int nghttp2_origin_equal(const nghttp2_origin *lhs, const nghttp2_origin *rhs) -{ - return strcmp(lhs->scheme, rhs->scheme) == 0 && - strcmp(lhs->host, rhs->host) == 0 && lhs->port == rhs->port; -} - -int nghttp2_origin_set(nghttp2_origin *origin, - const char *scheme, const char *host, uint16_t port) -{ - if(strlen(scheme) > NGHTTP2_MAX_SCHEME || - strlen(host) > NGHTTP2_MAX_HOSTNAME) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - strcpy(origin->scheme, scheme); - strcpy(origin->host, host); - origin->port = port; - return 0; -} - -const char* nghttp2_origin_get_scheme(const nghttp2_origin *origin) -{ - return origin->scheme; -} - -const char* nghttp2_origin_get_host(const nghttp2_origin *origin) -{ - return origin->host; -} - -uint16_t nghttp2_origin_get_port(const nghttp2_origin *origin) -{ - return origin->port; -} - -int nghttp2_client_cert_vector_init(nghttp2_client_cert_vector *certvec, - size_t size) -{ - certvec->size = certvec->capacity = size; - certvec->last_slot = 0; - if(certvec->capacity) { - size_t vec_size = sizeof(nghttp2_origin*)*certvec->capacity; - certvec->vector = malloc(vec_size); - if(certvec->vector == NULL) { - return NGHTTP2_ERR_NOMEM; - } - memset(certvec->vector, 0, vec_size); - } else { - certvec->vector = NULL; - } - return 0; -} - -void nghttp2_client_cert_vector_free(nghttp2_client_cert_vector *certvec) -{ - size_t i; - for(i = 0; i < certvec->size; ++i) { - free(certvec->vector[i]); - } - free(certvec->vector); -} - -int nghttp2_client_cert_vector_resize(nghttp2_client_cert_vector *certvec, - size_t size) -{ - if(certvec->capacity < size) { - nghttp2_origin **vector = realloc(certvec->vector, - sizeof(nghttp2_origin*)*size); - if(vector == NULL) { - return NGHTTP2_ERR_NOMEM; - } - memset(vector + certvec->capacity, 0, - sizeof(nghttp2_origin*)*(size - certvec->capacity)); - certvec->vector = vector; - certvec->size = certvec->capacity = size; - } else { - size_t i; - for(i = size; i < certvec->size; ++i) { - free(certvec->vector[i]); - certvec->vector[i] = NULL; - } - certvec->size = nghttp2_min(certvec->size, size); - if(certvec->last_slot > certvec->size) { - certvec->last_slot = certvec->size; - } - } - return 0; -} - -size_t nghttp2_client_cert_vector_find(nghttp2_client_cert_vector *certvec, - const nghttp2_origin *origin) -{ - size_t i; - for(i = 0; i < certvec->size && certvec->vector[i]; ++i) { - if(nghttp2_origin_equal(origin, certvec->vector[i])) { - return i+1; - } - } - return 0; -} - -size_t nghttp2_client_cert_vector_put(nghttp2_client_cert_vector *certvec, - nghttp2_origin *origin) -{ - if(certvec->size == 0) { - return 0; - } - if(certvec->last_slot == certvec->size) { - certvec->last_slot = 1; - } else { - ++certvec->last_slot; - } - free(certvec->vector[certvec->last_slot-1]); - certvec->vector[certvec->last_slot-1] = origin; - return certvec->last_slot; -} - -const nghttp2_origin* nghttp2_client_cert_vector_get_origin -(nghttp2_client_cert_vector *certvec, - size_t slot) -{ - if(slot == 0 || slot > certvec->size || !certvec->vector[slot-1]) { - return NULL; - } else { - return certvec->vector[slot-1]; - } -} diff --git a/lib/nghttp2_client_cert_vector.h b/lib/nghttp2_client_cert_vector.h deleted file mode 100644 index 1fd5cb99..00000000 --- a/lib/nghttp2_client_cert_vector.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * nghttp2 - HTTP/2.0 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_CLIENT_CERT_VECTOR_H -#define NGHTTP2_CLIENT_CERT_VECTOR_H - -#ifdef HAVE_CONFIG_H -# include -#endif /* HAVE_CONFIG_H */ - -#include - -struct nghttp2_origin { - char scheme[NGHTTP2_MAX_SCHEME+1]; - char host[NGHTTP2_MAX_HOSTNAME+1]; - uint16_t port; -}; - -typedef struct { - nghttp2_origin **vector; - /* The size of the vector. */ - size_t size; - /* The real capacity of the vector. size <= capacity holds true. */ - size_t capacity; - /* The last slot where origin is stored. The default value is 0. */ - size_t last_slot; -} nghttp2_client_cert_vector; - -/* - * Returns nonzero if |lhs| and |rhs| are equal. The equality is - * defined such that each member is equal respectively. - */ -int nghttp2_origin_equal(const nghttp2_origin *lhs, const nghttp2_origin *rhs); - -/* - * Convenient function to set members to the |origin|. The |origin| - * must be allocated prior this call. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |scheme| is longer than NGHTTP2_MAX_SCHEME; or the |host| - * is longer than NGHTTP2_MAX_HOSTNAME. - */ -int nghttp2_origin_set(nghttp2_origin *origin, - const char *scheme, const char *host, uint16_t port); - -/* - * Initializes the client certificate vector with the vector size - * |size|. - */ -int nghttp2_client_cert_vector_init(nghttp2_client_cert_vector *certvec, - size_t size); - -void nghttp2_client_cert_vector_free(nghttp2_client_cert_vector *certvec); - -/* - * Returns the slot of the |origin| in the client certificate vector. - * If it is not found, returns 0. - */ -size_t nghttp2_client_cert_vector_find(nghttp2_client_cert_vector *certvec, - const nghttp2_origin *origin); - -/* - * Puts the |origin| to the |certvec|. This function takes ownership - * of the |origin| on success. - * - * This function returns the positive slot index of the certificate - * vector where the |origin| is stored if it succeeds, or 0. - */ -size_t nghttp2_client_cert_vector_put(nghttp2_client_cert_vector *certvec, - nghttp2_origin *origin); - -/* - * Resizes client certificate vector to the size |size|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_client_cert_vector_resize(nghttp2_client_cert_vector *certvec, - size_t size); - -const nghttp2_origin* nghttp2_client_cert_vector_get_origin -(nghttp2_client_cert_vector *certvec, - size_t slot); - -#endif /* NGHTTP2_CLIENT_CERT_VECTOR_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index f7515e55..49b09204 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -1,7 +1,7 @@ /* * nghttp2 - HTTP/2.0 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,43 +32,40 @@ #include "nghttp2_helper.h" #include "nghttp2_net.h" -size_t nghttp2_frame_get_len_size(uint16_t version) +/* This is SPDY stuff, and will be removed after header compression is + implemented */ +static size_t nghttp2_frame_get_len_size(void) { - if(NGHTTP2_PROTO_SPDY2 == version) { - return 2; - } else if(NGHTTP2_PROTO_SPDY3 == version) { - return 4; - } else { - /* Unsupported version */ - return 0; - } + return 2; } -static uint8_t* nghttp2_pack_str(uint8_t *buf, const char *str, size_t len, - size_t len_size) +static uint8_t* nghttp2_pack_str(uint8_t *buf, const char *str, size_t len) { - nghttp2_frame_put_nv_len(buf, len, len_size); - buf += len_size; + nghttp2_frame_put_nv_len(buf, len); + buf += nghttp2_frame_get_len_size(); memcpy(buf, str, len); return buf+len; } -static void nghttp2_frame_pack_ctrl_hd(uint8_t* buf, const nghttp2_ctrl_hd *hd) +int nghttp2_frame_is_data_frame(uint8_t *head) { - nghttp2_put_uint16be(&buf[0], hd->version); - buf[0] |= 1 << 7; - nghttp2_put_uint16be(&buf[2], hd->type); - nghttp2_put_uint32be(&buf[4], hd->length); - buf[4] = hd->flags; + return head[2] == 0; } -static void nghttp2_frame_unpack_ctrl_hd(nghttp2_ctrl_hd *hd, - const uint8_t* buf) +void nghttp2_frame_pack_frame_hd(uint8_t* buf, const nghttp2_frame_hd *hd) { - hd->version = nghttp2_get_uint16(buf) & NGHTTP2_VERSION_MASK; - hd->type = nghttp2_get_uint16(&buf[2]); - hd->flags = buf[4]; - hd->length = nghttp2_get_uint32(&buf[4]) & NGHTTP2_LENGTH_MASK; + nghttp2_put_uint16be(&buf[0], hd->length); + buf[2]= hd->type; + buf[3] = hd->flags; + nghttp2_put_uint32be(&buf[4], hd->stream_id); +} + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf) +{ + hd->length = nghttp2_get_uint16(&buf[0]); + hd->type = buf[2]; + hd->flags = buf[3]; + hd->stream_id = nghttp2_get_uint32(&buf[4]) & NGHTTP2_STREAM_ID_MASK; } ssize_t nghttp2_frame_alloc_pack_nv(uint8_t **buf_ptr, @@ -103,8 +100,8 @@ ssize_t nghttp2_frame_alloc_pack_nv(uint8_t **buf_ptr, } framelen += nv_offset; - if(framelen - NGHTTP2_FRAME_HEAD_LENGTH > NGHTTP2_LENGTH_MASK) { - /* In SPDY/2 and 3, Max frame size is 2**24 - 1. */ + if(framelen - NGHTTP2_FRAME_HEAD_LENGTH >= 1 << 16) { + /* Max frame size is 2**16 - 1 */ return NGHTTP2_ERR_FRAME_TOO_LARGE; } return framelen; @@ -126,7 +123,7 @@ int nghttp2_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr, nghttp2_buffer_reader_init(&reader, in); /* TODO limit n in a reasonable number */ - n = nghttp2_frame_get_nv_len(&reader, len_size); + n = nghttp2_frame_get_nv_len(&reader); off += len_size; for(i = 0; i < n; ++i) { uint32_t len; @@ -135,7 +132,7 @@ int nghttp2_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr, if(inlen-off < len_size) { return NGHTTP2_ERR_INVALID_FRAME; } - len = nghttp2_frame_get_nv_len(&reader, len_size); + len = nghttp2_frame_get_nv_len(&reader); off += len_size; if(inlen-off < len) { return NGHTTP2_ERR_INVALID_FRAME; @@ -180,13 +177,13 @@ int nghttp2_frame_unpack_nv(char ***nv_ptr, nghttp2_buffer *in, nghttp2_buffer_reader_init(&reader, in); idx = (char**)buf; data = buf+(nvlen*2+1)*sizeof(char*); - n = nghttp2_frame_get_nv_len(&reader, len_size); + n = nghttp2_frame_get_nv_len(&reader); for(i = 0; i < n; ++i) { uint32_t len; char *name, *val; char *stop; int multival; - len = nghttp2_frame_get_nv_len(&reader, len_size); + len = nghttp2_frame_get_nv_len(&reader); if(len == 0) { invalid_header_block = 1; } @@ -201,7 +198,7 @@ int nghttp2_frame_unpack_nv(char ***nv_ptr, nghttp2_buffer *in, *data = '\0'; ++data; - len = nghttp2_frame_get_nv_len(&reader, len_size); + len = nghttp2_frame_get_nv_len(&reader); val = data; nghttp2_buffer_reader_data(&reader, (uint8_t*)data, len); @@ -299,7 +296,7 @@ ssize_t nghttp2_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size) if(prevvallen) { /* Join previous value, with NULL character */ cur_vallen += vallen+1; - nghttp2_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size); + nghttp2_frame_put_nv_len(cur_vallen_buf, cur_vallen); *bufp = '\0'; ++bufp; memcpy(bufp, val, vallen); @@ -308,31 +305,26 @@ ssize_t nghttp2_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size) /* Previous value is empty. In this case, drop the previous. */ cur_vallen += vallen; - nghttp2_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size); + nghttp2_frame_put_nv_len(cur_vallen_buf, cur_vallen); memcpy(bufp, val, vallen); bufp += vallen; } } } else { ++num_nv; - bufp = nghttp2_pack_str(bufp, key, keylen, len_size); + bufp = nghttp2_pack_str(bufp, key, keylen); prev = key; cur_vallen_buf = bufp; cur_vallen = vallen; prevkeylen = keylen; prevvallen = vallen; - bufp = nghttp2_pack_str(bufp, val, vallen, len_size); + bufp = nghttp2_pack_str(bufp, val, vallen); } } - nghttp2_frame_put_nv_len(buf, num_nv, len_size); + nghttp2_frame_put_nv_len(buf, num_nv); return bufp-buf; } -int nghttp2_frame_is_ctrl_frame(uint8_t first_byte) -{ - return first_byte & 0x80; -} - void nghttp2_frame_nv_del(char **nv) { free(nv); @@ -400,171 +392,25 @@ char** nghttp2_frame_nv_norm_copy(const char **nv) return nv_copy; } -/* Table to translate SPDY/3 header names to SPDY/2. */ -static const char *nghttp2_nv_3to2[] = { - ":host", "host", - ":method", "method", - ":path", "url", - ":scheme", "scheme", - ":status", "status", - ":version", "version", - NULL -}; - -void nghttp2_frame_nv_3to2(char **nv) +static void nghttp2_frame_set_hd(nghttp2_frame_hd *hd, uint16_t length, + uint8_t type, uint8_t flags, + int32_t stream_id) { - int i, j; - for(i = 0; nv[i]; i += 2) { - for(j = 0; nghttp2_nv_3to2[j]; j += 2) { - if(strcmp(nv[i], nghttp2_nv_3to2[j]) == 0) { - nv[i] = (char*)nghttp2_nv_3to2[j+1]; - break; - } - } - } + hd->length = length; + hd->type = type; + hd->flags = flags; + hd->stream_id = stream_id; } -void nghttp2_frame_nv_2to3(char **nv) -{ - int i, j; - for(i = 0; nv[i]; i += 2) { - for(j = 0; nghttp2_nv_3to2[j]; j += 2) { - if(strcmp(nv[i], nghttp2_nv_3to2[j+1]) == 0) { - nv[i] = (char*)nghttp2_nv_3to2[j]; - break; - } - } - } -} - -#define NGHTTP2_HTTPS_PORT 443 - -int nghttp2_frame_nv_set_origin(char **nv, nghttp2_origin *origin) -{ - int scheme_found, host_found; - int i; - scheme_found = host_found = 0; - for(i = 0; nv[i]; i += 2) { - if(scheme_found == 0 && strcmp(":scheme", nv[i]) == 0) { - size_t len = strlen(nv[i+1]); - if(len <= NGHTTP2_MAX_SCHEME) { - strcpy(origin->scheme, nv[i+1]); - scheme_found = 1; - } - } else if(host_found == 0 && strcmp(":host", nv[i]) == 0) { - size_t len = strlen(nv[i+1]); - char *sep = memchr(nv[i+1], ':', len); - size_t hostlen; - if(sep == NULL) { - origin->port = NGHTTP2_HTTPS_PORT; - sep = nv[i+1]+len; - } else { - unsigned long int port; - errno = 0; - port = strtoul(sep+1, NULL, 10); - if(errno != 0 || port == 0 || port > UINT16_MAX) { - continue; - } - origin->port = port; - } - hostlen = sep-nv[i+1]; - if(hostlen > NGHTTP2_MAX_HOSTNAME) { - continue; - } - memcpy(origin->host, nv[i+1], hostlen); - origin->host[hostlen] = '\0'; - host_found = 1; - } - } - if(scheme_found && host_found) { - return 0; - } else { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } -} - -void nghttp2_frame_syn_stream_init(nghttp2_syn_stream *frame, - uint16_t version, uint8_t flags, - int32_t stream_id, int32_t assoc_stream_id, - uint8_t pri, char **nv) -{ - memset(frame, 0, sizeof(nghttp2_syn_stream)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_SYN_STREAM; - frame->hd.flags = flags; - frame->stream_id = stream_id; - frame->assoc_stream_id = assoc_stream_id; - frame->pri = pri; - frame->nv = nv; -} - -void nghttp2_frame_syn_stream_free(nghttp2_syn_stream *frame) -{ - nghttp2_frame_nv_del(frame->nv); -} - -void nghttp2_frame_syn_reply_init(nghttp2_syn_reply *frame, - uint16_t version, uint8_t flags, - int32_t stream_id, char **nv) -{ - memset(frame, 0, sizeof(nghttp2_syn_reply)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_SYN_REPLY; - frame->hd.flags = flags; - frame->stream_id = stream_id; - frame->nv = nv; -} - -void nghttp2_frame_syn_reply_free(nghttp2_syn_reply *frame) -{ - nghttp2_frame_nv_del(frame->nv); -} - -void nghttp2_frame_ping_init(nghttp2_ping *frame, - uint16_t version, uint32_t unique_id) -{ - memset(frame, 0, sizeof(nghttp2_ping)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_PING; - frame->hd.flags = NGHTTP2_CTRL_FLAG_NONE; - frame->hd.length = 4; - frame->unique_id = unique_id; -} - -void nghttp2_frame_ping_free(nghttp2_ping *frame) -{} - -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, - uint16_t version, int32_t last_good_stream_id, - uint32_t status_code) -{ - memset(frame, 0, sizeof(nghttp2_goaway)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_GOAWAY; - if(version == NGHTTP2_PROTO_SPDY2) { - frame->hd.length = 4; - } else if(version == NGHTTP2_PROTO_SPDY3) { - frame->hd.length = 8; - frame->status_code = status_code; - } else { - frame->hd.length = 0; - } - frame->last_good_stream_id = last_good_stream_id; -} - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame) -{} - void nghttp2_frame_headers_init(nghttp2_headers *frame, - uint16_t version, uint8_t flags, - int32_t stream_id, char **nv) + uint8_t flags, int32_t stream_id, int32_t pri, + char **nv) { memset(frame, 0, sizeof(nghttp2_headers)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_HEADERS; - frame->hd.flags = flags; - frame->stream_id = stream_id; + nghttp2_frame_set_hd(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); + frame->pri = pri; frame->nv = nv; + frame->cat = NGHTTP2_HCAT_START_STREAM; } void nghttp2_frame_headers_free(nghttp2_headers *frame) @@ -572,48 +418,38 @@ void nghttp2_frame_headers_free(nghttp2_headers *frame) nghttp2_frame_nv_del(frame->nv); } +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + int32_t pri) +{ + memset(frame, 0, sizeof(nghttp2_priority)); + nghttp2_frame_set_hd(&frame->hd, 4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, + stream_id); + frame->pri = pri; +} + +void nghttp2_frame_priority_free(nghttp2_priority *frame) +{} + void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, - uint16_t version, - int32_t stream_id, uint32_t status_code) + int32_t stream_id, + nghttp2_error_code error_code) { memset(frame, 0, sizeof(nghttp2_rst_stream)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_RST_STREAM; - frame->hd.flags = 0; - frame->hd.length = 8; - frame->stream_id = stream_id; - frame->status_code = status_code; + nghttp2_frame_set_hd(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, + stream_id); + frame->error_code = error_code; } void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) {} -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint16_t version, - int32_t stream_id, - int32_t delta_window_size) -{ - memset(frame, 0, sizeof(nghttp2_window_update)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_WINDOW_UPDATE; - frame->hd.flags = 0; - frame->hd.length = 8; - frame->stream_id = stream_id; - frame->delta_window_size = delta_window_size; -} - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame) -{} void nghttp2_frame_settings_init(nghttp2_settings *frame, - uint16_t version, uint8_t flags, nghttp2_settings_entry *iv, size_t niv) { memset(frame, 0, sizeof(nghttp2_settings)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_SETTINGS; - frame->hd.flags = flags; - frame->hd.length = 4+niv*8; + nghttp2_frame_set_hd(&frame->hd, niv*8, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, + 0); frame->niv = niv; frame->iv = iv; } @@ -623,315 +459,108 @@ void nghttp2_frame_settings_free(nghttp2_settings *frame) free(frame->iv); } -void nghttp2_frame_credential_init(nghttp2_credential *frame, - uint16_t version, uint16_t slot, - nghttp2_mem_chunk *proof, - nghttp2_mem_chunk *certs, - size_t ncerts) + +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opaque_data) { - size_t i; - memset(frame, 0, sizeof(nghttp2_credential)); - frame->hd.version = version; - frame->hd.type = NGHTTP2_CREDENTIAL; - frame->slot = slot; - frame->proof = *proof; - frame->certs = certs; - frame->ncerts = ncerts; - frame->hd.length = 2+4+frame->proof.length; - for(i = 0; i < ncerts; ++i) { - frame->hd.length += 4+frame->certs[i].length; + memset(frame, 0, sizeof(nghttp2_ping)); + nghttp2_frame_set_hd(&frame->hd, 8, NGHTTP2_PING, flags, 0); + if(opaque_data) { + memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); } } -void nghttp2_frame_credential_free(nghttp2_credential *frame) +void nghttp2_frame_ping_free(nghttp2_ping *frame) +{} + +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len) { - size_t i; - free(frame->proof.data); - for(i = 0; i < frame->ncerts; ++i) { - free(frame->certs[i].data); - } - free(frame->certs); + memset(frame, 0, sizeof(nghttp2_goaway)); + nghttp2_frame_set_hd(&frame->hd, 8+opaque_data_len, NGHTTP2_GOAWAY, + NGHTTP2_FLAG_NONE, 0); + frame->last_stream_id = last_stream_id; + frame->error_code = error_code; + frame->opaque_data = opaque_data; + frame->opaque_data_len = opaque_data_len; } -void nghttp2_frame_data_init(nghttp2_data *frame, int32_t stream_id, - uint8_t flags, +void nghttp2_frame_goaway_free(nghttp2_goaway *frame) +{ + free(frame->opaque_data); +} + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) +{ + memset(frame, 0, sizeof(nghttp2_window_update)); + nghttp2_frame_set_hd(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); + frame->window_size_increment = window_size_increment; +} + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame) +{} + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id, const nghttp2_data_provider *data_prd) { memset(frame, 0, sizeof(nghttp2_data)); - frame->stream_id = stream_id; - frame->flags = flags; + /* At this moment, the length of DATA frame is unknown */ + nghttp2_frame_set_hd(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); frame->data_prd = *data_prd; } void nghttp2_frame_data_free(nghttp2_data *frame) {} -ssize_t nghttp2_frame_pack_syn_stream(uint8_t **buf_ptr, - size_t *buflen_ptr, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - nghttp2_syn_stream *frame, - nghttp2_zlib *deflater) +ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + nghttp2_headers *frame, + nghttp2_zlib *deflater) { ssize_t framelen; - size_t len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; + size_t len_size = nghttp2_frame_get_len_size(); + ssize_t nv_offset; + if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nv_offset = NGHTTP2_FRAME_HEAD_LENGTH + 4; + } else { + nv_offset = NGHTTP2_FRAME_HEAD_LENGTH; } framelen = nghttp2_frame_alloc_pack_nv(buf_ptr, buflen_ptr, nvbuf_ptr, nvbuflen_ptr, frame->nv, - NGHTTP2_SYN_STREAM_NV_OFFSET, + nv_offset, len_size, deflater); if(framelen < 0) { return framelen; } - frame->hd.length = framelen-NGHTTP2_FRAME_HEAD_LENGTH; - memset(*buf_ptr, 0, NGHTTP2_SYN_STREAM_NV_OFFSET); + frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + memset(*buf_ptr, 0, nv_offset); /* pack ctrl header after length is determined */ - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->stream_id); - nghttp2_put_uint32be(&(*buf_ptr)[12], frame->assoc_stream_id); - if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - (*buf_ptr)[16] = (frame->pri << 5); - (*buf_ptr)[17] = frame->slot; - } else { - (*buf_ptr)[16] = (frame->pri << 6); + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_put_uint32be(&(*buf_ptr)[8], frame->pri); } return framelen; } -int nghttp2_frame_unpack_syn_stream(nghttp2_syn_stream *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - nghttp2_buffer *inflatebuf) -{ - int r; - size_t len_size; - r = nghttp2_frame_unpack_syn_stream_without_nv(frame, head, headlen, - payload, payloadlen); - len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - if(r == 0) { - r = nghttp2_frame_unpack_nv(&frame->nv, inflatebuf, len_size); - } - return r; -} - -int nghttp2_frame_unpack_syn_stream_without_nv(nghttp2_syn_stream *frame, - const uint8_t *head, - size_t headlen, - const uint8_t *payload, - size_t payloadlen) -{ - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - if(headlen + payloadlen != NGHTTP2_SYN_STREAM_NV_OFFSET) { - return NGHTTP2_ERR_INVALID_FRAME; - } - frame->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->assoc_stream_id = - nghttp2_get_uint32(payload+4) & NGHTTP2_STREAM_ID_MASK; - if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - frame->pri = (*(payload+8) >> 5); - frame->slot = payload[9]; - } else { - frame->pri = (*(payload+8) >> 6); - frame->slot = 0; - } - frame->nv = NULL; - return 0; -} - -ssize_t nghttp2_frame_pack_syn_reply(uint8_t **buf_ptr, - size_t *buflen_ptr, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - nghttp2_syn_reply *frame, - nghttp2_zlib *deflater) -{ - ssize_t framelen; - size_t len_size; - ssize_t nv_offset; - len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - nv_offset = nghttp2_frame_nv_offset(NGHTTP2_SYN_REPLY, frame->hd.version); - assert(nv_offset > 0); - framelen = nghttp2_frame_alloc_pack_nv(buf_ptr, buflen_ptr, - nvbuf_ptr, nvbuflen_ptr, - frame->nv, nv_offset, - len_size, deflater); - if(framelen < 0) { - return framelen; - } - frame->hd.length = framelen-NGHTTP2_FRAME_HEAD_LENGTH; - memset(*buf_ptr, 0, nv_offset); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->stream_id); - return framelen; -} - -int nghttp2_frame_unpack_syn_reply(nghttp2_syn_reply *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - nghttp2_buffer *inflatebuf) -{ - int r; - r = nghttp2_frame_unpack_syn_reply_without_nv(frame, head, headlen, - payload, payloadlen); - if(r == 0) { - size_t len_size; - len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - r = nghttp2_frame_unpack_nv(&frame->nv, inflatebuf, len_size); - } - return r; -} - -int nghttp2_frame_unpack_syn_reply_without_nv(nghttp2_syn_reply *frame, - const uint8_t *head, - size_t headlen, - const uint8_t *payload, - size_t payloadlen) -{ - ssize_t nv_offset; - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - nv_offset = nghttp2_frame_nv_offset(NGHTTP2_SYN_REPLY, frame->hd.version); - assert(nv_offset > 0); - if((ssize_t)(headlen + payloadlen) != nv_offset) { - return NGHTTP2_ERR_INVALID_FRAME; - } - frame->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->nv = NULL; - return 0; -} - -ssize_t nghttp2_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_ping *frame) -{ - ssize_t framelen = 12; - int r; - r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); - if(r != 0) { - return r; - } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->unique_id); - return framelen; -} - -int nghttp2_frame_unpack_ping(nghttp2_ping *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen) -{ - if(payloadlen != 4) { - return NGHTTP2_ERR_INVALID_FRAME; - } - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - frame->unique_id = nghttp2_get_uint32(payload); - return 0; -} - -ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_goaway *frame) -{ - ssize_t framelen; - int r; - if(frame->hd.version == NGHTTP2_PROTO_SPDY2) { - framelen = 12; - } else if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - framelen = 16; - } else { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); - if(r != 0) { - return r; - } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->last_good_stream_id); - if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - nghttp2_put_uint32be(&(*buf_ptr)[12], frame->status_code); - } - return framelen; -} - -int nghttp2_frame_unpack_goaway(nghttp2_goaway *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen) -{ - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - if(frame->hd.version == NGHTTP2_PROTO_SPDY2) { - if(payloadlen != 4) { - return NGHTTP2_ERR_INVALID_FRAME; - } - } else if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - if(payloadlen != 8) { - return NGHTTP2_ERR_INVALID_FRAME; - } - } else { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - frame->last_good_stream_id = nghttp2_get_uint32(payload) & - NGHTTP2_STREAM_ID_MASK; - if(frame->hd.version == NGHTTP2_PROTO_SPDY3) { - frame->status_code = nghttp2_get_uint32(payload+4); - } else { - frame->status_code = 0; - } - return 0; -} - -ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, - uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr, - nghttp2_headers *frame, - nghttp2_zlib *deflater) -{ - ssize_t framelen; - size_t len_size; - ssize_t nv_offset; - len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - nv_offset = nghttp2_frame_nv_offset(NGHTTP2_HEADERS, frame->hd.version); - assert(nv_offset > 0); - framelen = nghttp2_frame_alloc_pack_nv(buf_ptr, buflen_ptr, - nvbuf_ptr, nvbuflen_ptr, - frame->nv, nv_offset, - len_size, deflater); - if(framelen < 0) { - return framelen; - } - frame->hd.length = framelen-NGHTTP2_FRAME_HEAD_LENGTH; - memset(*buf_ptr, 0, nv_offset); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->stream_id); - return framelen; -} - int nghttp2_frame_unpack_headers(nghttp2_headers *frame, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, nghttp2_buffer *inflatebuf) { int r; + size_t len_size = nghttp2_frame_get_len_size(); r = nghttp2_frame_unpack_headers_without_nv(frame, head, headlen, payload, payloadlen); if(r == 0) { - size_t len_size; - len_size = nghttp2_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } r = nghttp2_frame_unpack_nv(&frame->nv, inflatebuf, len_size); } return r; @@ -943,31 +572,62 @@ int nghttp2_frame_unpack_headers_without_nv(nghttp2_headers *frame, const uint8_t *payload, size_t payloadlen) { - ssize_t nv_offset; - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - nv_offset = nghttp2_frame_nv_offset(NGHTTP2_HEADERS, frame->hd.version); - assert(nv_offset > 0); - if((ssize_t)(headlen + payloadlen) != nv_offset) { - return NGHTTP2_ERR_INVALID_FRAME; + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + if(head[3] & NGHTTP2_FLAG_PRIORITY) { + if(payloadlen != 4) { + return NGHTTP2_ERR_INVALID_FRAME; + } + frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK; + } else { + if(payloadlen != 0) { + return NGHTTP2_ERR_INVALID_FRAME; + } + frame->pri = NGHTTP2_PRI_DEFAULT; } - frame->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; frame->nv = NULL; return 0; } -ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_rst_stream *frame) +ssize_t nghttp2_frame_pack_priority(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_priority *frame) { - ssize_t framelen = 16; + ssize_t framelen= NGHTTP2_FRAME_HEAD_LENGTH + 4; int r; r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); if(r != 0) { return r; } memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->stream_id); - nghttp2_put_uint32be(&(*buf_ptr)[12], frame->status_code); + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + nghttp2_put_uint32be(&(*buf_ptr)[8], frame->pri); + return framelen; +} + +int nghttp2_frame_unpack_priority(nghttp2_priority *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + if(payloadlen != 4) { + return NGHTTP2_ERR_INVALID_FRAME; + } + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK; + return 0; + +} + +ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_rst_stream *frame) +{ + ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 4; + int r; + r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + nghttp2_put_uint32be(&(*buf_ptr)[8], frame->error_code); return framelen; } @@ -975,81 +635,30 @@ int nghttp2_frame_unpack_rst_stream(nghttp2_rst_stream *frame, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen) { - if(payloadlen != 8) { + if(payloadlen != 4) { return NGHTTP2_ERR_INVALID_FRAME; } - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - frame->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->status_code = nghttp2_get_uint32(payload+4); - return 0; -} - -ssize_t nghttp2_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_window_update *frame) -{ - ssize_t framelen = 16; - int r; - r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); - if(r != 0) { - return r; - } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->stream_id); - nghttp2_put_uint32be(&(*buf_ptr)[12], frame->delta_window_size); - return framelen; -} - -int nghttp2_frame_unpack_window_update(nghttp2_window_update *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, - size_t payloadlen) -{ - if(payloadlen != 8) { - return NGHTTP2_ERR_INVALID_FRAME; - } - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - frame->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->delta_window_size = nghttp2_get_uint32(&payload[4]) & - NGHTTP2_DELTA_WINDOW_SIZE_MASK; + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + frame->error_code = nghttp2_get_uint32(payload); return 0; } ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, nghttp2_settings *frame) { - ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH+frame->hd.length; + ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; size_t i; int r; - if(frame->hd.version != NGHTTP2_PROTO_SPDY2 && - frame->hd.version != NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); if(r != 0) { return r; } memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->niv); - if(frame->hd.version == NGHTTP2_PROTO_SPDY2) { - for(i = 0; i < frame->niv; ++i) { - int off = i*8; - /* spdy/2 spec says ID is network byte order, but publicly - deployed server sends little endian host byte order. */ - (*buf_ptr)[12+off+0] = (frame->iv[i].settings_id) & 0xff; - (*buf_ptr)[12+off+1] = (frame->iv[i].settings_id >> 8) & 0xff; - (*buf_ptr)[12+off+2] = (frame->iv[i].settings_id >>16) & 0xff; - (*buf_ptr)[15+off] = frame->iv[i].flags; - nghttp2_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value); - } - } else { - for(i = 0; i < frame->niv; ++i) { - int off = i*8; - nghttp2_put_uint32be(&(*buf_ptr)[12+off], frame->iv[i].settings_id); - (*buf_ptr)[12+off] = frame->iv[i].flags; - nghttp2_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value); - } + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + nghttp2_put_uint32be(&(*buf_ptr)[8+off], frame->iv[i].settings_id); + nghttp2_put_uint32be(&(*buf_ptr)[12+off], frame->iv[i].value); } return framelen; } @@ -1059,194 +668,119 @@ int nghttp2_frame_unpack_settings(nghttp2_settings *frame, const uint8_t *payload, size_t payloadlen) { size_t i; - if(payloadlen < 4) { - return NGHTTP2_ERR_INVALID_FRAME; - } - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - if(frame->hd.version != NGHTTP2_PROTO_SPDY2 && - frame->hd.version != NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } - frame->niv = nghttp2_get_uint32(payload); - if(payloadlen != 4+frame->niv*8) { + if(payloadlen % 8) { return NGHTTP2_ERR_INVALID_FRAME; } + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + frame->niv = payloadlen / 8; frame->iv = malloc(frame->niv*sizeof(nghttp2_settings_entry)); if(frame->iv == NULL) { return NGHTTP2_ERR_NOMEM; } - if(frame->hd.version == NGHTTP2_PROTO_SPDY2) { - for(i = 0; i < frame->niv; ++i) { - size_t off = i*8; - /* ID is little endian. See comments in - nghttp2_frame_pack_settings(). */ - frame->iv[i].settings_id = payload[4+off] + (payload[4+off+1] << 8) - +(payload[4+off+2] << 16); - frame->iv[i].flags = payload[7+off]; - frame->iv[i].value = nghttp2_get_uint32(&payload[8+off]); - } - } else { - for(i = 0; i < frame->niv; ++i) { - size_t off = i*8; - frame->iv[i].settings_id = nghttp2_get_uint32(&payload[4+off]) & - NGHTTP2_SETTINGS_ID_MASK; - frame->iv[i].flags = payload[4+off]; - frame->iv[i].value = nghttp2_get_uint32(&payload[8+off]); - } + for(i = 0; i < frame->niv; ++i) { + size_t off = i*8; + frame->iv[i].settings_id = nghttp2_get_uint32(&payload[off]) & + NGHTTP2_SETTINGS_ID_MASK; + frame->iv[i].value = nghttp2_get_uint32(&payload[4+off]); } return 0; } -ssize_t nghttp2_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_credential *frame) +ssize_t nghttp2_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_ping *frame) { - ssize_t framelen; + ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 8; int r; - size_t i, offset; - framelen = NGHTTP2_FRAME_HEAD_LENGTH+2+4+frame->proof.length; - for(i = 0; i < frame->ncerts; ++i) { - framelen += 4+frame->certs[i].length; - } r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); if(r != 0) { return r; } memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); - offset = NGHTTP2_FRAME_HEAD_LENGTH; - nghttp2_put_uint16be(&(*buf_ptr)[offset], frame->slot); - offset += 2; - nghttp2_put_uint32be(&(*buf_ptr)[offset], frame->proof.length); - offset += 4; - memcpy(&(*buf_ptr)[offset], frame->proof.data, frame->proof.length); - offset += frame->proof.length; - for(i = 0; i < frame->ncerts; ++i) { - nghttp2_put_uint32be(&(*buf_ptr)[offset], frame->certs[i].length); - offset += 4; - memcpy(&(*buf_ptr)[offset], frame->certs[i].data, frame->certs[i].length); - offset += frame->certs[i].length; - } + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + memcpy(&(*buf_ptr)[8], frame->opaque_data, sizeof(frame->opaque_data)); return framelen; } -/* - * Counts number of client certificate in CREDENTIAL frame payload - * |payload| with length |payloadlen|. The |payload| points to the - * length of the first certificate. This function also checks the - * frame payload is properly composed. - * - * This function returns the number of certificates in |payload| if it - * succeeds, or one of the following negative error codes: - * - * NGHTTP2_ERR_INVALID_FRAME - * The frame payload is invalid. - */ -static int nghttp2_frame_count_unpack_cert(const uint8_t *payload, - size_t payloadlen) +int nghttp2_frame_unpack_ping(nghttp2_ping *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) { - size_t n, offset = 0; - for(n = 1; 1; ++n) { - size_t len; - if(offset+4 > payloadlen) { - return NGHTTP2_ERR_INVALID_FRAME; - } - len = nghttp2_get_uint32(&payload[offset]); - offset += 4; - if(len > payloadlen || offset+len > payloadlen) { - return NGHTTP2_ERR_INVALID_FRAME; - } else { - offset += len; - if(offset == payloadlen) { - break; - } - } - } - return n; -} - -/* - * Unpacks client certificates in the |payload| with length - * |payloadlen|. First allocates memory to store the |ncerts| - * certificates. Stores certificates in the allocated space and set - * its pointer to |*certs_ptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int nghttp2_frame_unpack_cert(nghttp2_mem_chunk **certs_ptr, - size_t ncerts, - const uint8_t *payload, size_t payloadlen) -{ - size_t offset, i, j; - nghttp2_mem_chunk *certs; - certs = malloc(sizeof(nghttp2_mem_chunk)*ncerts); - if(certs == NULL) { - return NGHTTP2_ERR_NOMEM; - } - offset = 0; - for(i = 0; i < ncerts; ++i) { - certs[i].length = nghttp2_get_uint32(&payload[offset]); - offset += 4; - certs[i].data = malloc(certs[i].length); - if(certs[i].data == NULL) { - goto fail; - } - memcpy(certs[i].data, &payload[offset], certs[i].length); - offset += certs[i].length; - } - *certs_ptr = certs; - return 0; - fail: - for(j = 0; j < i; ++j) { - free(certs[j].data); - } - free(certs); - return NGHTTP2_ERR_NOMEM; -} - -int nghttp2_frame_unpack_credential(nghttp2_credential *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, - size_t payloadlen) -{ - size_t offset; - int rv; - if(payloadlen < 10) { + if(payloadlen != 8) { return NGHTTP2_ERR_INVALID_FRAME; } - nghttp2_frame_unpack_ctrl_hd(&frame->hd, head); - offset = 0; - frame->slot = nghttp2_get_uint16(&payload[offset]); - offset += 2; - frame->proof.length = nghttp2_get_uint32(&payload[offset]); - offset += 4; - if(frame->proof.length > payloadlen || - offset+frame->proof.length > payloadlen) { + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); + return 0; +} + + +ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_goaway *frame) +{ + ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; + int r; + r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + nghttp2_put_uint32be(&(*buf_ptr)[8], frame->last_stream_id); + nghttp2_put_uint32be(&(*buf_ptr)[12], frame->error_code); + memcpy(&(*buf_ptr)[16], frame->opaque_data, frame->opaque_data_len); + return framelen; +} + +int nghttp2_frame_unpack_goaway(nghttp2_goaway *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + if(payloadlen < 8) { return NGHTTP2_ERR_INVALID_FRAME; } - frame->proof.data = malloc(frame->proof.length); - if(frame->proof.data == NULL) { - return NGHTTP2_ERR_NOMEM; - } - memcpy(frame->proof.data, &payload[offset], frame->proof.length); - offset += frame->proof.length; - rv = nghttp2_frame_count_unpack_cert(payload+offset, payloadlen-offset); - if(rv < 0) { - goto fail; - } - frame->ncerts = rv; - rv = nghttp2_frame_unpack_cert(&frame->certs, frame->ncerts, - payload+offset, payloadlen-offset); - if(rv != 0) { - goto fail; + frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->error_code = nghttp2_get_uint32(payload+4); + frame->opaque_data_len = payloadlen - 8; + if(frame->opaque_data_len == 0) { + frame->opaque_data = NULL; + } else { + frame->opaque_data = malloc(frame->opaque_data_len); + if(frame->opaque_data == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(frame->opaque_data, &payload[8], frame->opaque_data_len); } return 0; - fail: - free(frame->proof.data); - return rv; +} + +ssize_t nghttp2_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_window_update *frame) +{ + ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 4; + int r; + r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); + nghttp2_put_uint32be(&(*buf_ptr)[8], frame->window_size_increment); + return framelen; +} + +int nghttp2_frame_unpack_window_update(nghttp2_window_update *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, + size_t payloadlen) +{ + if(payloadlen != 4) { + return NGHTTP2_ERR_INVALID_FRAME; + } + nghttp2_frame_unpack_frame_hd(&frame->hd, head); + frame->window_size_increment = nghttp2_get_uint32(payload) & + NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; + return 0; } nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, @@ -1273,31 +807,20 @@ void nghttp2_frame_iv_sort(nghttp2_settings_entry *iv, size_t niv) qsort(iv, niv, sizeof(nghttp2_settings_entry), nghttp2_settings_entry_compar); } -ssize_t nghttp2_frame_nv_offset(nghttp2_frame_type type, uint16_t version) +ssize_t nghttp2_frame_nv_offset(const uint8_t *head) { - switch(type) { - case NGHTTP2_SYN_STREAM: - return NGHTTP2_SYN_STREAM_NV_OFFSET; - case NGHTTP2_SYN_REPLY: { - if(version == NGHTTP2_PROTO_SPDY2) { - return NGHTTP2_SPDY2_SYN_REPLY_NV_OFFSET; - } else if(version == NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_SPDY3_SYN_REPLY_NV_OFFSET; + switch(head[2]) { + case NGHTTP2_HEADERS: + if(head[3] & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_FRAME_HEAD_LENGTH + 4; + } else { + return NGHTTP2_FRAME_HEAD_LENGTH; } - break; - } - case NGHTTP2_HEADERS: { - if(version == NGHTTP2_PROTO_SPDY2) { - return NGHTTP2_SPDY2_HEADERS_NV_OFFSET; - } else if(version == NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_SPDY3_HEADERS_NV_OFFSET; - } - break; - } + case NGHTTP2_PUSH_PROMISE: + return NGHTTP2_FRAME_HEAD_LENGTH + 4; default: - break; + return -1; } - return -1; } int nghttp2_frame_nv_check_null(const char **nv) diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 53b9621f..c092215e 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -32,64 +32,46 @@ #include #include "nghttp2_zlib.h" #include "nghttp2_buffer.h" -#include "nghttp2_client_cert_vector.h" + +/** + * @macro + * default priority value + */ +#define NGHTTP2_PRI_DEFAULT (1 << 30) +#define NGHTTP2_PRI_LOWEST ((1U << 31) - 1) #define NGHTTP2_STREAM_ID_MASK 0x7fffffff -/* This is actually the maximum length of a control frame in SPDY/2 - and 3. */ -#define NGHTTP2_LENGTH_MASK 0xffffff -#define NGHTTP2_VERSION_MASK 0x7fff -#define NGHTTP2_DELTA_WINDOW_SIZE_MASK 0x7fffffff +#define NGHTTP2_PRIORITY_MASK 0x7fffffff +#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK 0x7fffffff #define NGHTTP2_SETTINGS_ID_MASK 0xffffff -/* The length of DATA frame payload. */ +/* The maximum length of DATA frame payload. */ #define NGHTTP2_DATA_PAYLOAD_LENGTH 4096 /* The number of bytes of frame header. */ #define NGHTTP2_FRAME_HEAD_LENGTH 8 -/* The offset to the name/value header block in the frame (including - frame header) */ -#define NGHTTP2_SYN_STREAM_NV_OFFSET 18 - -#define NGHTTP2_SPDY2_SYN_REPLY_NV_OFFSET 14 -#define NGHTTP2_SPDY3_SYN_REPLY_NV_OFFSET 12 - -#define NGHTTP2_SPDY2_HEADERS_NV_OFFSET 14 -#define NGHTTP2_SPDY3_HEADERS_NV_OFFSET 12 - -#define nghttp2_frame_get_nv_len(RED, LEN_SIZE) \ - (LEN_SIZE == 2 ? nghttp2_buffer_reader_uint16(RED) : \ - nghttp2_buffer_reader_uint32(RED)) - -#define nghttp2_frame_put_nv_len(OUT, VAL, LEN_SIZE) \ - (LEN_SIZE == 2 ? \ - nghttp2_put_uint16be(OUT, VAL) : nghttp2_put_uint32be(OUT, VAL)) - -/* Category of SPDY frames. */ +/* Category of frames. */ typedef enum { - /* Control frame */ - NGHTTP2_CTRL, + /* non-DATA frame */ + NGHTTP2_CAT_CTRL, /* DATA frame */ - NGHTTP2_DATA + NGHTTP2_CAT_DATA } nghttp2_frame_category; +#define nghttp2_frame_get_nv_len(RED) nghttp2_buffer_reader_uint16(RED) +#define nghttp2_frame_put_nv_len(OUT, VAL) nghttp2_put_uint16be(OUT, VAL) + /** * @struct * The DATA frame. It has the following members: */ typedef struct { - /** - * The stream ID. - */ - int32_t stream_id; - /** - * The DATA frame flags. See :type:`nghttp2_data_flag`. - */ - uint8_t flags; + nghttp2_frame_hd hd; /** * The flag to indicate whether EOF was reached or not. Initially - * |eof| is 0. It becomes 1 after all data were read. + * |eof| is 0. It becomes 1 after all data were read. This is used + * exclusively by nghttp2 library and not in the spec. */ uint8_t eof; /** @@ -98,15 +80,14 @@ typedef struct { nghttp2_data_provider data_prd; } nghttp2_data; -/* - * Returns the number of bytes in length of name/value pair for the - * given protocol version |version|. If |version| is not supported, - * returns 0. - */ -size_t nghttp2_frame_get_len_size(uint16_t version); +int nghttp2_frame_is_data_frame(uint8_t *head); + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf); /* - * Packs SYN_STREAM frame |frame| in wire format and store it in + * Packs HEADERS frame |frame| in wire format and store it in * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire * format temporarily. Its length is |*nvbuflen_ptr| bytes. This @@ -122,8 +103,6 @@ size_t nghttp2_frame_get_len_size(uint16_t version); * This function returns the size of packed frame if it succeeds, or * returns one of the following negative error codes: * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. * NGHTTP2_ERR_ZLIB * The deflate operation failed. * NGHTTP2_ERR_FRAME_TOO_LARGE @@ -131,18 +110,18 @@ size_t nghttp2_frame_get_len_size(uint16_t version); * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_syn_stream(uint8_t **buf_ptr, - size_t *buflen_ptr, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - nghttp2_syn_stream *frame, - nghttp2_zlib *deflater); +ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + nghttp2_headers *frame, + nghttp2_zlib *deflater); /* - * Unpacks SYN_STREAM frame byte sequence into |frame|. The control - * frame header is given in |head| with |headlen| length. In spdy/3 - * spec, headlen is 8 bytes. |payload| is the data after length field - * of the header and just before name/value header block. + * Unpacks HEADERS frame byte sequence into |frame|. The control + * frame header is given in |head| with |headlen| length. In the spec, + * headlen is 8 bytes. |payload| is the data after frame header and + * just before name/value header block. * * The |inflatebuf| contains inflated name/value header block in wire * foramt. @@ -158,107 +137,116 @@ ssize_t nghttp2_frame_pack_syn_stream(uint8_t **buf_ptr, * Unpacking succeeds but the header block is invalid. * NGHTTP2_ERR_INVALID_FRAME * The input data are invalid. - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_frame_unpack_syn_stream(nghttp2_syn_stream *frame, +int nghttp2_frame_unpack_headers(nghttp2_headers *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + nghttp2_buffer *inflatebuf); + +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_FRAME + * The input data are invalid. + */ +int nghttp2_frame_unpack_headers_without_nv(nghttp2_headers *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs PRIORITY frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +ssize_t nghttp2_frame_pack_priority(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_priority *frame); + +/* + * Unpacks PRIORITY wire format into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_FRAME + * The input data are invalid. + */ +int nghttp2_frame_unpack_priority(nghttp2_priority *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + +/* + * Packs RST_STREAM frame |frame| in wire frame format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16 + * bytes long. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_FRAME + * The input data are invalid. + */ +int nghttp2_frame_unpack_rst_stream(nghttp2_rst_stream *frame, const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - nghttp2_buffer *inflatebuf); + const uint8_t *payload, size_t payloadlen); /* - * Unpacks SYN_STREAM frame byte sequence into |frame|. This function - * only unapcks bytes that come before name/value header block. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - */ -int nghttp2_frame_unpack_syn_stream_without_nv(nghttp2_syn_stream *frame, - const uint8_t *head, - size_t headlen, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs SYN_REPLY frame |frame| in wire frame format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. - * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire - * format temporarily. Its length is |*nvbuflen_ptr| bytes. This - * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store - * frame and name/value pairs. When expansion occurred, memory - * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. - * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are - * updated accordingly. - * - * frame->hd.length is assigned after length is determined during - * packing process. + * Packs SETTINGS frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. * * This function returns the size of packed frame if it succeeds, or * returns one of the following negative error codes: * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_ZLIB - * The deflate operation failed. - * NGHTTP2_ERR_FRAME_TOO_LARGE - * The length of the frame is too large. * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_syn_reply(uint8_t **buf_ptr, - size_t *buflen_ptr, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - nghttp2_syn_reply *frame, - nghttp2_zlib *deflater); +ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, + nghttp2_settings *frame); /* - * Unpacks SYN_REPLY frame byte sequence into |frame|. - * - * The |inflatebuf| contains inflated name/value header block in wire - * foramt. - * - * This function also validates the name/value pairs. If unpacking - * succeeds but validation fails, it is indicated by returning - * NGHTTP2_ERR_INVALID_HEADER_BLOCK. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_HEADER_BLOCK - * Unpacking succeeds but the header block is invalid. - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_syn_reply(nghttp2_syn_reply *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - nghttp2_buffer *inflatebuf); - -/* - * Unpacks SYN_REPLY frame byte sequence into |frame|. This function - * only unapcks bytes that come before name/value header block. + * Unpacks SETTINGS wire format into |frame|. * * This function returns 0 if it succeeds or one of the following * negative error codes: * * NGHTTP2_ERR_INVALID_FRAME * The input data are invalid. + * NGHTTP2_ERR_NOMEM + * Out of memory. */ -int nghttp2_frame_unpack_syn_reply_without_nv(nghttp2_syn_reply *frame, - const uint8_t *head, - size_t headlen, - const uint8_t *payload, - size_t payloadlen); +int nghttp2_frame_unpack_settings(nghttp2_settings *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); /* * Packs PING frame |frame| in wire format and store it in @@ -297,8 +285,6 @@ int nghttp2_frame_unpack_ping(nghttp2_ping *frame, * This function returns 0 if it succeeds or one of the following * negative error codes: * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. * NGHTTP2_ERR_NOMEM * Out of memory. */ @@ -311,119 +297,15 @@ ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, * This function returns 0 if it succeeds or one of the following * negative error codes: * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. * NGHTTP2_ERR_INVALID_FRAME * The input data are invalid. + * NGHTTP2_ERR_NOMEM + * Out of memory. */ int nghttp2_frame_unpack_goaway(nghttp2_goaway *frame, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen); -/* - * Packs HEADERS frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. - * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire - * format temporarily. Its length is |*nvbuflen_ptr| bytes. This - * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store - * frame and name/value pairs. When expansion occurred, memory - * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. - * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are - * updated accordingly. - * - * frame->hd.length is assigned after length is determined during - * packing process. - * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: - * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_ZLIB - * The deflate operation failed. - * NGHTTP2_ERR_FRAME_TOO_LARGE - * The length of the frame is too large. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, - uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr, - nghttp2_headers *frame, - nghttp2_zlib *deflater); - -/* - * Unpacks HEADERS wire format into |frame|. - * - * The |inflatebuf| contains inflated name/value header block in wire - * foramt. - * - * This function also validates the name/value pairs. If unpacking - * succeeds but validation fails, it is indicated by returning - * NGHTTP2_ERR_INVALID_HEADER_BLOCK. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_HEADER_BLOCK - * Unpacking succeeds but the header block is invalid. - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_headers(nghttp2_headers *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - nghttp2_buffer *inflatebuf); - -/* - * Unpacks HEADERS frame byte sequence into |frame|. This function - * only unapcks bytes that come before name/value header block. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - */ -int nghttp2_frame_unpack_headers_without_nv(nghttp2_headers *frame, - const uint8_t *head, - size_t headlen, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs RST_STREAM frame |frame| in wire frame format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16 - * bytes long. - * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_rst_stream *frame); - -/* - * Unpacks RST_STREAM frame byte sequence into |frame|. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - */ -int nghttp2_frame_unpack_rst_stream(nghttp2_rst_stream *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen); - - /* * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it * in |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| @@ -454,69 +336,6 @@ int nghttp2_frame_unpack_window_update(nghttp2_window_update *frame, const uint8_t *payload, size_t payloadlen); -/* - * Packs SETTINGS frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. - * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: - * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_settings *frame); - -/* - * Unpacks SETTINGS wire format into |frame|. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_UNSUPPORTED_VERSION - * The version is not supported. - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_settings(nghttp2_settings *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen); - -/* - * Packs CREDENTIAL frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. - * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -ssize_t nghttp2_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_credential *frame); - -/* - * Unpacks CREDENTIAL wire format into |frame|. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_FRAME - * The input data are invalid. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_credential(nghttp2_credential *frame, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen); /* * Returns number of bytes to pack name/value pairs |nv|. This * function expects |nv| is sorted in ascending order of key. @@ -622,93 +441,73 @@ int nghttp2_frame_unpack_nv(char ***nv_ptr, nghttp2_buffer *in, size_t len_size); /* - * Initializes SYN_STREAM frame |frame| with given values. |frame| - * takes ownership of |nv|, so caller must not free it. If stream_id - * is not assigned yet, it must be 0. + * Initializes HEADERS frame |frame| with given values. |frame| + * takes ownership of |nv|, so caller must not free it. If |stream_id| + * is not assigned yet, it must be -1. */ -void nghttp2_frame_syn_stream_init(nghttp2_syn_stream *frame, - uint16_t version, uint8_t flags, - int32_t stream_id, int32_t assoc_stream_id, - uint8_t pri, char **nv); - -void nghttp2_frame_syn_stream_free(nghttp2_syn_stream *frame); - -void nghttp2_frame_syn_reply_init(nghttp2_syn_reply *frame, - uint16_t version, uint8_t flags, - int32_t stream_id, char **nv); - -void nghttp2_frame_syn_reply_free(nghttp2_syn_reply *frame); - -void nghttp2_frame_ping_init(nghttp2_ping *frame, uint16_t version, - uint32_t unique_id); - -void nghttp2_frame_ping_free(nghttp2_ping *frame); - -/* - * Initializes GOAWAY frame |frame| with given values. The - * |status_code| is ignored if |version| == NGHTTP2_PROTO_SPDY2. - */ -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, uint16_t version, - int32_t last_good_stream_id, - uint32_t status_code); - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame); - -void nghttp2_frame_headers_init(nghttp2_headers *frame, uint16_t version, - uint8_t flags, - int32_t stream_id, char **nv); +void nghttp2_frame_headers_init(nghttp2_headers *frame, + uint8_t flags, int32_t stream_id, int32_t pri, + char **nv); void nghttp2_frame_headers_free(nghttp2_headers *frame); + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + int32_t pri); + +void nghttp2_frame_priority_free(nghttp2_priority *frame); + void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, - uint16_t version, - int32_t stream_id, uint32_t status_code); + int32_t stream_id, + nghttp2_error_code error_code); void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint16_t version, - int32_t stream_id, - int32_t delta_window_size); - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame); - /* * Initializes SETTINGS frame |frame| with given values. |frame| takes * ownership of |iv|, so caller must not free it. The |flags| are * bitwise-OR of one or more of nghttp2_settings_flag. */ void nghttp2_frame_settings_init(nghttp2_settings *frame, - uint16_t version, uint8_t flags, nghttp2_settings_entry *iv, size_t niv); void nghttp2_frame_settings_free(nghttp2_settings *frame); /* - * Initializes CREDENTIAL frame |frame| with given values. This - * function takes ownership of |proof->data| and |certs| on success. - * Note that the ownership of |proof| is not taken. + * Initializes PING frame |frame| with given values. If the + * |opqeue_data| is not NULL, it must point to 8 bytes memory region + * of data. The data pointed by |opaque_data| is copied. It can be + * NULL. In this case, 8 bytes NULL is used. */ -void nghttp2_frame_credential_init(nghttp2_credential *frame, - uint16_t version, uint16_t slot, - nghttp2_mem_chunk *proof, - nghttp2_mem_chunk *certs, - size_t ncerts); +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opque_data); -void nghttp2_frame_credential_free(nghttp2_credential *frame); +void nghttp2_frame_ping_free(nghttp2_ping *frame); -void nghttp2_frame_data_init(nghttp2_data *frame, int32_t stream_id, - uint8_t flags, +/* + * Initializes GOAWAY frame |frame| with given values. On success, + * this function takes ownership of |opaque_data|, so caller must not + * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. + */ +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len); + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame); + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame); + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id, const nghttp2_data_provider *data_prd); void nghttp2_frame_data_free(nghttp2_data *frame); -/* - * Returns 1 if the first byte of this frame indicates it is a control - * frame. - */ -int nghttp2_frame_is_ctrl_frame(uint8_t first_byte); - /* * Deallocates memory of name/value pair |nv|. */ @@ -742,34 +541,6 @@ void nghttp2_frame_nv_downcase(char **nv); */ char** nghttp2_frame_nv_norm_copy(const char **nv); -/* - * Translates the |nv| in SPDY/3 header names into SPDY/2. - */ -void nghttp2_frame_nv_3to2(char **nv); - -/* - * Translates the |nv| in SPDY/2 header names into SPDY/3. - */ -void nghttp2_frame_nv_2to3(char **nv); - -/* - * Assigns the members of the |origin| using ":scheme" and ":host" - * values in |nv|. - * - * If ":host" value contains ':', this function parses the chracters - * after ':' as integer and uses it as port number. - * - * If ':' is missing in :host value, the default port number is used. - * The only defined default port number is 443. - * - * This function returns 0 if it succeeds, or one of the following - * negative error code: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |nv| lacks either :scheme or :host, or both. - */ -int nghttp2_frame_nv_set_origin(char **nv, nghttp2_origin *origin); - /* * Makes copy of |iv| and return the copy. The |niv| is the number of * entries in |iv|. This function returns the pointer to the copy if @@ -787,11 +558,10 @@ void nghttp2_frame_iv_sort(nghttp2_settings_entry *iv, size_t niv); /* * Returns the offset of the name/header block in the frame, including - * frame header. If |type| is neither NGHTTP2_SYN_STREAM, - * NGHTTP2_SYN_REPLY nor NGHTTP2_HEADERS, this function returns -1. - * If |version| is unknown, this function returns -1. + * frame header. The |head| is frame header. If the indicated frame + * type does not have header block, this function returns -1. */ -ssize_t nghttp2_frame_nv_offset(nghttp2_frame_type type, uint16_t version); +ssize_t nghttp2_frame_nv_offset(const uint8_t *head); /* * Checks names are not empty string and do not contain control diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c index e7132091..eccae010 100644 --- a/lib/nghttp2_helper.c +++ b/lib/nghttp2_helper.c @@ -107,7 +107,7 @@ const char* nghttp2_strerror(int error_code) return "Invalid stream state"; case NGHTTP2_ERR_DEFERRED_DATA_EXIST: return "Another DATA frame has already been deferred"; - case NGHTTP2_ERR_SYN_STREAM_NOT_ALLOWED: + case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: return "SYN_STREAM is not allowed"; case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: return "GOAWAY has already been sent"; diff --git a/lib/nghttp2_npn.c b/lib/nghttp2_npn.c index d82bb0d1..348a8192 100644 --- a/lib/nghttp2_npn.c +++ b/lib/nghttp2_npn.c @@ -26,37 +26,22 @@ #include -static const nghttp2_npn_proto proto_list[] = { - { (const unsigned char*)"spdy/3", 6, NGHTTP2_PROTO_SPDY3 }, - { (const unsigned char*)"spdy/2", 6, NGHTTP2_PROTO_SPDY2 } -}; - -const nghttp2_npn_proto* nghttp2_npn_get_proto_list(size_t *len_ptr) -{ - *len_ptr = sizeof(proto_list)/sizeof(nghttp2_npn_proto); - return proto_list; -} - int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { int http_selected = 0; unsigned int i = 0; for(; i < inlen; i += in[i]+1) { - unsigned int j; - for(j = 0; j < sizeof(proto_list)/sizeof(nghttp2_npn_proto); ++j) { - if(in[i] == proto_list[j].len && - memcmp(&in[i+1], proto_list[j].proto, in[i]) == 0) { - *out = (unsigned char*)&in[i+1]; - *outlen = in[i]; - return proto_list[j].version; - } + if(in[i] == 17 && memcmp(&in[i+1], "HTTP-draft-04/2.0", in[i]) == 0) { + *out = (unsigned char*)&in[i+1]; + *outlen = in[i]; + return 1; } if(in[i] == 8 && memcmp(&in[i+1], "http/1.1", in[i]) == 0) { http_selected = 1; *out = (unsigned char*)&in[i+1]; *outlen = in[i]; - /* Go through to the next iteration, because "spdy/X" may be + /* Go through to the next iteration, because "HTTP/2.0" may be there */ } } @@ -66,19 +51,3 @@ int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, return -1; } } - -uint16_t nghttp2_npn_get_version(const unsigned char *proto, size_t protolen) -{ - if(proto == NULL) { - return 0; - } else { - if(protolen == 6) { - if(memcmp("spdy/2", proto, 6) == 0) { - return NGHTTP2_PROTO_SPDY2; - } else if(memcmp("spdy/3", proto, 6) == 0) { - return NGHTTP2_PROTO_SPDY3; - } - } - return 0; - } -} diff --git a/lib/nghttp2_outbound_item.c b/lib/nghttp2_outbound_item.c index 0466147b..65b67550 100644 --- a/lib/nghttp2_outbound_item.c +++ b/lib/nghttp2_outbound_item.c @@ -31,18 +31,18 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item) if(item == NULL) { return; } - if(item->frame_cat == NGHTTP2_CTRL) { - nghttp2_frame_type frame_type; + if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; - frame_type = nghttp2_outbound_item_get_ctrl_frame_type(item); frame = nghttp2_outbound_item_get_ctrl_frame(item); - switch(frame_type) { - case NGHTTP2_SYN_STREAM: - nghttp2_frame_syn_stream_free(&frame->syn_stream); - free(((nghttp2_syn_stream_aux_data*)item->aux_data)->data_prd); + switch(frame->hd.type) { + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&frame->headers); + if(item->aux_data) { + free(((nghttp2_headers_aux_data*)item->aux_data)->data_prd); + } break; - case NGHTTP2_SYN_REPLY: - nghttp2_frame_syn_reply_free(&frame->syn_reply); + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&frame->priority); break; case NGHTTP2_RST_STREAM: nghttp2_frame_rst_stream_free(&frame->rst_stream); @@ -50,27 +50,17 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item) case NGHTTP2_SETTINGS: nghttp2_frame_settings_free(&frame->settings); break; - case NGHTTP2_NOOP: - /* We don't have any public API to add NOOP, so here is - unreachable. */ - assert(0); case NGHTTP2_PING: nghttp2_frame_ping_free(&frame->ping); break; case NGHTTP2_GOAWAY: nghttp2_frame_goaway_free(&frame->goaway); break; - case NGHTTP2_HEADERS: - nghttp2_frame_headers_free(&frame->headers); - break; case NGHTTP2_WINDOW_UPDATE: nghttp2_frame_window_update_free(&frame->window_update); break; - case NGHTTP2_CREDENTIAL: - nghttp2_frame_credential_free(&frame->credential); - break; } - } else if(item->frame_cat == NGHTTP2_DATA) { + } else if(item->frame_cat == NGHTTP2_CAT_DATA) { nghttp2_data *data_frame; data_frame = nghttp2_outbound_item_get_data_frame(item); nghttp2_frame_data_free(data_frame); diff --git a/lib/nghttp2_outbound_item.h b/lib/nghttp2_outbound_item.h index 0cb2914a..00500cf1 100644 --- a/lib/nghttp2_outbound_item.h +++ b/lib/nghttp2_outbound_item.h @@ -42,7 +42,7 @@ typedef struct { nghttp2_data_provider *data_prd; void *stream_user_data; -} nghttp2_syn_stream_aux_data; +} nghttp2_headers_aux_data; typedef struct { /* Type of |frame|. NGHTTP2_CTRL: nghttp2_frame*, NGHTTP2_DATA: @@ -74,7 +74,7 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item); /* Macros to cast nghttp2_outbound_item.frame to the proper type. */ #define nghttp2_outbound_item_get_ctrl_frame(ITEM) ((nghttp2_frame*)ITEM->frame) #define nghttp2_outbound_item_get_ctrl_frame_type(ITEM) \ - (((nghttp2_frame*)ITEM->frame)->ctrl.hd.type) + (((nghttp2_frame*)ITEM->frame)->hd.type) #define nghttp2_outbound_item_get_data_frame(ITEM) ((nghttp2_data*)ITEM->frame) #endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index cac9db72..a83f5be0 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -73,10 +73,10 @@ static int nghttp2_is_fatal(int error) } int nghttp2_session_fail_session(nghttp2_session *session, - uint32_t status_code) + nghttp2_error_code error_code) { session->goaway_flags |= NGHTTP2_GOAWAY_FAIL_ON_SEND; - return nghttp2_submit_goaway(session, status_code); + return nghttp2_submit_goaway(session, error_code, NULL, 0); } int nghttp2_session_is_my_stream_id(nghttp2_session *session, @@ -104,7 +104,7 @@ static int nghttp2_outbound_item_compar(const void *lhsx, const void *rhsx) if(lhs->pri == rhs->pri) { return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0); } else { - return lhs->pri-rhs->pri; + return lhs->pri - rhs->pri; } } @@ -125,28 +125,20 @@ static void nghttp2_inbound_frame_reset(nghttp2_inbound_frame *iframe) static size_t nghttp2_inbound_frame_payload_nv_offset (nghttp2_inbound_frame *iframe) { - uint16_t type, version; ssize_t offset; - type = nghttp2_get_uint16(&iframe->headbuf[2]); - version = nghttp2_get_uint16(&iframe->headbuf[0]) & NGHTTP2_VERSION_MASK; - offset = nghttp2_frame_nv_offset(type, version); + offset = nghttp2_frame_nv_offset(iframe->headbuf); if(offset != -1) { - offset -= NGHTTP2_HEAD_LEN; + offset -= NGHTTP2_FRAME_HEAD_LENGTH; } return offset; } static int nghttp2_session_new(nghttp2_session **session_ptr, - uint16_t version, const nghttp2_session_callbacks *callbacks, void *user_data, - size_t cli_certvec_length, int hd_comp) { int r; - if(version != NGHTTP2_PROTO_SPDY2 && version != NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_ERR_UNSUPPORTED_VERSION; - } *session_ptr = malloc(sizeof(nghttp2_session)); if(*session_ptr == NULL) { r = NGHTTP2_ERR_NOMEM; @@ -154,21 +146,18 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, } memset(*session_ptr, 0, sizeof(nghttp2_session)); - (*session_ptr)->version = version; - - /* next_stream_id, last_recv_stream_id and next_unique_id are - initialized in either nghttp2_session_client_new or - nghttp2_session_server_new */ - - (*session_ptr)->flow_control = - (*session_ptr)->version == NGHTTP2_PROTO_SPDY3; - - (*session_ptr)->last_ping_unique_id = 0; + /* next_stream_id and last_recv_stream_id are initialized in either + nghttp2_session_client_new or nghttp2_session_server_new */ (*session_ptr)->next_seq = 0; + (*session_ptr)->remote_flow_control = 1; + (*session_ptr)->local_flow_control = 1; + (*session_ptr)->window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; - (*session_ptr)->last_good_stream_id = 0; + (*session_ptr)->last_stream_id = 0; (*session_ptr)->max_recv_ctrl_frame_buf = (1 << 24)-1; @@ -235,16 +224,8 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, nghttp2_inbound_frame_reset(&(*session_ptr)->iframe); - r = nghttp2_client_cert_vector_init(&(*session_ptr)->cli_certvec, - cli_certvec_length); - if(r != 0) { - goto fail_client_cert_vector; - } - return 0; - fail_client_cert_vector: - free((*session_ptr)->iframe.buf); fail_iframe_buf: free((*session_ptr)->nvbuf); fail_nvbuf: @@ -265,38 +246,32 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, } int nghttp2_session_client_new(nghttp2_session **session_ptr, - uint16_t version, const nghttp2_session_callbacks *callbacks, void *user_data) { int r; /* For client side session, header compression is disabled. */ - r = nghttp2_session_new(session_ptr, version, callbacks, user_data, - NGHTTP2_INITIAL_CLIENT_CERT_VECTOR_LENGTH, 0); + r = nghttp2_session_new(session_ptr, callbacks, user_data, 0); if(r == 0) { /* IDs for use in client */ (*session_ptr)->next_stream_id = 1; (*session_ptr)->last_recv_stream_id = 0; - (*session_ptr)->next_unique_id = 1; } return r; } int nghttp2_session_server_new(nghttp2_session **session_ptr, - uint16_t version, const nghttp2_session_callbacks *callbacks, void *user_data) { int r; /* Enable header compression on server side. */ - r = nghttp2_session_new(session_ptr, version, callbacks, user_data, - 0, 1 /* hd_comp */); + r = nghttp2_session_new(session_ptr, callbacks, user_data, 1 /* hd_comp */); if(r == 0) { (*session_ptr)->server = 1; /* IDs for use in client */ (*session_ptr)->next_stream_id = 2; (*session_ptr)->last_recv_stream_id = 0; - (*session_ptr)->next_unique_id = 2; } return r; } @@ -343,8 +318,7 @@ void nghttp2_session_del(nghttp2_session *session) free(session->nvbuf); nghttp2_buffer_free(&session->iframe.inflatebuf); free(session->iframe.buf); - nghttp2_client_cert_vector_free(&session->cli_certvec); - free(session); + free(session); } int nghttp2_session_add_frame(nghttp2_session *session, @@ -362,26 +336,28 @@ int nghttp2_session_add_frame(nghttp2_session *session, item->frame = abs_frame; item->aux_data = aux_data; item->seq = session->next_seq++; - /* Set priority lowest at the moment. */ - item->pri = nghttp2_session_get_pri_lowest(session); - if(frame_cat == NGHTTP2_CTRL) { + /* Set priority to the default value at the moment. */ + item->pri = NGHTTP2_PRI_DEFAULT; + if(frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame = (nghttp2_frame*)abs_frame; - nghttp2_frame_type frame_type = frame->ctrl.hd.type; - switch(frame_type) { - case NGHTTP2_SYN_STREAM: - item->pri = frame->syn_stream.pri; - break; - case NGHTTP2_SYN_REPLY: { - nghttp2_stream *stream = nghttp2_session_get_stream - (session, frame->syn_reply.stream_id); - if(stream) { - item->pri = stream->pri; + switch(frame->hd.type) { + case NGHTTP2_HEADERS: + if(frame->hd.stream_id == -1) { + /* Initial HEADERS, which will open stream */ + item->pri = frame->headers.pri; + } else { + /* Otherwise, the frame must have stream ID. We use its + priority value. */ + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if(stream) { + item->pri = stream->pri; + } } break; - } case NGHTTP2_RST_STREAM: { - nghttp2_stream *stream = nghttp2_session_get_stream - (session, frame->rst_stream.stream_id); + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if(stream) { stream->state = NGHTTP2_STREAM_CLOSING; item->pri = stream->pri; @@ -392,10 +368,6 @@ int nghttp2_session_add_frame(nghttp2_session *session, /* Should NGHTTP2_SETTINGS have higher priority? */ item->pri = -1; break; - case NGHTTP2_NOOP: - /* We don't have any public API to add NOOP, so here is - unreachable. */ - assert(0); case NGHTTP2_PING: /* Ping has highest priority. */ item->pri = NGHTTP2_OB_PRI_PING; @@ -403,35 +375,28 @@ int nghttp2_session_add_frame(nghttp2_session *session, case NGHTTP2_GOAWAY: /* Should GOAWAY have higher priority? */ break; - case NGHTTP2_HEADERS: { - nghttp2_stream *stream = nghttp2_session_get_stream - (session, frame->headers.stream_id); - if(stream) { - item->pri = stream->pri; + case NGHTTP2_WINDOW_UPDATE: + if(frame->hd.stream_id == 0) { + /* Connection level window update should have higher priority */ + item->pri = -1; + } else { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if(stream) { + item->pri = stream->pri; + } } break; } - case NGHTTP2_WINDOW_UPDATE: { - nghttp2_stream *stream = nghttp2_session_get_stream - (session, frame->window_update.stream_id); - if(stream) { - item->pri = stream->pri; - } - break; - } - case NGHTTP2_CREDENTIAL: - item->pri = NGHTTP2_OB_PRI_CREDENTIAL; - break; - } - if(frame_type == NGHTTP2_SYN_STREAM) { + if(frame->hd.type == NGHTTP2_HEADERS && frame->hd.stream_id == -1) { r = nghttp2_pq_push(&session->ob_ss_pq, item); } else { r = nghttp2_pq_push(&session->ob_pq, item); } - } else if(frame_cat == NGHTTP2_DATA) { + } else if(frame_cat == NGHTTP2_CAT_DATA) { nghttp2_data *data_frame = (nghttp2_data*)abs_frame; - nghttp2_stream *stream = nghttp2_session_get_stream(session, - data_frame->stream_id); + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id); if(stream) { item->pri = stream->pri; } @@ -449,7 +414,8 @@ int nghttp2_session_add_frame(nghttp2_session *session, } int nghttp2_session_add_rst_stream(nghttp2_session *session, - int32_t stream_id, uint32_t status_code) + int32_t stream_id, + nghttp2_error_code error_code) { int r; nghttp2_frame *frame; @@ -457,9 +423,8 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, if(frame == NULL) { return NGHTTP2_ERR_NOMEM; } - nghttp2_frame_rst_stream_init(&frame->rst_stream, session->version, - stream_id, status_code); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); + nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); if(r != 0) { nghttp2_frame_rst_stream_free(&frame->rst_stream); free(frame); @@ -470,7 +435,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session, int32_t stream_id, - uint8_t flags, uint8_t pri, + uint8_t flags, int32_t pri, nghttp2_stream_state initial_state, void *stream_user_data) { @@ -480,6 +445,10 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session, return NULL; } nghttp2_stream_init(stream, stream_id, flags, pri, initial_state, + !session->remote_settings + [NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS], + !session->local_settings + [NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS], session->remote_settings [NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], stream_user_data); @@ -497,14 +466,14 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session, } int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - nghttp2_status_code status_code) + nghttp2_error_code error_code) { nghttp2_stream *stream = nghttp2_session_get_stream(session, stream_id); if(stream) { if(stream->state != NGHTTP2_STREAM_INITIAL && session->callbacks.on_stream_close_callback) { session->callbacks.on_stream_close_callback(session, stream_id, - status_code, + error_code, session->user_data); } if(nghttp2_session_is_my_stream_id(session, stream_id)) { @@ -526,27 +495,22 @@ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, { if((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { return nghttp2_session_close_stream(session, stream->stream_id, - NGHTTP2_OK); + NGHTTP2_NO_ERROR); } else { return 0; } } -void nghttp2_session_close_pushed_streams(nghttp2_session *session, - int32_t stream_id, - nghttp2_status_code status_code) -{ - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if(stream) { - size_t i; - for(i = 0; i < stream->pushed_streams_length; ++i) { - nghttp2_session_close_stream(session, stream->pushed_streams[i], - status_code); - } - } -} - +/* + * Check that we can send a frame to the |stream|. This function + * returns 0 if we can send a frame to the |frame|, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + */ static int nghttp2_predicate_stream_for_send(nghttp2_stream *stream) { if(stream == NULL) { @@ -559,46 +523,37 @@ static int nghttp2_predicate_stream_for_send(nghttp2_stream *stream) } /* - * This function checks SYN_STREAM frame |frame| can be sent at this - * time. + * This function checks HEADERS frame |frame|, which opens stream, can + * be sent at this time. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * NGHTTP2_ERR_STREAM_CLOSED - * The Associated-To-Stream is already closed or does not exist. + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. * NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE * Stream ID has reached the maximum value. Therefore no stream ID * is available. */ static int nghttp2_session_predicate_syn_stream_send -(nghttp2_session *session, - nghttp2_syn_stream *frame) +(nghttp2_session *session, nghttp2_headers *frame) { if(session->goaway_flags) { /* When GOAWAY is sent or received, peer must not send new SYN_STREAM. */ - return NGHTTP2_ERR_SYN_STREAM_NOT_ALLOWED; + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; } /* All 32bit signed stream IDs are spent. */ if(session->next_stream_id > INT32_MAX) { return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; } - if(frame->assoc_stream_id != 0) { - /* Check associated stream is active. */ - /* We assume here that if frame->assoc_stream_id != 0, - session->server is always 1. */ - if(nghttp2_session_get_stream(session, frame->assoc_stream_id) == - NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - } return 0; } /* - * This function checks SYN_REPLY with the stream ID |stream_id| can - * be sent at this time. + * This function checks HEADERS, which is the first frame from the + * server, with the stream ID |stream_id| can be sent at this time. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -639,8 +594,9 @@ static int nghttp2_session_predicate_syn_reply_send(nghttp2_session *session, } /* - * This function checks HEADERS with the stream ID |stream_id| can - * be sent at this time. + * This function checks HEADERS, which is neither stream-opening nor + * first response header, with the stream ID |stream_id| can be sent + * at this time. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -696,10 +652,14 @@ static int nghttp2_session_predicate_headers_send(nghttp2_session *session, * RST_STREAM was queued for this stream. */ static int nghttp2_session_predicate_window_update_send -(nghttp2_session *session, - int32_t stream_id) +(nghttp2_session *session, int32_t stream_id) { - nghttp2_stream *stream = nghttp2_session_get_stream(session, stream_id); + nghttp2_stream *stream; + if(stream_id == 0) { + /* Connection-level window update */ + return 0; + } + stream = nghttp2_session_get_stream(session, stream_id); if(stream == NULL) { return NGHTTP2_ERR_STREAM_CLOSED; } @@ -718,7 +678,8 @@ static int nghttp2_session_predicate_window_update_send static size_t nghttp2_session_next_data_read(nghttp2_session *session, nghttp2_stream *stream) { - if(session->flow_control == 0) { + /* TODO implement connection-level flow control here */ + if(stream->remote_flow_control == 0) { return NGHTTP2_DATA_PAYLOAD_LENGTH; } else if(stream->window_size > 0) { return stream->window_size < NGHTTP2_DATA_PAYLOAD_LENGTH ? @@ -788,243 +749,80 @@ static int nghttp2_session_predicate_data_send(nghttp2_session *session, } } -/* - * Retrieves cryptographic proof for the given |origin| using callback - * function and store it in |proof|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int nghttp2_session_get_credential_proof(nghttp2_session *session, - const nghttp2_origin *origin, - nghttp2_mem_chunk *proof) -{ - proof->length = session->callbacks.get_credential_proof(session, - origin, - NULL, 0, - session->user_data); - proof->data = malloc(proof->length); - if(proof->data == NULL) { - return NGHTTP2_ERR_NOMEM; - } - session->callbacks.get_credential_proof(session, origin, - proof->data, proof->length, - session->user_data); - return 0; -} - -/* - * Retrieves client certificate chain for the given |origin| using - * callback functions and store the pointer to the allocated buffer - * containing certificate chain to |*certs_ptr|. The length of - * certificate chain is exactly |ncerts|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int nghttp2_session_get_credential_cert(nghttp2_session *session, - const nghttp2_origin *origin, - nghttp2_mem_chunk **certs_ptr, - size_t ncerts) -{ - nghttp2_mem_chunk *certs; - size_t i, j; - certs = malloc(sizeof(nghttp2_mem_chunk)*ncerts); - if(certs == NULL) { - return NGHTTP2_ERR_NOMEM; - } - for(i = 0; i < ncerts; ++i) { - certs[i].length = session->callbacks.get_credential_cert - (session, - origin, - i, NULL, 0, - session->user_data); - certs[i].data = malloc(certs[i].length); - if(certs[i].data == NULL) { - goto fail; - } - session->callbacks.get_credential_cert(session, origin, i, - certs[i].data, certs[i].length, - session->user_data); - } - *certs_ptr = certs; - return 0; - fail: - for(j = 0; j < i; ++j) { - free(certs[j].data); - } - free(certs); - return NGHTTP2_ERR_NOMEM; -} - -int nghttp2_session_prep_credential(nghttp2_session *session, - nghttp2_syn_stream *syn_stream) -{ - int rv; - nghttp2_origin origin; - nghttp2_frame *frame; - nghttp2_mem_chunk proof; - if(session->cli_certvec.size == 0) { - return 0; - } - rv = nghttp2_frame_nv_set_origin(syn_stream->nv, &origin); - if(rv == 0) { - size_t slot; - slot = nghttp2_client_cert_vector_find(&session->cli_certvec, &origin); - if(slot == 0) { - ssize_t ncerts; - ncerts = session->callbacks.get_credential_ncerts(session, &origin, - session->user_data); - if(ncerts > 0) { - nghttp2_mem_chunk *certs; - nghttp2_origin *origin_copy; - frame = malloc(sizeof(nghttp2_frame)); - if(frame == NULL) { - return NGHTTP2_ERR_NOMEM; - } - origin_copy = malloc(sizeof(nghttp2_origin)); - if(origin_copy == NULL) { - goto fail_after_frame; - } - *origin_copy = origin; - slot = nghttp2_client_cert_vector_put(&session->cli_certvec, - origin_copy); - - rv = nghttp2_session_get_credential_proof(session, &origin, &proof); - if(rv != 0) { - goto fail_after_frame; - } - rv = nghttp2_session_get_credential_cert(session, &origin, - &certs, ncerts); - if(rv != 0) { - goto fail_after_proof; - } - nghttp2_frame_credential_init(&frame->credential, - session->version, - slot, &proof, certs, ncerts); - rv = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); - if(rv != 0) { - nghttp2_frame_credential_free(&frame->credential); - free(frame); - return rv; - } - return NGHTTP2_ERR_CREDENTIAL_PENDING; - } - } else { - return slot; - } - } - return 0; - fail_after_proof: - free(proof.data); - fail_after_frame: - free(frame); - return rv; - -} - static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, nghttp2_outbound_item *item) { ssize_t framebuflen = 0; - if(item->frame_cat == NGHTTP2_CTRL) { + if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; - nghttp2_frame_type frame_type; frame = nghttp2_outbound_item_get_ctrl_frame(item); - frame_type = nghttp2_outbound_item_get_ctrl_frame_type(item); - switch(frame_type) { - case NGHTTP2_SYN_STREAM: { - int32_t stream_id; - nghttp2_syn_stream_aux_data *aux_data; - int r; - r = nghttp2_session_predicate_syn_stream_send(session, - &frame->syn_stream); - if(r != 0) { - return r; - } - if(session->version == NGHTTP2_PROTO_SPDY3 && - session->callbacks.get_credential_proof && - session->callbacks.get_credential_cert && - !session->server) { - int slot_index; - slot_index = nghttp2_session_prep_credential(session, - &frame->syn_stream); - if(slot_index == NGHTTP2_ERR_CREDENTIAL_PENDING) { - /* CREDENTIAL frame has been queued. SYN_STREAM has to be - sent after that. Change the priority of this item to - achieve this. */ - item->pri = NGHTTP2_OB_PRI_AFTER_CREDENTIAL; - r = nghttp2_pq_push(&session->ob_ss_pq, item); - if(r == 0) { - return NGHTTP2_ERR_CREDENTIAL_PENDING; - } else { - return r; - } + switch(frame->hd.type) { + case NGHTTP2_HEADERS: + if(frame->hd.stream_id == -1) { + /* initial HEADERS, which opens stream */ + int32_t stream_id; + nghttp2_headers_aux_data *aux_data; + int r; + frame->headers.cat = NGHTTP2_HCAT_START_STREAM; + r = nghttp2_session_predicate_syn_stream_send(session, + &frame->headers); + if(r != 0) { + return r; } - frame->syn_stream.slot = slot_index; - } - stream_id = session->next_stream_id; - frame->syn_stream.stream_id = stream_id; - session->next_stream_id += 2; - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_3to2(frame->syn_stream.nv); - nghttp2_frame_nv_sort(frame->syn_stream.nv); - } - framebuflen = nghttp2_frame_pack_syn_stream(&session->aob.framebuf, - &session->aob.framebufmax, - &session->nvbuf, - &session->nvbuflen, - &frame->syn_stream, - &session->hd_deflater); - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame->syn_stream.nv); - nghttp2_frame_nv_sort(frame->syn_stream.nv); - } - if(framebuflen < 0) { - return framebuflen; - } - aux_data = (nghttp2_syn_stream_aux_data*)item->aux_data; - if(nghttp2_session_open_stream(session, stream_id, - frame->syn_stream.hd.flags, - frame->syn_stream.pri, - NGHTTP2_STREAM_INITIAL, - aux_data->stream_user_data) == NULL) { - return NGHTTP2_ERR_NOMEM; - } - break; - } - case NGHTTP2_SYN_REPLY: { - int r; - r = nghttp2_session_predicate_syn_reply_send(session, - frame->syn_reply.stream_id); - if(r != 0) { - return r; - } - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_3to2(frame->syn_reply.nv); - nghttp2_frame_nv_sort(frame->syn_reply.nv); - } - framebuflen = nghttp2_frame_pack_syn_reply(&session->aob.framebuf, + stream_id = session->next_stream_id; + frame->hd.stream_id = stream_id; + session->next_stream_id += 2; + framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, &session->aob.framebufmax, &session->nvbuf, &session->nvbuflen, - &frame->syn_reply, + &frame->headers, &session->hd_deflater); - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame->syn_reply.nv); - nghttp2_frame_nv_sort(frame->syn_reply.nv); - } - if(framebuflen < 0) { - return framebuflen; + if(framebuflen < 0) { + return framebuflen; + } + aux_data = (nghttp2_headers_aux_data*)item->aux_data; + if(nghttp2_session_open_stream + (session, stream_id, + frame->hd.flags, + frame->headers.pri, + NGHTTP2_STREAM_INITIAL, + aux_data ? aux_data->stream_user_data : NULL) == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else if(nghttp2_session_predicate_syn_reply_send + (session, frame->hd.stream_id) == 0) { + frame->headers.cat = NGHTTP2_HCAT_REPLY; + /* first response HEADERS */ + framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, + &frame->headers, + &session->hd_deflater); + if(framebuflen < 0) { + return framebuflen; + } + + } else { + int r; + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + r = nghttp2_session_predicate_headers_send(session, + frame->hd.stream_id); + if(r != 0) { + return r; + } + framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, + &frame->headers, + &session->hd_deflater); + if(framebuflen < 0) { + return framebuflen; + } } break; - } case NGHTTP2_RST_STREAM: framebuflen = nghttp2_frame_pack_rst_stream(&session->aob.framebuf, &session->aob.framebufmax, @@ -1041,10 +839,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return framebuflen; } break; - case NGHTTP2_NOOP: - /* We don't have any public API to add NOOP, so here is - unreachable. */ - assert(0); case NGHTTP2_PING: framebuflen = nghttp2_frame_pack_ping(&session->aob.framebuf, &session->aob.framebufmax, @@ -1053,36 +847,10 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return framebuflen; } break; - case NGHTTP2_HEADERS: { - int r; - r = nghttp2_session_predicate_headers_send(session, - frame->headers.stream_id); - if(r != 0) { - return r; - } - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_3to2(frame->headers.nv); - nghttp2_frame_nv_sort(frame->headers.nv); - } - framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, - &session->aob.framebufmax, - &session->nvbuf, - &session->nvbuflen, - &frame->headers, - &session->hd_deflater); - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame->headers.nv); - nghttp2_frame_nv_sort(frame->headers.nv); - } - if(framebuflen < 0) { - return framebuflen; - } - break; - } case NGHTTP2_WINDOW_UPDATE: { int r; r = nghttp2_session_predicate_window_update_send - (session, frame->window_update.stream_id); + (session, frame->hd.stream_id); if(r != 0) { return r; } @@ -1097,9 +865,9 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, case NGHTTP2_GOAWAY: if(session->goaway_flags & NGHTTP2_GOAWAY_SEND) { /* TODO The spec does not mandate that both endpoints have to - exchange GOAWAY. This implementation allows receiver of first - GOAWAY can sent its own GOAWAY to tell the remote peer that - last-good-stream-id. */ + exchange GOAWAY. This implementation allows receiver of + first GOAWAY can sent its own GOAWAY to tell the remote + peer that last-stream-id. */ return NGHTTP2_ERR_GOAWAY_ALREADY_SENT; } framebuflen = nghttp2_frame_pack_goaway(&session->aob.framebuf, @@ -1109,29 +877,20 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return framebuflen; } break; - case NGHTTP2_CREDENTIAL: { - framebuflen = nghttp2_frame_pack_credential(&session->aob.framebuf, - &session->aob.framebufmax, - &frame->credential); - if(framebuflen < 0) { - return framebuflen; - } - break; - } default: framebuflen = NGHTTP2_ERR_INVALID_ARGUMENT; } - } else if(item->frame_cat == NGHTTP2_DATA) { + } else if(item->frame_cat == NGHTTP2_CAT_DATA) { size_t next_readmax; nghttp2_stream *stream; nghttp2_data *data_frame; int r; data_frame = nghttp2_outbound_item_get_data_frame(item); - r = nghttp2_session_predicate_data_send(session, data_frame->stream_id); + r = nghttp2_session_predicate_data_send(session, data_frame->hd.stream_id); if(r != 0) { return r; } - stream = nghttp2_session_get_stream(session, data_frame->stream_id); + stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id); /* Assuming stream is not NULL */ assert(stream); next_readmax = nghttp2_session_next_data_read(session, stream); @@ -1148,7 +907,7 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, nghttp2_stream_defer_data(stream, item, NGHTTP2_DEFERRED_NONE); return NGHTTP2_ERR_DEFERRED; } else if(framebuflen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - r = nghttp2_session_add_rst_stream(session, data_frame->stream_id, + r = nghttp2_session_add_rst_stream(session, data_frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if(r == 0) { return framebuflen; @@ -1254,10 +1013,12 @@ nghttp2_outbound_item* nghttp2_session_pop_next_ob_item static void nghttp2_outbound_item_adjust_pri(nghttp2_session *session, nghttp2_outbound_item *item) { - if(item->pri > nghttp2_session_get_pri_lowest(session)) { + if(item->pri == NGHTTP2_PRI_LOWEST) { item->pri = item->inipri; + } else if(item->pri > (int32_t)NGHTTP2_PRI_LOWEST/2) { + item->pri = NGHTTP2_PRI_LOWEST; } else { - ++item->pri; + item->pri *= 2; } } @@ -1274,133 +1035,106 @@ static void nghttp2_outbound_item_adjust_pri(nghttp2_session *session, */ static int nghttp2_session_after_frame_sent(nghttp2_session *session) { - /* TODO handle FIN flag. */ nghttp2_outbound_item *item = session->aob.item; - if(item->frame_cat == NGHTTP2_CTRL) { + if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; - nghttp2_frame_type type; frame = nghttp2_outbound_item_get_ctrl_frame(session->aob.item); - type = nghttp2_outbound_item_get_ctrl_frame_type(session->aob.item); - if(session->callbacks.on_ctrl_send_callback) { - session->callbacks.on_ctrl_send_callback - (session, type, frame, session->user_data); + if(session->callbacks.on_frame_send_callback) { + session->callbacks.on_frame_send_callback(session, frame, + session->user_data); } - switch(type) { - case NGHTTP2_SYN_STREAM: { + switch(frame->hd.type) { + case NGHTTP2_HEADERS: { nghttp2_stream *stream = - nghttp2_session_get_stream(session, frame->syn_stream.stream_id); + nghttp2_session_get_stream(session, frame->hd.stream_id); + nghttp2_headers_aux_data *aux_data; if(stream) { - nghttp2_syn_stream_aux_data *aux_data; - stream->state = NGHTTP2_STREAM_OPENING; - if(frame->syn_stream.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - if(frame->syn_stream.hd.flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } - nghttp2_session_close_stream_if_shut_rdwr(session, stream); - /* We assume aux_data is a pointer to nghttp2_syn_stream_aux_data */ - aux_data = (nghttp2_syn_stream_aux_data*)item->aux_data; - if(aux_data->data_prd) { - int r; - /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ - r = nghttp2_submit_data(session, frame->syn_stream.stream_id, - NGHTTP2_DATA_FLAG_FIN, aux_data->data_prd); - if(r != 0) { - /* FATAL error */ - assert(r < NGHTTP2_ERR_FATAL); - /* TODO If r is not FATAL, we should send RST_STREAM. */ - return r; + switch(frame->headers.cat) { + case NGHTTP2_HCAT_START_STREAM: { + stream->state = NGHTTP2_STREAM_OPENING; + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); } - } - } - break; - } - case NGHTTP2_SYN_REPLY: { - nghttp2_stream *stream = - nghttp2_session_get_stream(session, frame->syn_reply.stream_id); - if(stream) { - stream->state = NGHTTP2_STREAM_OPENED; - if(frame->syn_reply.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if(item->aux_data) { - /* We assume aux_data is a pointer to nghttp2_data_provider */ - nghttp2_data_provider *data_prd = - (nghttp2_data_provider*)item->aux_data; - int r; - r = nghttp2_submit_data(session, frame->syn_reply.stream_id, - NGHTTP2_DATA_FLAG_FIN, data_prd); - if(r != 0) { - /* FATAL error */ - assert(r < NGHTTP2_ERR_FATAL); - /* TODO If r is not FATAL, we should send RST_STREAM. */ - return r; + nghttp2_session_close_stream_if_shut_rdwr(session, stream); + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = (nghttp2_headers_aux_data*)item->aux_data; + if(aux_data && aux_data->data_prd) { + int r; + /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ + r = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, aux_data->data_prd); + if(r != 0) { + /* FATAL error */ + assert(r < NGHTTP2_ERR_FATAL); + /* TODO If r is not FATAL, we should send RST_STREAM. */ + return r; + } } + break; + } + case NGHTTP2_HCAT_REPLY: + stream->state = NGHTTP2_STREAM_OPENED; + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + nghttp2_session_close_stream_if_shut_rdwr(session, stream); + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = (nghttp2_headers_aux_data*)item->aux_data; + if(aux_data && aux_data->data_prd) { + int r; + r = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, aux_data->data_prd); + if(r != 0) { + /* FATAL error */ + assert(r < NGHTTP2_ERR_FATAL); + /* TODO If r is not FATAL, we should send RST_STREAM. */ + return r; + } + } + break; + case NGHTTP2_HCAT_HEADERS: + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + nghttp2_session_close_stream_if_shut_rdwr(session, stream); + break; } } break; } case NGHTTP2_RST_STREAM: - if(!session->server && - nghttp2_session_is_my_stream_id(session, - frame->rst_stream.stream_id) && - frame->rst_stream.status_code == NGHTTP2_CANCEL) { - nghttp2_session_close_pushed_streams(session, - frame->rst_stream.stream_id, - frame->rst_stream.status_code); - } - nghttp2_session_close_stream(session, frame->rst_stream.stream_id, - frame->rst_stream.status_code); + nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); break; case NGHTTP2_SETTINGS: /* nothing to do */ break; - case NGHTTP2_NOOP: - /* We don't have any public API to add NOOP, so here is - unreachable. */ - assert(0); case NGHTTP2_PING: - /* We record the time now and show application code RTT when - reply PING is received. */ - session->last_ping_unique_id = frame->ping.unique_id; + /* nothing to do */ break; case NGHTTP2_GOAWAY: session->goaway_flags |= NGHTTP2_GOAWAY_SEND; break; - case NGHTTP2_HEADERS: { - nghttp2_stream *stream = - nghttp2_session_get_stream(session, frame->headers.stream_id); - if(stream) { - if(frame->headers.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - nghttp2_session_close_stream_if_shut_rdwr(session, stream); - } - break; - } case NGHTTP2_WINDOW_UPDATE: break; - case NGHTTP2_CREDENTIAL: - break; } nghttp2_active_outbound_item_reset(&session->aob); - } else if(item->frame_cat == NGHTTP2_DATA) { + } else if(item->frame_cat == NGHTTP2_CAT_DATA) { int r; nghttp2_data *data_frame; data_frame = nghttp2_outbound_item_get_data_frame(session->aob.item); if(session->callbacks.on_data_send_callback) { session->callbacks.on_data_send_callback (session, - data_frame->eof ? data_frame->flags : - (data_frame->flags & (~NGHTTP2_DATA_FLAG_FIN)), - data_frame->stream_id, - session->aob.framebuflen-NGHTTP2_HEAD_LEN, session->user_data); + data_frame->eof ? data_frame->hd.flags : + (data_frame->hd.flags & (~NGHTTP2_FLAG_END_STREAM)), + data_frame->hd.stream_id, + session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH, + session->user_data); } - if(data_frame->eof && (data_frame->flags & NGHTTP2_DATA_FLAG_FIN)) { + if(data_frame->eof && (data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { nghttp2_stream *stream = - nghttp2_session_get_stream(session, data_frame->stream_id); + nghttp2_session_get_stream(session, data_frame->hd.stream_id); if(stream) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); nghttp2_session_close_stream_if_shut_rdwr(session, stream); @@ -1410,7 +1144,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) further data. */ if(data_frame->eof || nghttp2_session_predicate_data_send(session, - data_frame->stream_id) != 0) { + data_frame->hd.stream_id) != 0) { nghttp2_active_outbound_item_reset(&session->aob); } else { nghttp2_outbound_item* next_item; @@ -1422,7 +1156,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) if(next_item == NULL || session->aob.item->pri <= next_item->pri) { size_t next_readmax; nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, data_frame->stream_id); + stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id); /* Assuming stream is not NULL */ assert(stream); next_readmax = nghttp2_session_next_data_read(session, stream); @@ -1447,7 +1181,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) /* Stop DATA frame chain and issue RST_STREAM to close the stream. We don't return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */ - r = nghttp2_session_add_rst_stream(session, data_frame->stream_id, + r = nghttp2_session_add_rst_stream(session, data_frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); nghttp2_active_outbound_item_reset(&session->aob); if(r != 0) { @@ -1501,25 +1235,21 @@ int nghttp2_session_send(nghttp2_session *session) framebuflen == NGHTTP2_ERR_CREDENTIAL_PENDING) { continue; } else if(framebuflen < 0) { - if(item->frame_cat == NGHTTP2_CTRL && - session->callbacks.on_ctrl_not_send_callback && + if(item->frame_cat == NGHTTP2_CAT_CTRL && + session->callbacks.on_frame_not_send_callback && nghttp2_is_non_fatal(framebuflen)) { /* The library is responsible for the transmission of WINDOW_UPDATE frame, so we don't call error callback for it. */ - nghttp2_frame_type frame_type; - frame_type = nghttp2_outbound_item_get_ctrl_frame_type(item); - if(frame_type != NGHTTP2_WINDOW_UPDATE) { - session->callbacks.on_ctrl_not_send_callback - (session, - frame_type, - nghttp2_outbound_item_get_ctrl_frame(item), - framebuflen, - session->user_data); + nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item); + if(frame->hd.type != NGHTTP2_WINDOW_UPDATE) { + session->callbacks.on_frame_not_send_callback + (session, frame, framebuflen, session->user_data); } } nghttp2_outbound_item_free(item); free(item); + if(nghttp2_is_fatal(framebuflen)) { return framebuflen; } else { @@ -1529,11 +1259,10 @@ int nghttp2_session_send(nghttp2_session *session) session->aob.item = item; session->aob.framebuflen = framebuflen; /* Call before_send callback */ - if(item->frame_cat == NGHTTP2_CTRL && - session->callbacks.before_ctrl_send_callback) { - session->callbacks.before_ctrl_send_callback + if(item->frame_cat == NGHTTP2_CAT_CTRL && + session->callbacks.before_frame_send_callback) { + session->callbacks.before_frame_send_callback (session, - nghttp2_outbound_item_get_ctrl_frame_type(item), nghttp2_outbound_item_get_ctrl_frame(item), session->user_data); } @@ -1550,14 +1279,13 @@ int nghttp2_session_send(nghttp2_session *session) } } else { session->aob.framebufoff += sentlen; - if(session->flow_control && - session->aob.item->frame_cat == NGHTTP2_DATA) { + if(session->aob.item->frame_cat == NGHTTP2_CAT_DATA) { nghttp2_data *frame; nghttp2_stream *stream; frame = nghttp2_outbound_item_get_data_frame(session->aob.item); - stream = nghttp2_session_get_stream(session, frame->stream_id); - if(stream) { - stream->window_size -= nghttp2_get_uint32(&session->aob.framebuf[4]); + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if(stream && stream->remote_flow_control) { + stream->window_size -= nghttp2_get_uint16(&session->aob.framebuf[0]); } } if(session->aob.framebufoff == session->aob.framebuflen) { @@ -1600,12 +1328,12 @@ static void nghttp2_session_call_on_request_recv } } -static void nghttp2_session_call_on_ctrl_frame_received -(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame) +static void nghttp2_session_call_on_frame_received +(nghttp2_session *session, nghttp2_frame *frame) { - if(session->callbacks.on_ctrl_recv_callback) { - session->callbacks.on_ctrl_recv_callback - (session, type, frame, session->user_data); + if(session->callbacks.on_frame_recv_callback) { + session->callbacks.on_frame_recv_callback(session, frame, + session->user_data); } } @@ -1627,44 +1355,15 @@ static int nghttp2_session_is_new_peer_stream_id(nghttp2_session *session, } /* - * Returns non-zero iff version == session->version - */ -static int nghttp2_session_check_version(nghttp2_session *session, - uint16_t version) -{ - return session->version == version; -} - -/* - * Validates received SYN_STREAM frame |frame|. This function returns - * 0 if it succeeds, or non-zero nghttp2_status_code. + * Validates received HEADERS frame |frame| with + * NGHTTP2_HCAT_START_STREAM category_. This function returns 0 if it + * succeeds, or non-zero nghttp2_error_code. */ static int nghttp2_session_validate_syn_stream(nghttp2_session *session, - nghttp2_syn_stream *frame) + nghttp2_headers *frame) { - if(!nghttp2_session_check_version(session, frame->hd.version)) { - return NGHTTP2_UNSUPPORTED_VERSION; - } - if(session->server) { - if(frame->assoc_stream_id != 0) { - return NGHTTP2_PROTOCOL_ERROR; - } - } else { - if(frame->assoc_stream_id == 0) { - /* spdy/2 spec: When a client receives a SYN_STREAM from the - server with an Associated-To-Stream-ID of 0, it must reply with - a RST_STREAM with error code INVALID_STREAM. */ - return NGHTTP2_INVALID_STREAM; - } - if((frame->hd.flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL) == 0 || - nghttp2_session_get_stream(session, frame->assoc_stream_id) == NULL) { - /* It seems spdy/2 spec does not say which status code should be - returned in these cases. */ - return NGHTTP2_PROTOCOL_ERROR; - } - } if(nghttp2_session_is_incoming_concurrent_streams_max(session)) { - /* spdy/2 spec does not clearly say what to do when max concurrent + /* The spec does not clearly say what to do when max concurrent streams number is reached. The mod_spdy sends NGHTTP2_REFUSED_STREAM and we think it is reasonable. So we follow it. */ @@ -1677,18 +1376,17 @@ static int nghttp2_session_validate_syn_stream(nghttp2_session *session, static int nghttp2_session_handle_invalid_stream (nghttp2_session *session, int32_t stream_id, - nghttp2_frame_type type, nghttp2_frame *frame, - nghttp2_status_code status_code) + nghttp2_error_code error_code) { int r; - r = nghttp2_session_add_rst_stream(session, stream_id, status_code); + r = nghttp2_session_add_rst_stream(session, stream_id, error_code); if(r != 0) { return r; } - if(session->callbacks.on_invalid_ctrl_recv_callback) { - session->callbacks.on_invalid_ctrl_recv_callback - (session, type, frame, status_code, session->user_data); + if(session->callbacks.on_invalid_frame_recv_callback) { + session->callbacks.on_invalid_frame_recv_callback + (session, frame, error_code, session->user_data); } return 0; } @@ -1697,104 +1395,67 @@ int nghttp2_session_on_syn_stream_received(nghttp2_session *session, nghttp2_frame *frame) { int r = 0; - int status_code; + nghttp2_error_code error_code = NGHTTP2_NO_ERROR; if(session->goaway_flags) { - /* We don't accept SYN_STREAM after GOAWAY is sent or received. */ + /* We don't accept new stream after GOAWAY is sent or received. */ return 0; } - if(session->last_recv_stream_id == frame->syn_stream.stream_id) { - /* SPDY/3 spec says if an endpoint receives same stream ID twice, - it MUST issue a stream error with status code - PROTOCOL_ERROR. */ - status_code = NGHTTP2_PROTOCOL_ERROR; - } else if(!nghttp2_session_is_new_peer_stream_id - (session, frame->syn_stream.stream_id)) { - /* SPDY/3 spec says if an endpoint receives a SYN_STREAM with a - stream ID which is less than any previously received - SYN_STREAM, it MUST issue a session error with status + if(!nghttp2_session_is_new_peer_stream_id + (session, frame->hd.stream_id)) { + /* The spec says if an endpoint receives a HEADERS with invalid + stream ID, it MUST issue connection error with error code PROTOCOL_ERROR */ - if(session->callbacks.on_invalid_ctrl_recv_callback) { - session->callbacks.on_invalid_ctrl_recv_callback(session, - NGHTTP2_SYN_STREAM, - frame, - NGHTTP2_PROTOCOL_ERROR, - session->user_data); + if(session->callbacks.on_invalid_frame_recv_callback) { + session->callbacks.on_invalid_frame_recv_callback(session, + frame, + NGHTTP2_PROTOCOL_ERROR, + session->user_data); } - return nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); + return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } else { - session->last_recv_stream_id = frame->syn_stream.stream_id; - status_code = nghttp2_session_validate_syn_stream(session, - &frame->syn_stream); + session->last_recv_stream_id = frame->hd.stream_id; + error_code = nghttp2_session_validate_syn_stream(session, &frame->headers); } - if(status_code == 0) { - uint8_t flags = frame->syn_stream.hd.flags; - if((flags & NGHTTP2_CTRL_FLAG_FIN) && - (flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL)) { - /* If the stream is UNIDIRECTIONAL and FIN bit set, we can close - stream upon receiving SYN_STREAM. So, the stream needs not to - be opened. */ - } else { - nghttp2_stream *stream; - stream = nghttp2_session_open_stream(session, frame->syn_stream.stream_id, - frame->syn_stream.hd.flags, - frame->syn_stream.pri, - NGHTTP2_STREAM_OPENING, - NULL); - if(stream) { - if(flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } - if(flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - /* We don't call nghttp2_session_close_stream_if_shut_rdwr() - here because either NGHTTP2_CTRL_FLAG_FIN or - NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL is not set here. */ - } + if(error_code == 0) { + uint8_t flags = frame->hd.flags; + nghttp2_stream *stream; + stream = nghttp2_session_open_stream(session, + frame->hd.stream_id, + frame->hd.flags, + frame->headers.pri, + NGHTTP2_STREAM_OPENING, + NULL); + if(!stream) { + return NGHTTP2_ERR_NOMEM; } - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_SYN_STREAM, - frame); - if(flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_session_call_on_request_recv(session, - frame->syn_stream.stream_id); - if(flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL) { - /* Note that we call on_stream_close_callback without opening - stream. */ - if(session->callbacks.on_stream_close_callback) { - session->callbacks.on_stream_close_callback - (session, frame->syn_stream.stream_id, NGHTTP2_OK, - session->user_data); - } - } + if(flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + nghttp2_session_call_on_frame_received(session, frame); + if(flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_session_call_on_request_recv(session, frame->hd.stream_id); } } else { r = nghttp2_session_handle_invalid_stream - (session, frame->syn_stream.stream_id, NGHTTP2_SYN_STREAM, frame, - status_code); + (session, frame->hd.stream_id, frame, error_code); } return r; } int nghttp2_session_on_syn_reply_received(nghttp2_session *session, - nghttp2_frame *frame) + nghttp2_frame *frame, + nghttp2_stream *stream) { int r = 0; int valid = 0; - int status_code = NGHTTP2_PROTOCOL_ERROR; - nghttp2_stream *stream; - if(!nghttp2_session_check_version(session, frame->syn_reply.hd.version)) { - return 0; - } - if((stream = nghttp2_session_get_stream(session, - frame->syn_reply.stream_id)) && - (stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { - if(nghttp2_session_is_my_stream_id(session, frame->syn_reply.stream_id)) { + nghttp2_error_code error_code = NGHTTP2_PROTOCOL_ERROR; + if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { + if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { if(stream->state == NGHTTP2_STREAM_OPENING) { valid = 1; stream->state = NGHTTP2_STREAM_OPENED; - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_SYN_REPLY, - frame); - if(frame->syn_reply.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { + nghttp2_session_call_on_frame_received(session, frame); + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { /* This is the last frame of this stream, so disallow further receptions. */ nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); @@ -1806,20 +1467,88 @@ int nghttp2_session_on_syn_reply_received(nghttp2_session *session, eventually sent, so we just ignore this frame. */ valid = 1; } else { - if(session->version == NGHTTP2_PROTO_SPDY3) { - /* SPDY/3 spec says if multiple SYN_REPLY frames for the - same active stream ID are received, the receiver must - issue a stream error with the status code - STREAM_IN_USE. */ - status_code = NGHTTP2_STREAM_IN_USE; + /* It seems that the spec does not say what to do if multiple + HEADERS for the same active stream ID are receives. The + SPDY/3 spec says it should be treated as stream error with + error code STREAM_IN_USE. The spec does not such code + anymore. It would be safer to reject those broken client at + the moment. Do you accept the web server which responds + with multiple response headers? */ + if(session->callbacks.on_invalid_frame_recv_callback) { + session->callbacks.on_invalid_frame_recv_callback + (session, + frame, + NGHTTP2_PROTOCOL_ERROR, + session->user_data); } + return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } } + } else { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + error_code = NGHTTP2_STREAM_CLOSED; } if(!valid) { r = nghttp2_session_handle_invalid_stream - (session, frame->syn_reply.stream_id, NGHTTP2_SYN_REPLY, frame, - status_code); + (session, frame->hd.stream_id, frame, error_code); + } + return r; +} + +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) +{ + int r = 0; + int valid = 0; + nghttp2_error_code error_code = NGHTTP2_PROTOCOL_ERROR; + if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { + if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if(stream->state == NGHTTP2_STREAM_OPENED) { + valid = 1; + nghttp2_session_call_on_frame_received(session, frame); + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + nghttp2_session_close_stream_if_shut_rdwr(session, stream); + } + } else if(stream->state == NGHTTP2_STREAM_CLOSING) { + /* This is race condition. NGHTTP2_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + valid = 1; + } + } else { + /* If this is remote peer initiated stream, it is OK unless it + have sent FIN frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + valid = 1; + if(stream->state != NGHTTP2_STREAM_CLOSING) { + nghttp2_session_call_on_frame_received(session, frame); + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_session_call_on_request_recv(session, frame->hd.stream_id); + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + nghttp2_session_close_stream_if_shut_rdwr(session, stream); + } + } + } + } else { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + error_code = NGHTTP2_STREAM_CLOSED; + } + if(!valid) { + r = nghttp2_session_handle_invalid_stream + (session, frame->hd.stream_id, frame, error_code); } return r; } @@ -1827,19 +1556,9 @@ int nghttp2_session_on_syn_reply_received(nghttp2_session *session, int nghttp2_session_on_rst_stream_received(nghttp2_session *session, nghttp2_frame *frame) { - if(!nghttp2_session_check_version(session, frame->rst_stream.hd.version)) { - return 0; - } - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_RST_STREAM, - frame); - if(session->server && - !nghttp2_session_is_my_stream_id(session, frame->rst_stream.stream_id) && - frame->rst_stream.status_code == NGHTTP2_CANCEL) { - nghttp2_session_close_pushed_streams(session, frame->rst_stream.stream_id, - frame->rst_stream.status_code); - } - nghttp2_session_close_stream(session, frame->rst_stream.stream_id, - frame->rst_stream.status_code); + nghttp2_session_call_on_frame_received(session, frame); + nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); return 0; } @@ -1912,22 +1631,18 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, int rv; size_t i; int check[NGHTTP2_SETTINGS_MAX+1]; - if(!nghttp2_session_check_version(session, frame->settings.hd.version)) { - return 0; - } /* Check ID/value pairs and persist them if necessary. */ memset(check, 0, sizeof(check)); for(i = 0; i < frame->settings.niv; ++i) { nghttp2_settings_entry *entry = &frame->settings.iv[i]; - /* SPDY/3 spec says if the multiple values for the same ID were + /* The spec says if the multiple values for the same ID were found, use the first one and ignore the rest. */ if(entry->settings_id > NGHTTP2_SETTINGS_MAX || entry->settings_id == 0 || check[entry->settings_id] == 1) { continue; } check[entry->settings_id] = 1; - if(entry->settings_id == NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE && - session->flow_control) { + if(entry->settings_id == NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) { /* Update the initial window size of the all active streams */ /* Check that initial_window_size < (1u << 31) */ if(entry->value < (1u << 31)) { @@ -1936,22 +1651,10 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, return rv; } } - } else if(entry->settings_id == - NGHTTP2_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE) { - if(!session->server) { - /* Limit certificate vector length in the reasonable size. */ - entry->value = nghttp2_min(entry->value, - NGHTTP2_MAX_CLIENT_CERT_VECTOR_LENGTH); - rv = nghttp2_client_cert_vector_resize(&session->cli_certvec, - entry->value); - if(rv != 0) { - return rv; - } - } } session->remote_settings[entry->settings_id] = entry->value; } - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_SETTINGS, frame); + nghttp2_session_call_on_frame_received(session, frame); return 0; } @@ -1959,164 +1662,82 @@ int nghttp2_session_on_ping_received(nghttp2_session *session, nghttp2_frame *frame) { int r = 0; - if(!nghttp2_session_check_version(session, frame->ping.hd.version)) { - return 0; - } - if(frame->ping.unique_id != 0) { - if(session->last_ping_unique_id == frame->ping.unique_id) { - /* This is ping reply from peer */ - /* Assign 0 to last_ping_unique_id so that we can ignore same - ID. */ - session->last_ping_unique_id = 0; - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_PING, frame); - } else if((session->server && frame->ping.unique_id % 2 == 1) || - (!session->server && frame->ping.unique_id % 2 == 0)) { - /* Peer sent ping, so ping it back */ - r = nghttp2_session_add_ping(session, frame->ping.unique_id); - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_PING, frame); - } + if((frame->hd.flags & NGHTTP2_FLAG_PONG) == 0) { + /* Peer sent ping, so ping it back */ + r = nghttp2_session_add_ping(session, NGHTTP2_FLAG_PONG, + frame->ping.opaque_data); } + nghttp2_session_call_on_frame_received(session, frame); return r; } int nghttp2_session_on_goaway_received(nghttp2_session *session, nghttp2_frame *frame) { - if(!nghttp2_session_check_version(session, frame->goaway.hd.version)) { - return 0; - } - session->last_good_stream_id = frame->goaway.last_good_stream_id; + session->last_stream_id = frame->goaway.last_stream_id; session->goaway_flags |= NGHTTP2_GOAWAY_RECV; - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_GOAWAY, frame); + nghttp2_session_call_on_frame_received(session, frame); return 0; } int nghttp2_session_on_window_update_received(nghttp2_session *session, nghttp2_frame *frame) { - nghttp2_stream *stream; - if(!nghttp2_session_check_version(session, frame->window_update.hd.version)) { + if(frame->hd.stream_id == 0) { + /* Handle connection-level flow control here */ return 0; - } - if(!session->flow_control) { - return 0; - } - stream = nghttp2_session_get_stream(session, frame->window_update.stream_id); - if(stream) { - if(INT32_MAX-frame->window_update.delta_window_size < stream->window_size) { - int r; - r = nghttp2_session_handle_invalid_stream - (session, frame->window_update.stream_id, NGHTTP2_WINDOW_UPDATE, frame, - NGHTTP2_FLOW_CONTROL_ERROR); - return r; - } else { - stream->window_size += frame->window_update.delta_window_size; - if(stream->window_size > 0 && - stream->deferred_data != NULL && - (stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) { + } else { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if(stream) { + if(INT32_MAX - frame->window_update.window_size_increment < + stream->window_size) { int r; - r = nghttp2_pq_push(&session->ob_pq, stream->deferred_data); - if(r == 0) { - nghttp2_stream_detach_deferred_data(stream); - } else if(r < 0) { - /* FATAL */ - assert(r < NGHTTP2_ERR_FATAL); - return r; - } - } - nghttp2_session_call_on_ctrl_frame_received(session, - NGHTTP2_WINDOW_UPDATE, frame); - } - } - return 0; -} - -int nghttp2_session_on_credential_received(nghttp2_session *session, - nghttp2_frame *frame) -{ - if(!nghttp2_session_check_version(session, frame->credential.hd.version)) { - return 0; - } - /* We don't care about the body of the CREDENTIAL frame. It is left - to the application code to decide it is invalid or not. */ - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_CREDENTIAL, - frame); - return 0; -} - -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame) -{ - int r = 0; - int valid = 0; - nghttp2_stream *stream; - if(!nghttp2_session_check_version(session, frame->headers.hd.version)) { - return 0; - } - if((stream = nghttp2_session_get_stream(session, - frame->headers.stream_id)) && - (stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { - if(nghttp2_session_is_my_stream_id(session, frame->headers.stream_id)) { - if(stream->state == NGHTTP2_STREAM_OPENED) { - valid = 1; - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_HEADERS, - frame); - if(frame->headers.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - nghttp2_session_close_stream_if_shut_rdwr(session, stream); - } - } else if(stream->state == NGHTTP2_STREAM_CLOSING) { - /* This is race condition. NGHTTP2_STREAM_CLOSING indicates - that we queued RST_STREAM but it has not been sent. It will - eventually sent, so we just ignore this frame. */ - valid = 1; - } - } else { - /* If this is remote peer initiated stream, it is OK unless it - have sent FIN frame already. But if stream is in - NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race - condition. */ - valid = 1; - if(stream->state != NGHTTP2_STREAM_CLOSING) { - nghttp2_session_call_on_ctrl_frame_received(session, NGHTTP2_HEADERS, - frame); - if(frame->headers.hd.flags & NGHTTP2_CTRL_FLAG_FIN) { - nghttp2_session_call_on_request_recv(session, - frame->headers.stream_id); - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - nghttp2_session_close_stream_if_shut_rdwr(session, stream); + r = nghttp2_session_handle_invalid_stream + (session, frame->hd.stream_id, frame, NGHTTP2_FLOW_CONTROL_ERROR); + return r; + } else { + stream->window_size += frame->window_update.window_size_increment; + if(stream->window_size > 0 && + stream->deferred_data != NULL && + (stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) { + int r; + r = nghttp2_pq_push(&session->ob_pq, stream->deferred_data); + if(r == 0) { + nghttp2_stream_detach_deferred_data(stream); + } else if(r < 0) { + /* FATAL */ + assert(r < NGHTTP2_ERR_FATAL); + return r; + } } + nghttp2_session_call_on_frame_received(session, frame); } } } - if(!valid) { - r = nghttp2_session_handle_invalid_stream - (session, frame->headers.stream_id, NGHTTP2_HEADERS, frame, - NGHTTP2_PROTOCOL_ERROR); - } - return r; + return 0; } static void nghttp2_session_handle_parse_error(nghttp2_session *session, nghttp2_frame_type type, - int error_code) + int lib_error_code) { - if(session->callbacks.on_ctrl_recv_parse_error_callback) { - session->callbacks.on_ctrl_recv_parse_error_callback + if(session->callbacks.on_frame_recv_parse_error_callback) { + session->callbacks.on_frame_recv_parse_error_callback (session, type, session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, session->iframe.buflen, - error_code, + lib_error_code, session->user_data); } } -static int nghttp2_get_status_code_from_error_code(int error_code) +static int nghttp2_get_status_code_from_error_code(int lib_error_code) { - switch(error_code) { + switch(lib_error_code) { case(NGHTTP2_ERR_FRAME_TOO_LARGE): return NGHTTP2_FRAME_TOO_LARGE; default: @@ -2130,138 +1751,8 @@ static int nghttp2_session_process_ctrl_frame(nghttp2_session *session) int r = 0; uint16_t type; nghttp2_frame frame; - type = nghttp2_get_uint16(&session->iframe.headbuf[2]); + type = session->iframe.headbuf[2]; switch(type) { - case NGHTTP2_SYN_STREAM: - if(session->iframe.error_code == 0) { - r = nghttp2_frame_unpack_syn_stream(&frame.syn_stream, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen, - &session->iframe.inflatebuf); - } else if(session->iframe.error_code == NGHTTP2_ERR_FRAME_TOO_LARGE) { - r = nghttp2_frame_unpack_syn_stream_without_nv - (&frame.syn_stream, - session->iframe.headbuf, sizeof(session->iframe.headbuf), - session->iframe.buf, session->iframe.buflen); - if(r == 0) { - r = session->iframe.error_code; - } - } else { - r = session->iframe.error_code; - } - if(r == 0) { - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame.syn_stream.nv); - } - r = nghttp2_session_on_syn_stream_received(session, &frame); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - } else if(r == NGHTTP2_ERR_INVALID_HEADER_BLOCK || - r == NGHTTP2_ERR_FRAME_TOO_LARGE) { - r = nghttp2_session_handle_invalid_stream - (session, frame.syn_stream.stream_id, NGHTTP2_SYN_STREAM, &frame, - nghttp2_get_status_code_from_error_code(r)); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_SYN_REPLY: - if(session->iframe.error_code == 0) { - r = nghttp2_frame_unpack_syn_reply(&frame.syn_reply, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen, - &session->iframe.inflatebuf); - } else if(session->iframe.error_code == NGHTTP2_ERR_FRAME_TOO_LARGE) { - r = nghttp2_frame_unpack_syn_reply_without_nv - (&frame.syn_reply, - session->iframe.headbuf, sizeof(session->iframe.headbuf), - session->iframe.buf, session->iframe.buflen); - if(r == 0) { - r = session->iframe.error_code; - } - } else { - r = session->iframe.error_code; - } - if(r == 0) { - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame.syn_reply.nv); - } - r = nghttp2_session_on_syn_reply_received(session, &frame); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - } else if(r == NGHTTP2_ERR_INVALID_HEADER_BLOCK || - r == NGHTTP2_ERR_FRAME_TOO_LARGE) { - r = nghttp2_session_handle_invalid_stream - (session, frame.syn_reply.stream_id, NGHTTP2_SYN_REPLY, &frame, - nghttp2_get_status_code_from_error_code(r)); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_RST_STREAM: - r = nghttp2_frame_unpack_rst_stream(&frame.rst_stream, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen); - if(r == 0) { - r = nghttp2_session_on_rst_stream_received(session, &frame); - nghttp2_frame_rst_stream_free(&frame.rst_stream); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_SETTINGS: - r = nghttp2_frame_unpack_settings(&frame.settings, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen); - if(r == 0) { - r = nghttp2_session_on_settings_received(session, &frame); - nghttp2_frame_settings_free(&frame.settings); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_NOOP: - break; - case NGHTTP2_PING: - r = nghttp2_frame_unpack_ping(&frame.ping, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen); - if(r == 0) { - r = nghttp2_session_on_ping_received(session, &frame); - nghttp2_frame_ping_free(&frame.ping); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_GOAWAY: - r = nghttp2_frame_unpack_goaway(&frame.goaway, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen); - if(r == 0) { - r = nghttp2_session_on_goaway_received(session, &frame); - nghttp2_frame_goaway_free(&frame.goaway); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; case NGHTTP2_HEADERS: if(session->iframe.error_code == 0) { r = nghttp2_frame_unpack_headers(&frame.headers, @@ -2282,20 +1773,91 @@ static int nghttp2_session_process_ctrl_frame(nghttp2_session *session) r = session->iframe.error_code; } if(r == 0) { - if(session->version == NGHTTP2_PROTO_SPDY2) { - nghttp2_frame_nv_2to3(frame.headers.nv); + if(nghttp2_session_is_my_stream_id(session, frame.hd.stream_id)) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame.hd.stream_id); + if(stream) { + if(stream->state == NGHTTP2_STREAM_OPENING) { + frame.headers.cat = NGHTTP2_HCAT_REPLY; + r = nghttp2_session_on_syn_reply_received(session, &frame, stream); + } else { + frame.headers.cat = NGHTTP2_HCAT_HEADERS; + r = nghttp2_session_on_headers_received(session, &frame, stream); + } + } else { + r = nghttp2_session_handle_invalid_stream + (session, frame.hd.stream_id, &frame, NGHTTP2_PROTOCOL_ERROR); + } + } else { + frame.headers.cat = NGHTTP2_HCAT_START_STREAM; + r = nghttp2_session_on_syn_stream_received(session, &frame); } - r = nghttp2_session_on_headers_received(session, &frame); nghttp2_frame_headers_free(&frame.headers); } else if(r == NGHTTP2_ERR_INVALID_HEADER_BLOCK || r == NGHTTP2_ERR_FRAME_TOO_LARGE) { r = nghttp2_session_handle_invalid_stream - (session, frame.headers.stream_id, NGHTTP2_HEADERS, &frame, + (session, frame.hd.stream_id, &frame, nghttp2_get_status_code_from_error_code(r)); nghttp2_frame_headers_free(&frame.headers); } else if(nghttp2_is_non_fatal(r)) { nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); + } + break; + case NGHTTP2_RST_STREAM: + r = nghttp2_frame_unpack_rst_stream(&frame.rst_stream, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen); + if(r == 0) { + r = nghttp2_session_on_rst_stream_received(session, &frame); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + } else if(nghttp2_is_non_fatal(r)) { + nghttp2_session_handle_parse_error(session, type, r); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); + } + break; + case NGHTTP2_SETTINGS: + r = nghttp2_frame_unpack_settings(&frame.settings, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen); + if(r == 0) { + r = nghttp2_session_on_settings_received(session, &frame); + nghttp2_frame_settings_free(&frame.settings); + } else if(nghttp2_is_non_fatal(r)) { + nghttp2_session_handle_parse_error(session, type, r); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); + } + break; + case NGHTTP2_PING: + r = nghttp2_frame_unpack_ping(&frame.ping, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen); + if(r == 0) { + r = nghttp2_session_on_ping_received(session, &frame); + nghttp2_frame_ping_free(&frame.ping); + } else if(nghttp2_is_non_fatal(r)) { + nghttp2_session_handle_parse_error(session, type, r); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); + } + break; + case NGHTTP2_GOAWAY: + r = nghttp2_frame_unpack_goaway(&frame.goaway, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen); + if(r == 0) { + r = nghttp2_session_on_goaway_received(session, &frame); + nghttp2_frame_goaway_free(&frame.goaway); + } else if(nghttp2_is_non_fatal(r)) { + nghttp2_session_handle_parse_error(session, type, r); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } break; case NGHTTP2_WINDOW_UPDATE: @@ -2309,27 +1871,13 @@ static int nghttp2_session_process_ctrl_frame(nghttp2_session *session) nghttp2_frame_window_update_free(&frame.window_update); } else if(nghttp2_is_non_fatal(r)) { nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); - } - break; - case NGHTTP2_CREDENTIAL: - r = nghttp2_frame_unpack_credential(&frame.credential, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.buflen); - if(r == 0) { - r = nghttp2_session_on_credential_received(session, &frame); - nghttp2_frame_credential_free(&frame.credential); - } else if(nghttp2_is_non_fatal(r)) { - nghttp2_session_handle_parse_error(session, type, r); - r = nghttp2_session_fail_session(session, NGHTTP2_GOAWAY_PROTOCOL_ERROR); + r = nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } break; default: /* Unknown frame */ - if(session->callbacks.on_unknown_ctrl_recv_callback) { - session->callbacks.on_unknown_ctrl_recv_callback + if(session->callbacks.on_unknown_frame_recv_callback) { + session->callbacks.on_unknown_frame_recv_callback (session, session->iframe.headbuf, sizeof(session->iframe.headbuf), @@ -2346,12 +1894,18 @@ static int nghttp2_session_process_ctrl_frame(nghttp2_session *session) } int nghttp2_session_on_data_received(nghttp2_session *session, - uint8_t flags, int32_t length, + uint16_t length, uint8_t flags, int32_t stream_id) { int r = 0; - nghttp2_status_code status_code = 0; + nghttp2_error_code error_code = 0; nghttp2_stream *stream; + if(stream_id == 0) { + /* The spec says that if a DATA frame is received whose stream ID + is 0, the recipient MUST respond with a connection error of + type PROTOCOL_ERROR. */ + return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); + } stream = nghttp2_session_get_stream(session, stream_id); if(stream) { if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { @@ -2364,7 +1918,7 @@ int nghttp2_session_on_data_received(nghttp2_session *session, (session, flags, stream_id, length, session->user_data); } } else if(stream->state != NGHTTP2_STREAM_CLOSING) { - status_code = NGHTTP2_PROTOCOL_ERROR; + error_code = NGHTTP2_PROTOCOL_ERROR; } } else if(stream->state != NGHTTP2_STREAM_CLOSING) { /* It is OK if this is remote peer initiated stream and we did @@ -2375,24 +1929,30 @@ int nghttp2_session_on_data_received(nghttp2_session *session, session->callbacks.on_data_recv_callback (session, flags, stream_id, length, session->user_data); } - if(flags & NGHTTP2_DATA_FLAG_FIN) { + if(flags & NGHTTP2_FLAG_END_STREAM) { nghttp2_session_call_on_request_recv(session, stream_id); } } if(valid) { - if(flags & NGHTTP2_DATA_FLAG_FIN) { + if(flags & NGHTTP2_FLAG_END_STREAM) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); nghttp2_session_close_stream_if_shut_rdwr(session, stream); } } } else { - status_code = NGHTTP2_PROTOCOL_ERROR; + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + error_code = NGHTTP2_STREAM_CLOSED; } } else { - status_code = NGHTTP2_INVALID_STREAM; + error_code = NGHTTP2_PROTOCOL_ERROR; } - if(status_code != 0) { - r = nghttp2_session_add_rst_stream(session, stream_id, status_code); + if(error_code != 0) { + r = nghttp2_session_add_rst_stream(session, stream_id, error_code); } return r; } @@ -2400,16 +1960,11 @@ int nghttp2_session_on_data_received(nghttp2_session *session, /* For errors, this function only returns FATAL error. */ static int nghttp2_session_process_data_frame(nghttp2_session *session) { - uint8_t flags; - int32_t length; - int32_t stream_id; int r; - stream_id = nghttp2_get_uint32(session->iframe.headbuf) & - NGHTTP2_STREAM_ID_MASK; - flags = session->iframe.headbuf[4]; - length = nghttp2_get_uint32(&session->iframe.headbuf[4]) & - NGHTTP2_LENGTH_MASK; - r = nghttp2_session_on_data_received(session, flags, length, stream_id); + nghttp2_frame_hd hd; + nghttp2_frame_unpack_frame_hd(&hd, session->iframe.headbuf); + r = nghttp2_session_on_data_received(session, hd.length, hd.flags, + hd.stream_id); if(nghttp2_is_fatal(r)) { return r; } else { @@ -2429,11 +1984,9 @@ static int nghttp2_session_process_data_frame(nghttp2_session *session) * Out of memory. */ static int nghttp2_session_update_recv_window_size(nghttp2_session *session, - int32_t stream_id, + nghttp2_stream *stream, int32_t delta_size) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); if(stream) { /* If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set and the application does not send WINDOW_UPDATE and the remote endpoint keeps @@ -2451,7 +2004,9 @@ static int nghttp2_session_update_recv_window_size(nghttp2_session *session, if((size_t)stream->recv_window_size*2 >= session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]) { int r; - r = nghttp2_session_add_window_update(session, stream_id, + r = nghttp2_session_add_window_update(session, + NGHTTP2_FLAG_NONE, + stream->stream_id, stream->recv_window_size); if(r == 0) { stream->recv_window_size = 0; @@ -2505,18 +2060,17 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, if(bufavail == 0) { break; } - remheadbytes = NGHTTP2_HEAD_LEN-session->iframe.headbufoff; + remheadbytes = NGHTTP2_FRAME_HEAD_LENGTH - session->iframe.headbufoff; readlen = nghttp2_min(remheadbytes, bufavail); memcpy(session->iframe.headbuf+session->iframe.headbufoff, inmark, readlen); inmark += readlen; session->iframe.headbufoff += readlen; - if(session->iframe.headbufoff == NGHTTP2_HEAD_LEN) { + if(session->iframe.headbufoff == NGHTTP2_FRAME_HEAD_LENGTH) { session->iframe.state = NGHTTP2_RECV_PAYLOAD; session->iframe.payloadlen = - nghttp2_get_uint32(&session->iframe.headbuf[4]) & - NGHTTP2_LENGTH_MASK; - if(nghttp2_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + nghttp2_get_uint16(&session->iframe.headbuf[0]); + if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) { /* control frame */ ssize_t buflen; buflen = nghttp2_inbound_frame_payload_nv_offset(&session->iframe); @@ -2555,7 +2109,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, /* Check stream is open. If it is not open or closing, ignore payload. */ int32_t stream_id; - stream_id = nghttp2_get_uint32(session->iframe.headbuf); + stream_id = nghttp2_get_uint32(&session->iframe.headbuf[4]) & + NGHTTP2_STREAM_ID_MASK; if(!nghttp2_session_check_data_recv_allowed(session, stream_id)) { session->iframe.state = NGHTTP2_RECV_PAYLOAD_IGN; } @@ -2571,7 +2126,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, size_t rempayloadlen; size_t bufavail, readlen; int32_t data_stream_id = 0; - uint8_t data_flags = NGHTTP2_DATA_FLAG_NONE; + uint8_t data_flags = NGHTTP2_FLAG_NONE; rempayloadlen = session->iframe.payloadlen - session->iframe.off; bufavail = inlimit - inmark; @@ -2621,16 +2176,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, session->iframe.error_code = NGHTTP2_ERR_FRAME_TOO_LARGE; } } - } else if(nghttp2_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + } else if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) { if(session->iframe.state != NGHTTP2_RECV_PAYLOAD_IGN) { memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); } } else { /* For data frame, We don't buffer data. Instead, just pass received data to callback function. */ - data_stream_id = nghttp2_get_uint32(session->iframe.headbuf) & + data_stream_id = nghttp2_get_uint32(&session->iframe.headbuf[4]) & NGHTTP2_STREAM_ID_MASK; - data_flags = session->iframe.headbuf[4]; + data_flags = session->iframe.headbuf[3]; if(session->iframe.state != NGHTTP2_RECV_PAYLOAD_IGN) { if(session->callbacks.on_data_chunk_recv_callback) { session->callbacks.on_data_chunk_recv_callback(session, @@ -2645,14 +2200,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, session->iframe.off += readlen; inmark += readlen; - if(session->flow_control && - session->iframe.state != NGHTTP2_RECV_PAYLOAD_IGN && - !nghttp2_frame_is_ctrl_frame(session->iframe.headbuf[0])) { - if(readlen > 0 && - (session->iframe.payloadlen != session->iframe.off || - (data_flags & NGHTTP2_DATA_FLAG_FIN) == 0)) { + if(session->iframe.state != NGHTTP2_RECV_PAYLOAD_IGN && + nghttp2_frame_is_data_frame(session->iframe.headbuf) && + readlen > 0 && + (session->iframe.payloadlen != session->iframe.off || + (data_flags & NGHTTP2_FLAG_END_STREAM) == 0)) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, data_stream_id); + if(stream->local_flow_control) { r = nghttp2_session_update_recv_window_size(session, - data_stream_id, + stream, readlen); if(r < 0) { /* FATAL */ @@ -2662,7 +2219,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, } } if(session->iframe.payloadlen == session->iframe.off) { - if(nghttp2_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) { r = nghttp2_session_process_ctrl_frame(session); } else { r = nghttp2_session_process_data_frame(session); @@ -2736,7 +2293,8 @@ int nghttp2_session_want_write(nghttp2_session *session) (!session->goaway_flags || nghttp2_map_size(&session->streams) > 0); } -int nghttp2_session_add_ping(nghttp2_session *session, uint32_t unique_id) +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + uint8_t *opaque_data) { int r; nghttp2_frame *frame; @@ -2744,8 +2302,8 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint32_t unique_id) if(frame == NULL) { return NGHTTP2_ERR_NOMEM; } - nghttp2_frame_ping_init(&frame->ping, session->version, unique_id); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); + nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); if(r != 0) { nghttp2_frame_ping_free(&frame->ping); free(frame); @@ -2754,18 +2312,31 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint32_t unique_id) } int nghttp2_session_add_goaway(nghttp2_session *session, - int32_t last_good_stream_id, - uint32_t status_code) + int32_t last_stream_id, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len) { int r; nghttp2_frame *frame; + uint8_t *opaque_data_copy = NULL; + if(opaque_data_len) { + if(opaque_data_len > UINT16_MAX - 8) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + opaque_data_copy = malloc(opaque_data_len); + if(opaque_data_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(opaque_data_copy, opaque_data, opaque_data_len); + } frame = malloc(sizeof(nghttp2_frame)); if(frame == NULL) { + free(opaque_data_copy); return NGHTTP2_ERR_NOMEM; } - nghttp2_frame_goaway_init(&frame->goaway, session->version, - last_good_stream_id, status_code); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); + nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, + opaque_data_copy, opaque_data_len); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); if(r != 0) { nghttp2_frame_goaway_free(&frame->goaway); free(frame); @@ -2773,9 +2344,9 @@ int nghttp2_session_add_goaway(nghttp2_session *session, return r; } -int nghttp2_session_add_window_update(nghttp2_session *session, +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, - int32_t delta_window_size) + int32_t window_size_increment) { int r; nghttp2_frame *frame; @@ -2783,9 +2354,9 @@ int nghttp2_session_add_window_update(nghttp2_session *session, if(frame == NULL) { return NGHTTP2_ERR_NOMEM; } - nghttp2_frame_window_update_init(&frame->window_update, session->version, - stream_id, delta_window_size); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); + nghttp2_frame_window_update_init(&frame->window_update, flags, + stream_id, window_size_increment); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); if(r != 0) { nghttp2_frame_window_update_free(&frame->window_update); free(frame); @@ -2807,7 +2378,7 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, } eof_flags = 0; r = frame->data_prd.read_callback - (session, frame->stream_id, (*buf_ptr)+8, datamax, + (session, frame->hd.stream_id, (*buf_ptr)+8, datamax, &eof_flags, &frame->data_prd.source, session->user_data); if(r == NGHTTP2_ERR_DEFERRED || r == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { return r; @@ -2815,35 +2386,20 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, /* This is the error code when callback is failed. */ return NGHTTP2_ERR_CALLBACK_FAILURE; } - memset(*buf_ptr, 0, NGHTTP2_HEAD_LEN); - nghttp2_put_uint32be(&(*buf_ptr)[0], frame->stream_id); - nghttp2_put_uint32be(&(*buf_ptr)[4], r); + memset(*buf_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); + nghttp2_put_uint16be(&(*buf_ptr)[0], r); flags = 0; if(eof_flags) { frame->eof = 1; - if(frame->flags & NGHTTP2_DATA_FLAG_FIN) { - flags |= NGHTTP2_DATA_FLAG_FIN; + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + flags |= NGHTTP2_FLAG_END_STREAM; } } - (*buf_ptr)[4] = flags; + (*buf_ptr)[3] = flags; + nghttp2_put_uint32be(&(*buf_ptr)[4], frame->hd.stream_id); return r+8; } -uint32_t nghttp2_session_get_next_unique_id(nghttp2_session *session) -{ - uint32_t ret_id; - if(session->next_unique_id > NGHTTP2_MAX_UNIQUE_ID) { - if(session->server) { - session->next_unique_id = 2; - } else { - session->next_unique_id = 1; - } - } - ret_id = session->next_unique_id; - session->next_unique_id += 2; - return ret_id; -} - void* nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) { @@ -2872,58 +2428,11 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) return r; } -uint8_t nghttp2_session_get_pri_lowest(nghttp2_session *session) -{ - if(session->version == NGHTTP2_PROTO_SPDY2) { - return NGHTTP2_PRI_LOWEST_SPDY2; - } else if(session->version == NGHTTP2_PROTO_SPDY3) { - return NGHTTP2_PRI_LOWEST_SPDY3; - } else { - return 0; - } -} - size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { return nghttp2_pq_size(&session->ob_pq)+nghttp2_pq_size(&session->ob_ss_pq); } -int nghttp2_session_set_initial_client_cert_origin(nghttp2_session *session, - const char *scheme, - const char *host, - uint16_t port) -{ - nghttp2_origin *origin; - size_t slot; - if(strlen(scheme) > NGHTTP2_MAX_SCHEME || - strlen(host) > NGHTTP2_MAX_HOSTNAME) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - if(session->server || - (session->cli_certvec.size == 0 || - session->cli_certvec.last_slot != 0)) { - return NGHTTP2_ERR_INVALID_STATE; - } - origin = malloc(sizeof(nghttp2_origin)); - if(origin == NULL) { - return NGHTTP2_ERR_NOMEM; - } - strcpy(origin->scheme, scheme); - strcpy(origin->host, host); - origin->port = port; - slot = nghttp2_client_cert_vector_put(&session->cli_certvec, origin); - assert(slot == 1); - return 0; -} - -const nghttp2_origin* nghttp2_session_get_client_cert_origin -(nghttp2_session *session, - size_t slot) -{ - return nghttp2_client_cert_vector_get_origin(&session->cli_certvec, slot); -} - - int nghttp2_session_set_option(nghttp2_session *session, int optname, void *optval, size_t optlen) { diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index f0e2ab2e..788750a0 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -37,18 +37,6 @@ #include "nghttp2_stream.h" #include "nghttp2_buffer.h" #include "nghttp2_outbound_item.h" -#include "nghttp2_client_cert_vector.h" - -/** - * @macro - * Lowest priority value in SPDY/2, which is 3. - */ -#define NGHTTP2_PRI_LOWEST_SPDY2 3 -/** - * @macro - * Lowest priority value in SPDY/3, which is 7. - */ -#define NGHTTP2_PRI_LOWEST_SPDY3 7 /* * Option flags. @@ -81,11 +69,7 @@ typedef struct { #define NGHTTP2_INITIAL_NV_BUFFER_LENGTH 4096 #define NGHTTP2_INITIAL_WINDOW_SIZE 65536 - -/* Initial size of client certificate vector */ -#define NGHTTP2_INITIAL_CLIENT_CERT_VECTOR_LENGTH 8 -/* Maxmum size of client certificate vector */ -#define NGHTTP2_MAX_CLIENT_CERT_VECTOR_LENGTH 255 +#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE 65536 /* Internal state when receiving incoming frame */ typedef enum { @@ -96,22 +80,16 @@ typedef enum { /* Receiving frame payload, but the received bytes are discarded. */ NGHTTP2_RECV_PAYLOAD_IGN, /* Receiving frame payload that comes before name/value header - block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */ + block. Applied only for HEADERS and PUSH_PROMISE. */ NGHTTP2_RECV_PAYLOAD_PRE_NV, /* Receiving name/value header block in frame payload. Applied only - for SYN_STREAM, SYN_REPLY and HEADERS. */ + for HEADERS and PUSH_PROMISE. */ NGHTTP2_RECV_PAYLOAD_NV } nghttp2_inbound_state; -#define NGHTTP2_HEAD_LEN 8 - -/* Maximum unique ID in use for PING. If unique ID exeeds this number, - it wraps to 1 (client) or 2 (server) */ -#define NGHTTP2_MAX_UNIQUE_ID ((1u << 31)-1) - typedef struct { nghttp2_inbound_state state; - uint8_t headbuf[NGHTTP2_HEAD_LEN]; + uint8_t headbuf[NGHTTP2_FRAME_HEAD_LENGTH]; /* How many bytes are filled in headbuf */ size_t headbufoff; /* Payload for control frames. It is not used for DATA frames */ @@ -170,9 +148,9 @@ struct nghttp2_session { local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]. */ size_t num_incoming_streams; - /* Queue for outbound frames other than SYN_STREAM */ + /* Queue for outbound frames other than stream-creating HEADERS */ nghttp2_pq /* */ ob_pq; - /* Queue for outbound SYN_STREAM frame */ + /* Queue for outbound stream-creating HEADERS frame */ nghttp2_pq /* */ ob_ss_pq; nghttp2_active_outbound_item aob; @@ -188,18 +166,26 @@ struct nghttp2_session { nghttp2_zlib hd_deflater; nghttp2_zlib hd_inflater; - /* The last unique ID sent to the peer. */ - uint32_t last_ping_unique_id; - /* Flags indicating GOAWAY is sent and/or recieved. The flags are composed by bitwise OR-ing nghttp2_goaway_flag. */ uint8_t goaway_flags; /* This is the value in GOAWAY frame sent by remote endpoint. */ - int32_t last_good_stream_id; + int32_t last_stream_id; - /* Flag to indicate whether this session enforces flow - control. Nonzero for flow control enabled. */ - uint8_t flow_control; + /* Non-zero indicates connection-level flow control on remote side + is in effect. This will be disabled when WINDOW_UPDATE with + END_FLOW_CONTROL bit set is received. */ + uint8_t remote_flow_control; + /* Non-zero indicates connection-level flow control on local side is + in effect. This will be disabled when WINDOW_UPDATE with + END_FLOW_CONTROL bit set is sent. */ + uint8_t local_flow_control; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. */ + int32_t recv_window_size; /* Settings value received from the remote endpoint. We just use ID as index. The index = 0 is unused. */ @@ -212,9 +198,6 @@ struct nghttp2_session { /* Maxmum size of buffer to use when receving control frame. */ uint32_t max_recv_ctrl_frame_buf; - /* Client certificate vector */ - nghttp2_client_cert_vector cli_certvec; - nghttp2_session_callbacks callbacks; void *user_data; }; @@ -256,8 +239,8 @@ int nghttp2_session_add_frame(nghttp2_session *session, void *abs_frame, void *aux_data); /* - * Adds RST_STREAM frame for the stream |stream_id| with status code - * |status_code|. This is a convenient function built on top of + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of * nghttp2_session_add_frame() to add RST_STREAM easily. * * This function returns 0 if it succeeds, or one of the following @@ -267,12 +250,16 @@ int nghttp2_session_add_frame(nghttp2_session *session, * Out of memory. */ int nghttp2_session_add_rst_stream(nghttp2_session *session, - int32_t stream_id, uint32_t status_code); + int32_t stream_id, + nghttp2_error_code error_code); /* - * Adds PING frame with unique ID |unique_id|. This is a convenient - * functin built on top of nghttp2_session_add_frame() to add PING - * easily. + * Adds PING frame. This is a convenient functin built on top of + * nghttp2_session_add_frame() to add PING easily. + * + * If the |opaque_data| is not NULL, it must point to 8 bytes memory + * region of data. The data pointed by |opaque_data| is copied. It can + * be NULL. In this case, 8 bytes NULL is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -280,14 +267,13 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_session_add_ping(nghttp2_session *session, uint32_t unique_id); +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + uint8_t *opaque_data); /* - * Adds GOAWAY frame with last-good-stream-ID |last_good_stream_id| - * and the status code |status_code|. The |status_code| is ignored if - * the protocol version is NGHTTP2_PROTO_SPDY2. This is a convenient - * function built on top of nghttp2_session_add_frame() to add GOAWAY - * easily. + * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the + * error code |error_code|. This is a convenient function built on top + * of nghttp2_session_add_frame() to add GOAWAY easily. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -296,12 +282,13 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint32_t unique_id); * Out of memory. */ int nghttp2_session_add_goaway(nghttp2_session *session, - int32_t last_good_stream_id, - uint32_t status_code); + int32_t last_stream_id, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len); /* * Adds WINDOW_UPDATE frame with stream ID |stream_id| and - * delta-window-size |delta_window_size|. This is a convenient + * window-size-increment |window_size_increment|. This is a convenient * function built on top of nghttp2_session_add_frame() to add * WINDOW_UPDATE easily. * @@ -311,34 +298,32 @@ int nghttp2_session_add_goaway(nghttp2_session *session, * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_session_add_window_update(nghttp2_session *session, +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, - int32_t delta_window_size); + int32_t window_size_increment); /* * Creates new stream in |session| with stream ID |stream_id|, - * priority |pri| and flags |flags|. NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL - * flag is set in |flags|, this stream is - * unidirectional. NGHTTP2_CTRL_FLAG_FIN flag is set in |flags|, the - * sender of SYN_STREAM will not send any further data in this - * stream. Since this function is called when SYN_STREAM is sent or - * received, these flags are taken from SYN_STREAM. The state of - * stream is set to |initial_state|. |stream_user_data| is a pointer - * to the arbitrary user supplied data to be associated to this - * stream. + * priority |pri| and flags |flags|. NGHTTP2_FLAG_END_STREAM flag is + * set in |flags|, the sender of HEADERS will not send any further + * data in this stream. Since this function is called when initial + * HEADERS is sent or received, these flags are taken from it. The + * state of stream is set to |initial_state|. The |stream_user_data| + * is a pointer to the arbitrary user supplied data to be associated + * to this stream. * * This function returns a pointer to created new stream object, or * NULL. */ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session, int32_t stream_id, - uint8_t flags, uint8_t pri, + uint8_t flags, int32_t pri, nghttp2_stream_state initial_state, void *stream_user_data); /* * Closes stream whose stream ID is |stream_id|. The reason of closure - * is indicated by |status_code|. When closing the stream, + * is indicated by the |error_code|. When closing the stream, * on_stream_close_callback will be called. * * This function returns 0 if it succeeds, or one the following @@ -348,19 +333,11 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session, * The specified stream does not exist. */ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - nghttp2_status_code status_code); - -/* - * Closes all pushed streams which associate them to stream - * |stream_id| with the status code |status_code|. - */ -void nghttp2_session_close_pushed_streams(nghttp2_session *session, - int32_t stream_id, - nghttp2_status_code status_code); + nghttp2_error_code error_code); /* * If further receptions and transmissions over the stream |stream_id| - * are disallowed, close the stream with status code |status_code|. + * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. * * This function returns 0 if it * succeeds, or one of the following negative error codes: @@ -371,24 +348,18 @@ void nghttp2_session_close_pushed_streams(nghttp2_session *session, int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, nghttp2_stream *stream); -/* - * Called when SYN_STREAM is received, assuming |frame.syn_stream| is - * properly initialized. This function does first validate received - * frame and then open stream and call callback functions. This - * function does not return error if frame is not valid. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ + int nghttp2_session_on_syn_stream_received(nghttp2_session *session, nghttp2_frame *frame); +int nghttp2_session_on_syn_reply_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + /* - * Called when SYN_REPLY is received, assuming |frame.syn_reply| is - * properly initialized. + * Called when HEADERS is received, assuming |frame| is properly + * initialized. This function does first validate received frame and + * then open stream and call callback functions. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -396,13 +367,13 @@ int nghttp2_session_on_syn_stream_received(nghttp2_session *session, * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_session_on_syn_reply_received(nghttp2_session *session, - nghttp2_frame *frame); - +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); /* - * Called when RST_STREAM is received, assuming |frame.rst_stream| is - * properly initialized. + * Called when RST_STREAM is received, assuming |frame| is properly + * initialized. * * This function returns 0 and never fail. */ @@ -410,8 +381,8 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session, nghttp2_frame *frame); /* - * Called when SETTINGS is received, assuming |frame.settings| is - * properly initialized. + * Called when SETTINGS is received, assuming |frame| is properly + * initialized. * * This function returns 0 and never fail. */ @@ -419,7 +390,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, nghttp2_frame *frame); /* - * Called when PING is received, assuming |frame.ping| is properly + * Called when PING is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following @@ -432,7 +403,7 @@ int nghttp2_session_on_ping_received(nghttp2_session *session, nghttp2_frame *frame); /* - * Called when GOAWAY is received, assuming |frame.goaway| is properly + * Called when GOAWAY is received, assuming |frame| is properly * initialized. * * This function returns 0 and never fail. @@ -441,21 +412,8 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session, nghttp2_frame *frame); /* - * Called when HEADERS is recieved, assuming |frame.headers| is - * properly initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when WINDOW_UPDATE is recieved, assuming - * |frame.window_update| is properly initialized. + * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly + * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -466,15 +424,6 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, int nghttp2_session_on_window_update_received(nghttp2_session *session, nghttp2_frame *frame); -/* - * Called when CREDENTIAL is received, assuming |frame.credential| is - * properly initialized. - * - * Currently, this function always succeeds and returns 0. - */ -int nghttp2_session_on_credential_received(nghttp2_session *session, - nghttp2_frame *frame); - /* * Called when DATA is received. * @@ -485,7 +434,7 @@ int nghttp2_session_on_credential_received(nghttp2_session *session, * Out of memory. */ int nghttp2_session_on_data_received(nghttp2_session *session, - uint8_t flags, int32_t length, + uint16_t length, uint8_t flags, int32_t stream_id); /* @@ -520,11 +469,6 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, size_t datamax, nghttp2_data *frame); -/* - * Returns next unique ID which can be used with PING. - */ -uint32_t nghttp2_session_get_next_unique_id(nghttp2_session *session); - /* * Returns top of outbound frame queue. This function returns NULL if * queue is empty. @@ -561,21 +505,4 @@ void nghttp2_session_update_local_settings(nghttp2_session *session, nghttp2_settings_entry *iv, size_t niv); -/* - * Returns the index in the client certificate vector for the - * |syn_stream|. The origin is computed from |syn_stream->nv|. If no - * client certificate is required, return 0. If CREDENTIAL frame needs - * to be sent before the |syn_stream|, this function returns - * :macro:`NGHTTP2_ERR_CREDENTIAL_PENDING`. In this case, CREDENTIAL - * frame has been already queued. This function returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CREDENTIAL_PENDING - * The CREDENTIAL frame must be sent before the |syn_stream|. - */ -int nghttp2_session_prep_credential(nghttp2_session *session, - nghttp2_syn_stream *syn_stream); - #endif /* NGHTTP2_SESSION_H */ diff --git a/lib/nghttp2_stream.c b/lib/nghttp2_stream.c index 95d6d523..6a0af20d 100644 --- a/lib/nghttp2_stream.c +++ b/lib/nghttp2_stream.c @@ -27,8 +27,10 @@ #include void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, uint8_t pri, + uint8_t flags, int32_t pri, nghttp2_stream_state initial_state, + uint8_t remote_flow_control, + uint8_t local_flow_control, int32_t initial_window_size, void *stream_user_data) { @@ -44,6 +46,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, stream->stream_user_data = stream_user_data; stream->deferred_data = NULL; stream->deferred_flags = NGHTTP2_DEFERRED_NONE; + stream->remote_flow_control = remote_flow_control; + stream->local_flow_control = local_flow_control; stream->window_size = initial_window_size; stream->recv_window_size = 0; } diff --git a/lib/nghttp2_stream.h b/lib/nghttp2_stream.h index 9a6be542..dd5d34ce 100644 --- a/lib/nghttp2_stream.h +++ b/lib/nghttp2_stream.h @@ -84,8 +84,8 @@ typedef struct { nghttp2_stream_state state; /* Use same value in SYN_STREAM frame */ uint8_t flags; - /* Use same scheme in SYN_STREAM frame */ - uint8_t pri; + /* Use same value in SYN_STREAM frame */ + int32_t pri; /* Bitwise OR of zero or more nghttp2_shut_flag values */ uint8_t shut_flags; /* The array of server-pushed stream IDs which associate them to @@ -103,6 +103,18 @@ typedef struct { /* The flags for defered DATA. Bitwise OR of zero or more nghttp2_deferred_flag values */ uint8_t deferred_flags; + /* Flag to indicate whether the remote side has flow control + enabled. If it is enabled, we have to enforces flow control to + send data to the other side. This could be disabled when + receiving SETTINGS with flow control options off or receiving + WINDOW_UPDATE with END_FLOW_CONTROL bit set. */ + uint8_t remote_flow_control; + /* Flag to indicate whether the local side has flow control + enabled. If it is enabled, the received data are subject to the + flow control. This could be disabled by sending SETTINGS with + flow control options off or sending WINDOW_UPDATE with + END_FLOW_CONTROL bit set. */ + uint8_t local_flow_control; /* Current sender window size. This value is computed against the current initial window size of remote endpoint. */ int32_t window_size; @@ -112,8 +124,10 @@ typedef struct { } nghttp2_stream; void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, uint8_t pri, + uint8_t flags, int32_t pri, nghttp2_stream_state initial_state, + uint8_t remote_flow_control, + uint8_t local_flow_control, int32_t initial_window_size, void *stream_user_data); diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 46b31cfc..730128b9 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -30,11 +30,11 @@ #include "nghttp2_frame.h" #include "nghttp2_helper.h" -static int nghttp2_submit_syn_stream_shared +static int nghttp2_submit_headers_shared (nghttp2_session *session, uint8_t flags, - int32_t assoc_stream_id, - uint8_t pri, + int32_t stream_id, + int32_t pri, const char **nv, const nghttp2_data_provider *data_prd, void *stream_user_data) @@ -44,13 +44,10 @@ static int nghttp2_submit_syn_stream_shared char **nv_copy; uint8_t flags_copy; nghttp2_data_provider *data_prd_copy = NULL; - nghttp2_syn_stream_aux_data *aux_data; - if(pri > nghttp2_session_get_pri_lowest(session)) { + nghttp2_headers_aux_data *aux_data = NULL; + if(pri < 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } - if(assoc_stream_id != 0 && session->server == 0) { - assoc_stream_id = 0; - } if(!nghttp2_frame_nv_check_null(nv)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } @@ -61,14 +58,15 @@ static int nghttp2_submit_syn_stream_shared } *data_prd_copy = *data_prd; } - aux_data = malloc(sizeof(nghttp2_syn_stream_aux_data)); - if(aux_data == NULL) { - free(data_prd_copy); - return NGHTTP2_ERR_NOMEM; + if(data_prd || stream_user_data) { + aux_data = malloc(sizeof(nghttp2_headers_aux_data)); + if(aux_data == NULL) { + free(data_prd_copy); + return NGHTTP2_ERR_NOMEM; + } + aux_data->data_prd = data_prd_copy; + aux_data->stream_user_data = stream_user_data; } - aux_data->data_prd = data_prd_copy; - aux_data->stream_user_data = stream_user_data; - frame = malloc(sizeof(nghttp2_frame)); if(frame == NULL) { free(aux_data); @@ -82,20 +80,16 @@ static int nghttp2_submit_syn_stream_shared free(data_prd_copy); return NGHTTP2_ERR_NOMEM; } - flags_copy = 0; - if(flags & NGHTTP2_CTRL_FLAG_FIN) { - flags_copy |= NGHTTP2_CTRL_FLAG_FIN; - } - if(flags & NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL) { - flags_copy |= NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL; - } - nghttp2_frame_syn_stream_init(&frame->syn_stream, - session->version, flags_copy, - 0, assoc_stream_id, pri, nv_copy); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, + /* TODO Implement header continuation */ + flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | + NGHTTP2_FLAG_END_HEADERS; + + nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri, + nv_copy); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data); if(r != 0) { - nghttp2_frame_syn_stream_free(&frame->syn_stream); + nghttp2_frame_headers_free(&frame->headers); free(frame); free(aux_data); free(data_prd_copy); @@ -103,99 +97,35 @@ static int nghttp2_submit_syn_stream_shared return r; } -int nghttp2_submit_syn_stream(nghttp2_session *session, uint8_t flags, - int32_t assoc_stream_id, uint8_t pri, - const char **nv, void *stream_user_data) -{ - return nghttp2_submit_syn_stream_shared(session, flags, assoc_stream_id, - pri, nv, NULL, stream_user_data); -} - -int nghttp2_submit_syn_reply(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const char **nv) -{ - int r; - nghttp2_frame *frame; - char **nv_copy; - uint8_t flags_copy; - if(!nghttp2_frame_nv_check_null(nv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - frame = malloc(sizeof(nghttp2_frame)); - if(frame == NULL) { - return NGHTTP2_ERR_NOMEM; - } - nv_copy = nghttp2_frame_nv_norm_copy(nv); - if(nv_copy == NULL) { - free(frame); - return NGHTTP2_ERR_NOMEM; - } - flags_copy = 0; - if(flags & NGHTTP2_CTRL_FLAG_FIN) { - flags_copy |= NGHTTP2_CTRL_FLAG_FIN; - } - nghttp2_frame_syn_reply_init(&frame->syn_reply, session->version, flags_copy, - stream_id, nv_copy); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); - if(r != 0) { - nghttp2_frame_syn_reply_free(&frame->syn_reply); - free(frame); - } - return r; -} - int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const char **nv) + int32_t stream_id, int32_t pri, + const char **nv, void *stream_user_data) { - int r; - nghttp2_frame *frame; - char **nv_copy; - uint8_t flags_copy; - if(!nghttp2_frame_nv_check_null(nv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - frame = malloc(sizeof(nghttp2_frame)); - if(frame == NULL) { - return NGHTTP2_ERR_NOMEM; - } - nv_copy = nghttp2_frame_nv_norm_copy(nv); - if(nv_copy == NULL) { - free(frame); - return NGHTTP2_ERR_NOMEM; - } - flags_copy = 0; - if(flags & NGHTTP2_CTRL_FLAG_FIN) { - flags_copy |= NGHTTP2_CTRL_FLAG_FIN; - } - nghttp2_frame_headers_init(&frame->headers, session->version, flags_copy, - stream_id, nv_copy); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); - if(r != 0) { - nghttp2_frame_headers_free(&frame->headers); - free(frame); - } - return r; + return nghttp2_submit_headers_shared(session, flags, stream_id, + pri, nv, NULL, stream_user_data); } -int nghttp2_submit_ping(nghttp2_session *session) + +int nghttp2_submit_ping(nghttp2_session *session, uint8_t *opaque_data) { - return nghttp2_session_add_ping(session, - nghttp2_session_get_next_unique_id(session)); + return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data); } int nghttp2_submit_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t status_code) + nghttp2_error_code error_code) { - return nghttp2_session_add_rst_stream(session, stream_id, status_code); + return nghttp2_session_add_rst_stream(session, stream_id, error_code); } -int nghttp2_submit_goaway(nghttp2_session *session, uint32_t status_code) +int nghttp2_submit_goaway(nghttp2_session *session, + nghttp2_error_code error_code, + uint8_t *opaque_data, size_t opaque_data_len) { return nghttp2_session_add_goaway(session, session->last_recv_stream_id, - status_code); + error_code, opaque_data, opaque_data_len); } -int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, +int nghttp2_submit_settings(nghttp2_session *session, const nghttp2_settings_entry *iv, size_t niv) { nghttp2_frame *frame; @@ -222,9 +152,8 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, return NGHTTP2_ERR_NOMEM; } nghttp2_frame_iv_sort(iv_copy, niv); - nghttp2_frame_settings_init(&frame->settings, session->version, - flags, iv_copy, niv); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); + nghttp2_frame_settings_init(&frame->settings, iv_copy, niv); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); if(r == 0) { nghttp2_session_update_local_settings(session, iv_copy, niv); } else { @@ -234,85 +163,61 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, return r; } -int nghttp2_submit_window_update(nghttp2_session *session, int32_t stream_id, - int32_t delta_window_size) +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { nghttp2_stream *stream; - if(delta_window_size <= 0) { + if(window_size_increment <= 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } - stream = nghttp2_session_get_stream(session, stream_id); - if(stream) { - stream->recv_window_size -= nghttp2_min(delta_window_size, - stream->recv_window_size); - return nghttp2_session_add_window_update(session, stream_id, - delta_window_size); + if(stream_id == 0) { + return nghttp2_session_add_window_update(session, flags, stream_id, + window_size_increment); } else { - return NGHTTP2_ERR_STREAM_CLOSED; + stream = nghttp2_session_get_stream(session, stream_id); + if(stream) { + stream->recv_window_size -= nghttp2_min(window_size_increment, + stream->recv_window_size); + return nghttp2_session_add_window_update(session, flags, stream_id, + window_size_increment); + } else { + return NGHTTP2_ERR_STREAM_CLOSED; + } } } -int nghttp2_submit_request(nghttp2_session *session, uint8_t pri, +int nghttp2_submit_request(nghttp2_session *session, int32_t pri, const char **nv, const nghttp2_data_provider *data_prd, void *stream_user_data) { - int flags; - flags = 0; + uint8_t flags = NGHTTP2_FLAG_NONE; if(data_prd == NULL || data_prd->read_callback == NULL) { - flags |= NGHTTP2_CTRL_FLAG_FIN; + flags |= NGHTTP2_FLAG_END_STREAM; } - return nghttp2_submit_syn_stream_shared(session, flags, 0, pri, nv, data_prd, - stream_user_data); + if(pri != NGHTTP2_PRI_DEFAULT) { + flags |= NGHTTP2_FLAG_PRIORITY; + } + return nghttp2_submit_headers_shared(session, flags, -1, pri, nv, + data_prd, stream_user_data); } int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const char **nv, const nghttp2_data_provider *data_prd) { - int r; - nghttp2_frame *frame; - char **nv_copy; - uint8_t flags = 0; - nghttp2_data_provider *data_prd_copy = NULL; - if(!nghttp2_frame_nv_check_null(nv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; + uint8_t flags = NGHTTP2_FLAG_NONE; + if(data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; } - if(data_prd != NULL && data_prd->read_callback != NULL) { - data_prd_copy = malloc(sizeof(nghttp2_data_provider)); - if(data_prd_copy == NULL) { - return NGHTTP2_ERR_NOMEM; - } - *data_prd_copy = *data_prd; - } - frame = malloc(sizeof(nghttp2_frame)); - if(frame == NULL) { - free(data_prd_copy); - return NGHTTP2_ERR_NOMEM; - } - nv_copy = nghttp2_frame_nv_norm_copy(nv); - if(nv_copy == NULL) { - free(frame); - free(data_prd_copy); - return NGHTTP2_ERR_NOMEM; - } - if(data_prd_copy == NULL) { - flags |= NGHTTP2_CTRL_FLAG_FIN; - } - nghttp2_frame_syn_reply_init(&frame->syn_reply, session->version, flags, - stream_id, nv_copy); - r = nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, - data_prd_copy); - if(r != 0) { - nghttp2_frame_syn_reply_free(&frame->syn_reply); - free(frame); - free(data_prd_copy); - } - return r; + return nghttp2_submit_headers_shared(session, flags, stream_id, + NGHTTP2_PRI_DEFAULT, nv, data_prd, + NULL); } -int nghttp2_submit_data(nghttp2_session *session, int32_t stream_id, - uint8_t flags, +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_data_provider *data_prd) { int r; @@ -322,11 +227,11 @@ int nghttp2_submit_data(nghttp2_session *session, int32_t stream_id, if(data_frame == NULL) { return NGHTTP2_ERR_NOMEM; } - if(flags & NGHTTP2_DATA_FLAG_FIN) { - nflags |= NGHTTP2_DATA_FLAG_FIN; + if(flags & NGHTTP2_FLAG_END_STREAM) { + nflags |= NGHTTP2_FLAG_END_STREAM; } - nghttp2_frame_data_init(data_frame, stream_id, nflags, data_prd); - r = nghttp2_session_add_frame(session, NGHTTP2_DATA, data_frame, NULL); + nghttp2_frame_data_init(data_frame, nflags, stream_id, data_prd); + r = nghttp2_session_add_frame(session, NGHTTP2_CAT_DATA, data_frame, NULL); if(r != 0) { nghttp2_frame_data_free(data_frame); free(data_frame); diff --git a/lib/nghttp2_zlib.c b/lib/nghttp2_zlib.c index 7029f9fa..3fac3a08 100644 --- a/lib/nghttp2_zlib.c +++ b/lib/nghttp2_zlib.c @@ -230,13 +230,8 @@ static const uint8_t* nghttp2_select_hd_dict(size_t *len_ptr, uint16_t version) { const uint8_t *hd_dict; hd_dict = NULL; - if(version == NGHTTP2_PROTO_SPDY2) { - hd_dict = (const uint8_t*)spdy2_hd_dict; - *len_ptr = sizeof(spdy2_hd_dict); - } else if(version == NGHTTP2_PROTO_SPDY3) { - hd_dict = spdy3_hd_dict; - *len_ptr = sizeof(spdy3_hd_dict); - } + hd_dict = (const uint8_t*)spdy2_hd_dict; + *len_ptr = sizeof(spdy2_hd_dict); return hd_dict; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 93ff1046..46ce6734 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,35 +24,39 @@ SUBDIRS = testdata if HAVE_CUNIT -check_PROGRAMS = main failmalloc +check_PROGRAMS = main +# failmalloc OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ - nghttp2_buffer_test.c nghttp2_zlib_test.c nghttp2_session_test.c \ - nghttp2_frame_test.c nghttp2_stream_test.c nghttp2_npn_test.c \ - nghttp2_client_cert_vector_test.c nghttp2_gzip_test.c \ - nghttp2_test_helper.c + nghttp2_buffer_test.c nghttp2_zlib_test.c \ + nghttp2_test_helper.c \ + nghttp2_frame_test.c \ + nghttp2_stream_test.c \ + nghttp2_session_test.c \ + nghttp2_npn_test.c \ + nghttp2_gzip_test.c HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ nghttp2_buffer_test.h nghttp2_zlib_test.h nghttp2_session_test.h \ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_npn_test.h \ - nghttp2_client_cert_vector_test.h nghttp2_gzip_test.h \ - nghttp2_test_helper.h + nghttp2_gzip_test.h nghttp2_test_helper.h main_SOURCES = $(HFILES) $(OBJECTS) main_LDADD = ${top_builddir}/lib/libnghttp2.la main_LDFLAGS = -static @CUNIT_LIBS@ @TESTS_LIBS@ -failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ - malloc_wrapper.c malloc_wrapper.h \ - nghttp2_test_helper.c nghttp2_test_helper.h -failmalloc_LDADD = $(main_LDADD) -failmalloc_LDFLAGS = $(main_LDFLAGS) +# failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ +# malloc_wrapper.c malloc_wrapper.h \ +# nghttp2_test_helper.c nghttp2_test_helper.h +# failmalloc_LDADD = $(main_LDADD) +# failmalloc_LDFLAGS = $(main_LDFLAGS) AM_CFLAGS = -Wall -I${top_srcdir}/lib -I${top_srcdir}/lib/includes -I${top_builddir}/lib/includes \ @CUNIT_CFLAGS@ @DEFS@ -TESTS = main failmalloc +TESTS = main +# failmalloc if ENABLE_SRC diff --git a/tests/main.c b/tests/main.c index 522ef024..e54bc387 100644 --- a/tests/main.c +++ b/tests/main.c @@ -35,7 +35,6 @@ #include "nghttp2_frame_test.h" #include "nghttp2_stream_test.h" #include "nghttp2_npn_test.h" -#include "nghttp2_client_cert_vector_test.h" #include "nghttp2_gzip_test.h" static int init_suite1(void) @@ -73,170 +72,133 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "queue", test_nghttp2_queue) || !CU_add_test(pSuite, "buffer", test_nghttp2_buffer) || !CU_add_test(pSuite, "buffer_reader", test_nghttp2_buffer_reader) || - !CU_add_test(pSuite, "zlib_spdy2", test_nghttp2_zlib_spdy2) || - !CU_add_test(pSuite, "zlib_spdy3", test_nghttp2_zlib_spdy3) || + !CU_add_test(pSuite, "zlib", test_nghttp2_zlib) || !CU_add_test(pSuite, "npn", test_nghttp2_npn) || - !CU_add_test(pSuite, "npn_get_proto_list", - test_nghttp2_npn_get_proto_list) || !CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) || !CU_add_test(pSuite, "session_recv_invalid_stream_id", test_nghttp2_session_recv_invalid_stream_id) || - !CU_add_test(pSuite, "session_add_frame", - test_nghttp2_session_add_frame) || - !CU_add_test(pSuite, "session_on_syn_stream_received", - test_nghttp2_session_on_syn_stream_received) || - !CU_add_test(pSuite, "session_on_syn_stream_received_with_push", - test_nghttp2_session_on_syn_stream_received_with_push) || - !CU_add_test(pSuite, "session_on_syn_reply_received", - test_nghttp2_session_on_syn_reply_received) || - !CU_add_test(pSuite, "session_send_syn_stream", - test_nghttp2_session_send_syn_stream) || - !CU_add_test(pSuite, "session_send_syn_reply", - test_nghttp2_session_send_syn_reply) || - !CU_add_test(pSuite, "submit_response", test_nghttp2_submit_response) || - !CU_add_test(pSuite, "submit_response_without_data", - test_nghttp2_submit_response_with_null_data_read_callback) || - !CU_add_test(pSuite, "submit_request_with_data", - test_nghttp2_submit_request_with_data) || - !CU_add_test(pSuite, "submit_request_without_data", - test_nghttp2_submit_request_with_null_data_read_callback) || - !CU_add_test(pSuite, "submit_syn_stream", - test_nghttp2_submit_syn_stream) || - !CU_add_test(pSuite, "submit_syn_reply", test_nghttp2_submit_syn_reply) || - !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || - !CU_add_test(pSuite, "submit_invalid_nv", - test_nghttp2_submit_invalid_nv) || - !CU_add_test(pSuite, "session_reply_fail", - test_nghttp2_session_reply_fail) || - !CU_add_test(pSuite, "session_on_headers_received", - test_nghttp2_session_on_headers_received) || - !CU_add_test(pSuite, "session_on_window_update_received", - test_nghttp2_session_on_window_update_received) || - !CU_add_test(pSuite, "session_on_ping_received", - test_nghttp2_session_on_ping_received) || - !CU_add_test(pSuite, "session_on_goaway_received", - test_nghttp2_session_on_goaway_received) || - !CU_add_test(pSuite, "session_on_data_received", - test_nghttp2_session_on_data_received) || - !CU_add_test(pSuite, "session_on_rst_stream_received", - test_nghttp2_session_on_rst_received) || - !CU_add_test(pSuite, "session_is_my_stream_id", - test_nghttp2_session_is_my_stream_id) || - !CU_add_test(pSuite, "session_send_rst_stream", - test_nghttp2_session_send_rst_stream) || - !CU_add_test(pSuite, "session_get_next_ob_item", - test_nghttp2_session_get_next_ob_item) || - !CU_add_test(pSuite, "session_pop_next_ob_item", - test_nghttp2_session_pop_next_ob_item) || - !CU_add_test(pSuite, "session_on_request_recv_callback", - test_nghttp2_session_on_request_recv_callback) || - !CU_add_test(pSuite, "session_on_stream_close", - test_nghttp2_session_on_stream_close) || - !CU_add_test(pSuite, "session_max_concurrent_streams", - test_nghttp2_session_max_concurrent_streams) || - !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", - test_nghttp2_session_data_backoff_by_high_pri_frame) || - !CU_add_test(pSuite, "session_stop_data_with_rst_stream", - test_nghttp2_session_stop_data_with_rst_stream) || - !CU_add_test(pSuite, "session_stream_close_on_syn_stream", - test_nghttp2_session_stream_close_on_syn_stream) || !CU_add_test(pSuite, "session_recv_invalid_frame", test_nghttp2_session_recv_invalid_frame) || - !CU_add_test(pSuite, "session_defer_data", - test_nghttp2_session_defer_data) || - !CU_add_test(pSuite, "session_flow_control", - test_nghttp2_session_flow_control) || - !CU_add_test(pSuite, "session_on_ctrl_not_send", - test_nghttp2_session_on_ctrl_not_send) || - !CU_add_test(pSuite, "session_on_settings_received", - test_nghttp2_session_on_settings_received) || - !CU_add_test(pSuite, "session_submit_settings", - test_nghttp2_submit_settings) || - !CU_add_test(pSuite, "session_get_outbound_queue_size", - test_nghttp2_session_get_outbound_queue_size) || - !CU_add_test(pSuite, "session_prep_credential", - test_nghttp2_session_prep_credential) || - !CU_add_test(pSuite, "session_submit_syn_stream_with_credential", - test_nghttp2_submit_syn_stream_with_credential) || - !CU_add_test(pSuite, "session_set_initial_client_cert_origin", - test_nghttp2_session_set_initial_client_cert_origin) || - !CU_add_test(pSuite, "session_set_option", - test_nghttp2_session_set_option) || - !CU_add_test(pSuite, "submit_window_update", - test_nghttp2_submit_window_update) || - !CU_add_test(pSuite, "session_data_read_temporal_failure", - test_nghttp2_session_data_read_temporal_failure) || !CU_add_test(pSuite, "session_recv_eof", test_nghttp2_session_recv_eof) || !CU_add_test(pSuite, "session_recv_data", test_nghttp2_session_recv_data) || - !CU_add_test(pSuite, "frame_unpack_nv_spdy2", - test_nghttp2_frame_unpack_nv_spdy2) || - !CU_add_test(pSuite, "frame_unpack_nv_spdy3", - test_nghttp2_frame_unpack_nv_spdy3) || + !CU_add_test(pSuite, "session_add_frame", + test_nghttp2_session_add_frame) || + !CU_add_test(pSuite, "session_on_syn_stream_received", + test_nghttp2_session_on_syn_stream_received) || + !CU_add_test(pSuite, "session_on_syn_reply_received", + test_nghttp2_session_on_syn_reply_received) || + !CU_add_test(pSuite, "session_on_headers_received", + test_nghttp2_session_on_headers_received) || + !CU_add_test(pSuite, "session_on_rst_stream_received", + test_nghttp2_session_on_rst_stream_received) || + !CU_add_test(pSuite, "session_on_settings_received", + test_nghttp2_session_on_settings_received) || + !CU_add_test(pSuite, "session_on_ping_received", + test_nghttp2_session_on_ping_received) || + !CU_add_test(pSuite, "session_on_goaway_received", + test_nghttp2_session_on_goaway_received) || + !CU_add_test(pSuite, "session_on_window_update_received", + test_nghttp2_session_on_window_update_received) || + !CU_add_test(pSuite, "session_on_data_received", + test_nghttp2_session_on_data_received) || + !CU_add_test(pSuite, "session_send_headers_start_stream", + test_nghttp2_session_send_headers_start_stream) || + !CU_add_test(pSuite, "session_send_headers_reply", + test_nghttp2_session_send_headers_reply) || + !CU_add_test(pSuite, "session_send_rst_stream", + test_nghttp2_session_send_rst_stream) || + !CU_add_test(pSuite, "session_is_my_stream_id", + test_nghttp2_session_is_my_stream_id) || + !CU_add_test(pSuite, "submit_response", test_nghttp2_submit_response) || + !CU_add_test(pSuite, "submit_response_without_data", + test_nghttp2_submit_response_without_data) || + !CU_add_test(pSuite, "submit_request_with_data", + test_nghttp2_submit_request_with_data) || + !CU_add_test(pSuite, "submit_request_without_data", + test_nghttp2_submit_request_without_data) || + !CU_add_test(pSuite, "submit_headers_start_stream", + test_nghttp2_submit_headers_start_stream) || + !CU_add_test(pSuite, "submit_headers_reply", + test_nghttp2_submit_headers_reply) || + !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || + !CU_add_test(pSuite, "session_submit_settings", + test_nghttp2_submit_settings) || + !CU_add_test(pSuite, "submit_window_update", + test_nghttp2_submit_window_update) || + !CU_add_test(pSuite, "submit_invalid_nv", + test_nghttp2_submit_invalid_nv) || + !CU_add_test(pSuite, "session_get_next_ob_item", + test_nghttp2_session_get_next_ob_item) || + !CU_add_test(pSuite, "session_pop_next_ob_item", + test_nghttp2_session_pop_next_ob_item) || + !CU_add_test(pSuite, "session_reply_fail", + test_nghttp2_session_reply_fail) || + !CU_add_test(pSuite, "session_max_concurrent_streams", + test_nghttp2_session_max_concurrent_streams) || + !CU_add_test(pSuite, "session_stream_close_on_headers_push", + test_nghttp2_session_stream_close_on_headers_push) || + !CU_add_test(pSuite, "session_stop_data_with_rst_stream", + test_nghttp2_session_stop_data_with_rst_stream) || + !CU_add_test(pSuite, "session_defer_data", + test_nghttp2_session_defer_data) || + !CU_add_test(pSuite, "session_flow_control", + test_nghttp2_session_flow_control) || + !CU_add_test(pSuite, "session_data_read_temporal_failure", + test_nghttp2_session_data_read_temporal_failure) || + !CU_add_test(pSuite, "session_on_request_recv_callback", + test_nghttp2_session_on_request_recv_callback) || + !CU_add_test(pSuite, "session_on_stream_close", + test_nghttp2_session_on_stream_close) || + !CU_add_test(pSuite, "session_on_ctrl_not_send", + test_nghttp2_session_on_ctrl_not_send) || + !CU_add_test(pSuite, "session_get_outbound_queue_size", + test_nghttp2_session_get_outbound_queue_size) || + !CU_add_test(pSuite, "session_set_option", + test_nghttp2_session_set_option) || + !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", + test_nghttp2_session_data_backoff_by_high_pri_frame) || + !CU_add_test(pSuite, "frame_unpack_nv", + test_nghttp2_frame_unpack_nv) || + !CU_add_test(pSuite, "frame_unpack_nv_check_name", + test_nghttp2_frame_unpack_nv_check_name) || + !CU_add_test(pSuite, "frame_unpack_nv_last_empty_value", + test_nghttp2_frame_unpack_nv_last_empty_value) || + !CU_add_test(pSuite, "frame_pack_nv_duplicate_keys", + test_nghttp2_frame_pack_nv_duplicate_keys) || !CU_add_test(pSuite, "frame_count_nv_space", test_nghttp2_frame_count_nv_space) || + !CU_add_test(pSuite, "frame_pack_nv_empty_value", + test_nghttp2_frame_pack_nv_empty_value) || !CU_add_test(pSuite, "frame_count_unpack_nv_space", test_nghttp2_frame_count_unpack_nv_space) || - !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) || - !CU_add_test(pSuite, "frame_pack_goaway_spdy2", - test_nghttp2_frame_pack_goaway_spdy2) || - !CU_add_test(pSuite, "frame_pack_goaway_spdy3", - test_nghttp2_frame_pack_goaway_spdy3) || - !CU_add_test(pSuite, "frame_pack_syn_stream_spdy2", - test_nghttp2_frame_pack_syn_stream_spdy2) || - !CU_add_test(pSuite, "frame_pack_syn_stream_spdy3", - test_nghttp2_frame_pack_syn_stream_spdy3) || - !CU_add_test(pSuite, "frame_pack_syn_stream_frame_too_large", - test_nghttp2_frame_pack_syn_stream_frame_too_large) || - !CU_add_test(pSuite, "frame_pack_syn_reply_spdy2", - test_nghttp2_frame_pack_syn_reply_spdy2) || - !CU_add_test(pSuite, "frame_pack_syn_reply_spdy3", - test_nghttp2_frame_pack_syn_reply_spdy3) || - !CU_add_test(pSuite, "frame_pack_headers_spdy2", - test_nghttp2_frame_pack_headers_spdy2) || - !CU_add_test(pSuite, "frame_pack_headers_spdy3", - test_nghttp2_frame_pack_headers_spdy3) || - !CU_add_test(pSuite, "frame_pack_window_update", - test_nghttp2_frame_pack_window_update) || - !CU_add_test(pSuite, "frame_pack_settings_spdy2", - test_nghttp2_frame_pack_settings_spdy2) || - !CU_add_test(pSuite, "frame_pack_settings_spdy3", - test_nghttp2_frame_pack_settings_spdy3) || - !CU_add_test(pSuite, "frame_pack_credential", - test_nghttp2_frame_pack_credential) || !CU_add_test(pSuite, "frame_nv_sort", test_nghttp2_frame_nv_sort) || !CU_add_test(pSuite, "frame_nv_downcase", test_nghttp2_frame_nv_downcase) || - !CU_add_test(pSuite, "frame_pack_nv_duplicate_keys", - test_nghttp2_frame_pack_nv_duplicate_keys) || - !CU_add_test(pSuite, "frame_pack_nv_empty_value_spdy2", - test_nghttp2_frame_pack_nv_empty_value_spdy2) || - !CU_add_test(pSuite, "frame_pack_nv_empty_value_spdy3", - test_nghttp2_frame_pack_nv_empty_value_spdy3) || - !CU_add_test(pSuite, "frame_nv_2to3", test_nghttp2_frame_nv_2to3) || - !CU_add_test(pSuite, "frame_nv_3to2", test_nghttp2_frame_nv_3to2) || - !CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy2", - test_nghttp2_frame_unpack_nv_check_name_spdy2) || - !CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy3", - test_nghttp2_frame_unpack_nv_check_name_spdy3) || - !CU_add_test(pSuite, "frame_unpack_nv_last_empty_value_spdy2", - test_nghttp2_frame_unpack_nv_last_empty_value_spdy2) || - !CU_add_test(pSuite, "frame_unpack_nv_last_empty_value_spdy3", - test_nghttp2_frame_unpack_nv_last_empty_value_spdy3) || - !CU_add_test(pSuite, "frame_nv_set_origin", - test_nghttp2_frame_nv_set_origin) || !CU_add_test(pSuite, "frame_nv_check_null", test_nghttp2_frame_nv_check_null) || - !CU_add_test(pSuite, "stream_add_pushed_stream", - test_nghttp2_stream_add_pushed_stream) || - !CU_add_test(pSuite, "client_cert_vector_find", - test_nghttp2_client_cert_vector_find) || - !CU_add_test(pSuite, "client_cert_vector_resize", - test_nghttp2_client_cert_vector_resize) || - !CU_add_test(pSuite, "client_cert_vector_get_origin", - test_nghttp2_client_cert_vector_get_origin) || - !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate)) { + !CU_add_test(pSuite, "frame_pack_headers", + test_nghttp2_frame_pack_headers) || + !CU_add_test(pSuite, "frame_pack_headers_frame_too_large", + test_nghttp2_frame_pack_headers_frame_too_large) || + !CU_add_test(pSuite, "frame_pack_priority", + test_nghttp2_frame_pack_priority) || + !CU_add_test(pSuite, "frame_pack_rst_stream", + test_nghttp2_frame_pack_rst_stream) || + !CU_add_test(pSuite, "frame_pack_settings", + test_nghttp2_frame_pack_settings) || + !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) || + !CU_add_test(pSuite, "frame_pack_goaway", + test_nghttp2_frame_pack_goaway) || + !CU_add_test(pSuite, "frame_pack_window_update", + test_nghttp2_frame_pack_window_update) || + + /* !CU_add_test(pSuite, "stream_add_pushed_stream", */ + /* test_nghttp2_stream_add_pushed_stream) || */ + + !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) + ) { CU_cleanup_registry(); return CU_get_error(); } diff --git a/tests/nghttp2_client_cert_vector_test.c b/tests/nghttp2_client_cert_vector_test.c deleted file mode 100644 index e7a6f72d..00000000 --- a/tests/nghttp2_client_cert_vector_test.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * nghttp2 - HTTP/2.0 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_client_cert_vector_test.h" - -#include - -#include "nghttp2_client_cert_vector.h" - -static nghttp2_origin* create_origin(const char *scheme, const char *host, - uint16_t port) -{ - nghttp2_origin *origin = malloc(sizeof(nghttp2_origin)); - nghttp2_origin_set(origin, scheme, host, port); - return origin; -} - -void test_nghttp2_client_cert_vector_find(void) -{ - nghttp2_client_cert_vector certvec; - nghttp2_origin *origin; - const nghttp2_origin *origin_get; - size_t slot; - nghttp2_client_cert_vector_init(&certvec, 3); - - origin = create_origin("https", "example.org", 443); - CU_ASSERT(0 == nghttp2_client_cert_vector_find(&certvec, origin)); - CU_ASSERT(1 == nghttp2_client_cert_vector_put(&certvec, origin)); - slot = nghttp2_client_cert_vector_find(&certvec, origin); - CU_ASSERT(1 == slot); - origin_get = nghttp2_client_cert_vector_get_origin(&certvec, slot); - CU_ASSERT(strcmp(origin->scheme, origin_get->scheme) == 0); - CU_ASSERT(strcmp(origin->host, origin_get->host) == 0); - CU_ASSERT(origin->port == origin_get->port); - - origin = create_origin("https", "example.org", 8443); - CU_ASSERT(0 == nghttp2_client_cert_vector_find(&certvec, origin)); - CU_ASSERT(2 == nghttp2_client_cert_vector_put(&certvec, origin)); - slot = nghttp2_client_cert_vector_find(&certvec, origin); - CU_ASSERT(2 == slot); - - origin = create_origin("https", "example.com", 443); - CU_ASSERT(0 == nghttp2_client_cert_vector_find(&certvec, origin)); - CU_ASSERT(3 == nghttp2_client_cert_vector_put(&certvec, origin)); - slot = nghttp2_client_cert_vector_find(&certvec, origin); - CU_ASSERT(3 == slot); - - origin = create_origin("https", "example.com", 8443); - CU_ASSERT(0 == nghttp2_client_cert_vector_find(&certvec, origin)); - CU_ASSERT(1 == nghttp2_client_cert_vector_put(&certvec, origin)); - slot = nghttp2_client_cert_vector_find(&certvec, origin); - CU_ASSERT(1 == slot); - - origin = create_origin("https", "example.org", 443); - CU_ASSERT(0 == nghttp2_client_cert_vector_find(&certvec, origin)); - free(origin); - - nghttp2_client_cert_vector_free(&certvec); -} - -void test_nghttp2_client_cert_vector_resize(void) -{ - nghttp2_client_cert_vector certvec; - nghttp2_origin *origin; - size_t i; - nghttp2_client_cert_vector_init(&certvec, 3); - - origin = create_origin("https", "example.org", 443); - nghttp2_client_cert_vector_put(&certvec, origin); - origin = create_origin("https", "example.com", 443); - nghttp2_client_cert_vector_put(&certvec, origin); - - CU_ASSERT(0 == nghttp2_client_cert_vector_resize(&certvec, 1)); - CU_ASSERT(NULL != nghttp2_client_cert_vector_get_origin(&certvec, 1)); - CU_ASSERT(1 == certvec.last_slot); - - CU_ASSERT(0 == nghttp2_client_cert_vector_resize(&certvec, 8)); - CU_ASSERT(NULL != nghttp2_client_cert_vector_get_origin(&certvec, 1)); - CU_ASSERT(1 == certvec.last_slot); - for(i = 2; i <= 8; ++i) { - CU_ASSERT(NULL == nghttp2_client_cert_vector_get_origin(&certvec, i)); - } - - nghttp2_client_cert_vector_free(&certvec); -} - -void test_nghttp2_client_cert_vector_get_origin(void) -{ - nghttp2_client_cert_vector certvec; - nghttp2_origin *origin; - nghttp2_client_cert_vector_init(&certvec, 3); - - origin = create_origin("https", "example.org", 443); - CU_ASSERT(1 == nghttp2_client_cert_vector_put(&certvec, origin)); - - CU_ASSERT(NULL == nghttp2_client_cert_vector_get_origin(&certvec, 0)); - CU_ASSERT(NULL != nghttp2_client_cert_vector_get_origin(&certvec, 1)); - CU_ASSERT(NULL == nghttp2_client_cert_vector_get_origin(&certvec, 2)); - CU_ASSERT(NULL == nghttp2_client_cert_vector_get_origin(&certvec, 3)); - CU_ASSERT(NULL == nghttp2_client_cert_vector_get_origin(&certvec, 4)); - - nghttp2_client_cert_vector_free(&certvec); -} diff --git a/tests/nghttp2_client_cert_vector_test.h b/tests/nghttp2_client_cert_vector_test.h deleted file mode 100644 index 151dce99..00000000 --- a/tests/nghttp2_client_cert_vector_test.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2.0 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_CLIENT_CERT_VECTOR_TEST_H -#define NGHTTP2_CLIENT_CERT_VECTOR_TEST_H - -void test_nghttp2_client_cert_vector_find(void); -void test_nghttp2_client_cert_vector_resize(void); -void test_nghttp2_client_cert_vector_get_origin(void); - -#endif /* NGHTTP2_CLIENT_CERT_VECTOR_TEST_H */ diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index 076abc46..239b5c21 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -25,6 +25,7 @@ #include "nghttp2_frame_test.h" #include +#include #include @@ -32,20 +33,11 @@ #include "nghttp2_helper.h" #include "nghttp2_test_helper.h" -/* Reads |len_size| byte from |data| as |len_size| byte network byte - order integer, and returns it in host byte order. Currently, we - only support len_size == 2 or 4 */ +/* Reads |len_size| byte from |data| as 2 bytes network byte + order integer, and returns it in host byte order. */ static int get_packed_hd_len(uint8_t *data, size_t len_size) { - if(len_size == 2) { - return nghttp2_get_uint16(data); - } else if(len_size == 4) { - return nghttp2_get_uint32(data); - } else { - /* Not supported */ - assert(0); - return 0; - } + return nghttp2_get_uint16(data); } static const char *headers[] = { @@ -59,8 +51,9 @@ static const char *headers[] = { NULL }; -static void test_nghttp2_frame_unpack_nv_with(size_t len_size) +void test_nghttp2_frame_unpack_nv() { + size_t len_size = 2; uint8_t out[1024]; char **nv; size_t inlen = nghttp2_frame_pack_nv(out, (char**)headers, len_size); @@ -100,16 +93,112 @@ static void test_nghttp2_frame_unpack_nv_with(size_t len_size) nghttp2_buffer_free(&buffer); } -void test_nghttp2_frame_unpack_nv_spdy2(void) +/* This function intentionally does not merge same header field into + one */ +static size_t nghttp2_pack_nv(uint8_t *buf, size_t buflen, const char **nv, + size_t len_size) { - test_nghttp2_frame_unpack_nv_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY2)); + size_t i, n; + uint8_t *buf_ptr; + buf_ptr = buf; + for(n = 0; nv[n]; ++n); + nghttp2_frame_put_nv_len(buf_ptr, n/2); + buf_ptr += len_size; + for(i = 0; i < n; ++i) { + size_t len = strlen(nv[i]); + nghttp2_frame_put_nv_len(buf_ptr, len); + buf_ptr += len_size; + memcpy(buf_ptr, nv[i], len); + buf_ptr += len; + } + return buf_ptr-buf; } -void test_nghttp2_frame_unpack_nv_spdy3(void) +static const char *empty_name_headers[] = { + "method", "GET", + "", "https", + "url", "/", + NULL +}; + +static const char non_ascii_header_name[] = { (char)0xff }; + +static const char *non_ascii_headers[] = { + non_ascii_header_name, "foo", + NULL +}; + +void test_nghttp2_frame_unpack_nv_check_name() { - test_nghttp2_frame_unpack_nv_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY2)); + size_t len_size = 2; + uint8_t nvbuf[1024]; + size_t nvbuflen; + nghttp2_buffer buffer; + char **nv; + + nghttp2_buffer_init(&buffer, 32); + + nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), headers, len_size); + nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); + + CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == + nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); + + nghttp2_frame_nv_del(nv); + nghttp2_buffer_reset(&buffer); + + nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), empty_name_headers, + len_size); + nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); + + CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == + nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); + + nghttp2_frame_nv_del(nv); + nghttp2_buffer_reset(&buffer); + + nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), non_ascii_headers, + len_size); + nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); + CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == + nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); + + nghttp2_frame_nv_del(nv); + nghttp2_buffer_free(&buffer); +} + +void test_nghttp2_frame_unpack_nv_last_empty_value() +{ + size_t len_size = 2; + size_t nvbuflen; + uint8_t nvbuf[256]; + uint8_t *nvbufptr; + nghttp2_buffer buffer; + char **outnv = 0; + const char hdname[] = "method"; + + nvbufptr = nvbuf; + nghttp2_frame_put_nv_len(nvbufptr, 1); + nvbufptr += len_size; + nghttp2_frame_put_nv_len(nvbufptr, sizeof(hdname)-1); + nvbufptr += len_size; + memcpy(nvbufptr, hdname, sizeof(hdname)-1); + nvbufptr += sizeof(hdname)-1; + nghttp2_frame_put_nv_len(nvbufptr, 4); + nvbufptr += len_size; + /* Copy including terminating NULL */ + memcpy(nvbufptr, "GET", 4); + nvbufptr += 4; + nvbuflen = nvbufptr - nvbuf; + + nghttp2_buffer_init(&buffer, 32); + + nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); + CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == + nghttp2_frame_unpack_nv(&outnv, &buffer, len_size)); + + nghttp2_frame_nv_del(outnv); + nghttp2_buffer_free(&buffer); } void test_nghttp2_frame_pack_nv_duplicate_keys(void) @@ -256,8 +345,9 @@ static void frame_pack_nv_empty_value_check(uint8_t *outptr, } } -static void test_nghttp2_frame_pack_nv_empty_value_with(size_t len_size) +void test_nghttp2_frame_pack_nv_empty_value() { + size_t len_size = 2; uint8_t out[256]; char **nv; ssize_t rv; @@ -282,18 +372,6 @@ static void test_nghttp2_frame_pack_nv_empty_value_with(size_t len_size) nghttp2_frame_nv_del(nv); } -void test_nghttp2_frame_pack_nv_empty_value_spdy2(void) -{ - test_nghttp2_frame_pack_nv_empty_value_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY2)); -} - -void test_nghttp2_frame_pack_nv_empty_value_spdy3(void) -{ - test_nghttp2_frame_pack_nv_empty_value_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY3)); -} - void test_nghttp2_frame_count_unpack_nv_space(void) { size_t nvlen, buflen; @@ -343,389 +421,6 @@ void test_nghttp2_frame_count_unpack_nv_space(void) nghttp2_buffer_free(&buffer); } -void test_nghttp2_frame_pack_ping(void) -{ - nghttp2_frame frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - ssize_t framelen; - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_PROTO_SPDY2, 1); - framelen = nghttp2_frame_pack_ping(&buf, &buflen, &frame.ping); - CU_ASSERT(0 == nghttp2_frame_unpack_ping - (&oframe.ping, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - CU_ASSERT(1 == oframe.ping.unique_id); - free(buf); - nghttp2_frame_ping_free(&oframe.ping); - nghttp2_frame_ping_free(&frame.ping); -} - -static void test_nghttp2_frame_pack_goaway_version(uint16_t version) -{ - nghttp2_frame frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - ssize_t framelen; - nghttp2_frame_goaway_init(&frame.goaway, version, 1000000007, - NGHTTP2_GOAWAY_PROTOCOL_ERROR); - framelen = nghttp2_frame_pack_goaway(&buf, &buflen, &frame.goaway); - CU_ASSERT(0 == nghttp2_frame_unpack_goaway - (&oframe.goaway, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id); - if(version == NGHTTP2_PROTO_SPDY2) { - /* The status code is ignored in SPDY/2 */ - CU_ASSERT(0 == oframe.goaway.status_code); - } else if(version == NGHTTP2_PROTO_SPDY3) { - CU_ASSERT(NGHTTP2_GOAWAY_PROTOCOL_ERROR == oframe.goaway.status_code); - } - CU_ASSERT(version == oframe.goaway.hd.version); - CU_ASSERT(NGHTTP2_GOAWAY == oframe.goaway.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_NONE == oframe.goaway.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.goaway.hd.length); - free(buf); - nghttp2_frame_goaway_free(&oframe.goaway); - nghttp2_frame_goaway_free(&frame.goaway); -} - -void test_nghttp2_frame_pack_goaway_spdy2(void) -{ - test_nghttp2_frame_pack_goaway_version(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_frame_pack_goaway_spdy3(void) -{ - test_nghttp2_frame_pack_goaway_version(NGHTTP2_PROTO_SPDY3); -} - -static void test_nghttp2_frame_pack_syn_stream_version(uint16_t version) -{ - nghttp2_zlib deflater, inflater; - nghttp2_frame frame, oframe; - uint8_t *buf = NULL, *nvbuf = NULL; - size_t buflen = 0, nvbuflen = 0; - ssize_t framelen; - uint8_t pri; - if(version == NGHTTP2_PROTO_SPDY2) { - pri = 3; - } else { - pri = 7; - } - nghttp2_zlib_deflate_hd_init(&deflater, 1, version); - nghttp2_zlib_inflate_hd_init(&inflater, version); - nghttp2_frame_syn_stream_init(&frame.syn_stream, version, - NGHTTP2_CTRL_FLAG_FIN, 65536, 1000000007, pri, - nghttp2_frame_nv_copy(headers)); - framelen = nghttp2_frame_pack_syn_stream(&buf, &buflen, - &nvbuf, &nvbuflen, - &frame.syn_stream, &deflater); - - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_SYN_STREAM, - version, - &oframe, - &inflater, - buf, framelen)); - CU_ASSERT(65536 == oframe.syn_stream.stream_id); - CU_ASSERT(1000000007 == oframe.syn_stream.assoc_stream_id); - CU_ASSERT(version == oframe.syn_stream.hd.version); - CU_ASSERT(NGHTTP2_SYN_STREAM == oframe.syn_stream.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == oframe.syn_stream.hd.flags); - CU_ASSERT(pri == oframe.syn_stream.pri); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.syn_stream.hd.length); - CU_ASSERT(strcmp("method", oframe.syn_stream.nv[0]) == 0); - CU_ASSERT(strcmp("GET", oframe.syn_stream.nv[1]) == 0); - CU_ASSERT(NULL == oframe.syn_stream.nv[14]); - free(buf); - free(nvbuf); - nghttp2_frame_syn_stream_free(&oframe.syn_stream); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - nghttp2_zlib_inflate_free(&inflater); - nghttp2_zlib_deflate_free(&deflater); -} - -void test_nghttp2_frame_pack_syn_stream_spdy2(void) -{ - test_nghttp2_frame_pack_syn_stream_version(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_frame_pack_syn_stream_spdy3(void) -{ - test_nghttp2_frame_pack_syn_stream_version(NGHTTP2_PROTO_SPDY3); -} - -void test_nghttp2_frame_pack_syn_stream_frame_too_large(void) -{ - nghttp2_zlib deflater; - nghttp2_frame frame; - uint8_t *buf = NULL, *nvbuf = NULL; - size_t buflen = 0, nvbuflen = 0; - ssize_t framelen; - size_t big_vallen = 16777215; - char *big_val = malloc(big_vallen + 1); - const char *big_hds[] = { "header", big_val, NULL }; - memset(big_val, '0', big_vallen); - big_val[big_vallen] = '\0'; - /* No compression */ - nghttp2_zlib_deflate_hd_init(&deflater, 0, NGHTTP2_PROTO_SPDY3); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY3, - NGHTTP2_CTRL_FLAG_FIN, 65536, 1000000007, 3, - nghttp2_frame_nv_copy(big_hds)); - framelen = nghttp2_frame_pack_syn_stream(&buf, &buflen, - &nvbuf, &nvbuflen, - &frame.syn_stream, &deflater); - CU_ASSERT_EQUAL(NGHTTP2_ERR_FRAME_TOO_LARGE, framelen); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - free(buf); - free(nvbuf); - free(big_val); - nghttp2_zlib_deflate_free(&deflater); -} - -static void test_nghttp2_frame_pack_syn_reply_version(uint16_t version) -{ - nghttp2_zlib deflater, inflater; - nghttp2_frame frame, oframe; - uint8_t *buf = NULL, *nvbuf = NULL; - size_t buflen = 0, nvbuflen = 0; - ssize_t framelen; - nghttp2_zlib_deflate_hd_init(&deflater, 1, version); - nghttp2_zlib_inflate_hd_init(&inflater, version); - nghttp2_frame_syn_reply_init(&frame.syn_reply, version, - NGHTTP2_CTRL_FLAG_FIN, 3, - nghttp2_frame_nv_copy(headers)); - framelen = nghttp2_frame_pack_syn_reply(&buf, &buflen, - &nvbuf, &nvbuflen, - &frame.syn_reply, &deflater); - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_SYN_REPLY, - version, - &oframe, - &inflater, - buf, framelen)); - CU_ASSERT(3 == oframe.syn_reply.stream_id); - CU_ASSERT(version == oframe.syn_reply.hd.version); - CU_ASSERT(NGHTTP2_SYN_REPLY == oframe.syn_reply.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == oframe.syn_reply.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.syn_reply.hd.length); - CU_ASSERT(strcmp("method", oframe.syn_reply.nv[0]) == 0); - CU_ASSERT(strcmp("GET", oframe.syn_reply.nv[1]) == 0); - CU_ASSERT(NULL == oframe.syn_reply.nv[14]); - free(buf); - free(nvbuf); - nghttp2_frame_syn_reply_free(&oframe.syn_reply); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - nghttp2_zlib_inflate_free(&inflater); - nghttp2_zlib_deflate_free(&deflater); -} - -void test_nghttp2_frame_pack_syn_reply_spdy2(void) -{ - test_nghttp2_frame_pack_syn_reply_version(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_frame_pack_syn_reply_spdy3(void) -{ - test_nghttp2_frame_pack_syn_reply_version(NGHTTP2_PROTO_SPDY3); -} - -static void test_nghttp2_frame_pack_headers_version(uint16_t version) -{ - nghttp2_zlib deflater, inflater; - nghttp2_frame frame, oframe; - uint8_t *buf = NULL, *nvbuf = NULL; - size_t buflen = 0, nvbuflen = 0; - nghttp2_buffer inflatebuf; - ssize_t framelen; - nghttp2_buffer_init(&inflatebuf, 4096); - nghttp2_zlib_deflate_hd_init(&deflater, 1, version); - nghttp2_zlib_inflate_hd_init(&inflater, version); - nghttp2_frame_headers_init(&frame.headers, version, - NGHTTP2_CTRL_FLAG_FIN, 3, - nghttp2_frame_nv_copy(headers)); - framelen = nghttp2_frame_pack_headers(&buf, &buflen, - &nvbuf, &nvbuflen, - &frame.headers, &deflater); - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_HEADERS, - version, - &oframe, - &inflater, - buf, framelen)); - CU_ASSERT(3 == oframe.headers.stream_id); - CU_ASSERT(version == oframe.headers.hd.version); - CU_ASSERT(NGHTTP2_HEADERS == oframe.headers.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == oframe.headers.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.headers.hd.length); - CU_ASSERT(strcmp("method", oframe.headers.nv[0]) == 0); - CU_ASSERT(strcmp("GET", oframe.headers.nv[1]) == 0); - CU_ASSERT(NULL == oframe.headers.nv[14]); - free(buf); - free(nvbuf); - nghttp2_frame_headers_free(&oframe.headers); - nghttp2_frame_headers_free(&frame.headers); - nghttp2_zlib_inflate_free(&inflater); - nghttp2_zlib_deflate_free(&deflater); - nghttp2_buffer_free(&inflatebuf); -} - -void test_nghttp2_frame_pack_headers_spdy2(void) -{ - test_nghttp2_frame_pack_headers_version(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_frame_pack_headers_spdy3(void) -{ - test_nghttp2_frame_pack_headers_version(NGHTTP2_PROTO_SPDY3); -} - -void test_nghttp2_frame_pack_window_update(void) -{ - nghttp2_frame frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - ssize_t framelen; - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_PROTO_SPDY3, - 1000000007, 4096); - framelen = nghttp2_frame_pack_window_update(&buf, &buflen, - &frame.window_update); - CU_ASSERT(0 == nghttp2_frame_unpack_window_update - (&oframe.window_update, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - CU_ASSERT(1000000007 == oframe.window_update.stream_id); - CU_ASSERT(4096 == oframe.window_update.delta_window_size); - CU_ASSERT(NGHTTP2_PROTO_SPDY3 == oframe.window_update.hd.version); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == oframe.window_update.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_NONE == oframe.window_update.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == - oframe.window_update.hd.length); - free(buf); - nghttp2_frame_window_update_free(&oframe.window_update); - nghttp2_frame_window_update_free(&frame.window_update); -} - - -static void test_nghttp2_frame_pack_settings_version(uint16_t version) -{ - nghttp2_frame frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - ssize_t framelen; - int i; - nghttp2_settings_entry iv[3]; - iv[0].settings_id = NGHTTP2_SETTINGS_UPLOAD_BANDWIDTH; - iv[0].flags = NGHTTP2_ID_FLAG_SETTINGS_PERSIST_VALUE; - iv[0].value = 256; - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - iv[1].value = 100; - iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[2].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - iv[2].value = 65536; - - nghttp2_frame_settings_init - (&frame.settings, version, NGHTTP2_FLAG_SETTINGS_CLEAR_SETTINGS, - nghttp2_frame_iv_copy(iv, 3), 3); - framelen = nghttp2_frame_pack_settings(&buf, &buflen, &frame.settings); - CU_ASSERT(8+4+3*8 == framelen); - - CU_ASSERT(0 == nghttp2_frame_unpack_settings - (&oframe.settings, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - - CU_ASSERT(version == oframe.settings.hd.version); - CU_ASSERT(NGHTTP2_SETTINGS == oframe.settings.hd.type); - CU_ASSERT(NGHTTP2_FLAG_SETTINGS_CLEAR_SETTINGS == oframe.settings.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.settings.hd.length); - - CU_ASSERT(3 == oframe.settings.niv); - for(i = 0; i < 3; ++i) { - CU_ASSERT(iv[i].settings_id == oframe.settings.iv[i].settings_id); - CU_ASSERT(iv[i].flags == oframe.settings.iv[i].flags); - CU_ASSERT(iv[i].value == oframe.settings.iv[i].value); - } - - free(buf); - nghttp2_frame_settings_free(&frame.settings); - nghttp2_frame_settings_free(&oframe.settings); -} - -void test_nghttp2_frame_pack_settings_spdy2(void) -{ - test_nghttp2_frame_pack_settings_version(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_frame_pack_settings_spdy3(void) -{ - test_nghttp2_frame_pack_settings_version(NGHTTP2_PROTO_SPDY3); -} - -static char* strcopy(const char* s) -{ - size_t len = strlen(s); - char *dest = malloc(len+1); - memcpy(dest, s, len); - dest[len] = '\0'; - return dest; -} - -void test_nghttp2_frame_pack_credential(void) -{ - nghttp2_frame frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - ssize_t framelen; - nghttp2_mem_chunk proof; - nghttp2_mem_chunk *certs; - size_t ncerts; - proof.data = (uint8_t*)strcopy("PROOF"); - proof.length = strlen("PROOF"); - ncerts = 2; - certs = malloc(sizeof(nghttp2_mem_chunk)*ncerts); - certs[0].data = (uint8_t*)strcopy("CERT0"); - certs[0].length = strlen("CERT0"); - certs[1].data = (uint8_t*)strcopy("CERT1"); - certs[1].length = strlen("CERT1"); - nghttp2_frame_credential_init(&frame.credential, NGHTTP2_PROTO_SPDY3, - 1, &proof, certs, ncerts); - framelen = nghttp2_frame_pack_credential(&buf, &buflen, &frame.credential); - CU_ASSERT(0 == nghttp2_frame_unpack_credential - (&oframe.credential, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - CU_ASSERT(1 == oframe.credential.slot); - CU_ASSERT(5 == oframe.credential.proof.length); - CU_ASSERT(memcmp("PROOF", oframe.credential.proof.data, 5) == 0); - CU_ASSERT(2 == oframe.credential.ncerts); - CU_ASSERT(5 == oframe.credential.certs[0].length); - CU_ASSERT(memcmp("CERT0", oframe.credential.certs[0].data, 5) == 0); - CU_ASSERT(5 == oframe.credential.certs[1].length); - CU_ASSERT(memcmp("CERT1", oframe.credential.certs[1].data, 5) == 0); - CU_ASSERT(NGHTTP2_PROTO_SPDY3 == oframe.credential.hd.version); - CU_ASSERT(NGHTTP2_CREDENTIAL == oframe.credential.hd.type); - CU_ASSERT(NGHTTP2_CTRL_FLAG_NONE == oframe.credential.hd.flags); - CU_ASSERT(framelen-NGHTTP2_FRAME_HEAD_LENGTH == oframe.credential.hd.length); - nghttp2_frame_credential_free(&oframe.credential); - - /* Put large certificate length */ - nghttp2_put_uint32be(&buf[8+2+4+5], INT32_MAX); - CU_ASSERT(NGHTTP2_ERR_INVALID_FRAME == nghttp2_frame_unpack_credential - (&oframe.credential, - &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, - &buf[NGHTTP2_FRAME_HEAD_LENGTH], - framelen-NGHTTP2_FRAME_HEAD_LENGTH)); - - free(buf); - nghttp2_frame_credential_free(&frame.credential); -} - void test_nghttp2_frame_nv_sort(void) { char *nv[7]; @@ -762,232 +457,6 @@ void test_nghttp2_frame_nv_downcase(void) nghttp2_frame_nv_del(nv); } -void test_nghttp2_frame_nv_2to3(void) -{ - const char *nv_src[] = { - "host", "localhost", - "method", "GET", - "url", "/", - "accept", "*/*", - "scheme", "https", - "status", "200 OK", - "version", "HTTP/1.1", - NULL - }; - char **nv; - nv = nghttp2_frame_nv_copy(nv_src); - nghttp2_frame_nv_2to3(nv); - CU_ASSERT(0 == strcmp(":host", nv[0])); - CU_ASSERT(0 == strcmp(":method", nv[2])); - CU_ASSERT(0 == strcmp(":path", nv[4])); - CU_ASSERT(0 == strcmp("accept", nv[6])); - CU_ASSERT(0 == strcmp(":scheme", nv[8])); - CU_ASSERT(0 == strcmp(":status", nv[10])); - CU_ASSERT(0 == strcmp(":version", nv[12])); - nghttp2_frame_nv_del(nv); -} - -void test_nghttp2_frame_nv_3to2(void) -{ - const char *nv_src[] = { - ":host", "localhost", - ":method", "GET", - ":path", "/", - "accept", "*/*", - ":scheme", "https", - ":status", "200 OK", - ":version", "HTTP/1.1", - NULL - }; - char **nv; - nv = nghttp2_frame_nv_copy(nv_src); - nghttp2_frame_nv_3to2(nv); - CU_ASSERT(0 == strcmp("host", nv[0])); - CU_ASSERT(0 == strcmp("method", nv[2])); - CU_ASSERT(0 == strcmp("url", nv[4])); - CU_ASSERT(0 == strcmp("accept", nv[6])); - CU_ASSERT(0 == strcmp("scheme", nv[8])); - CU_ASSERT(0 == strcmp("status", nv[10])); - CU_ASSERT(0 == strcmp("version", nv[12])); - nghttp2_frame_nv_del(nv); -} - -/* This function intentionally does not merge same header field into - one */ -static size_t nghttp2_pack_nv(uint8_t *buf, size_t buflen, const char **nv, - size_t len_size) -{ - size_t i, n; - uint8_t *buf_ptr; - buf_ptr = buf; - for(n = 0; nv[n]; ++n); - nghttp2_frame_put_nv_len(buf_ptr, n/2, len_size); - buf_ptr += len_size; - for(i = 0; i < n; ++i) { - size_t len = strlen(nv[i]); - nghttp2_frame_put_nv_len(buf_ptr, len, len_size); - buf_ptr += len_size; - memcpy(buf_ptr, nv[i], len); - buf_ptr += len; - } - return buf_ptr-buf; -} - -static const char *empty_name_headers[] = { - "method", "GET", - "", "https", - "url", "/", - NULL -}; - -static const char non_ascii_header_name[] = { (char)0xff }; - -static const char *non_ascii_headers[] = { - non_ascii_header_name, "foo", - NULL -}; - -static void test_nghttp2_frame_unpack_nv_check_name_with(size_t len_size) -{ - uint8_t nvbuf[1024]; - size_t nvbuflen; - nghttp2_buffer buffer; - char **nv; - - nghttp2_buffer_init(&buffer, 32); - - nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), headers, len_size); - nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); - - CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == - nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); - - nghttp2_frame_nv_del(nv); - nghttp2_buffer_reset(&buffer); - - nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), empty_name_headers, - len_size); - nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); - - CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == - nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); - - nghttp2_frame_nv_del(nv); - nghttp2_buffer_reset(&buffer); - - nvbuflen = nghttp2_pack_nv(nvbuf, sizeof(nvbuf), non_ascii_headers, - len_size); - nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); - CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == - nghttp2_frame_unpack_nv(&nv, &buffer, len_size)); - - nghttp2_frame_nv_del(nv); - nghttp2_buffer_free(&buffer); -} - -void test_nghttp2_frame_unpack_nv_check_name_spdy2(void) -{ - test_nghttp2_frame_unpack_nv_check_name_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY2)); -} - -void test_nghttp2_frame_unpack_nv_check_name_spdy3(void) -{ - test_nghttp2_frame_unpack_nv_check_name_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY3)); -} - -static void test_nghttp2_frame_unpack_nv_last_empty_value_with(size_t len_size) -{ - size_t nvbuflen; - uint8_t nvbuf[256]; - uint8_t *nvbufptr; - nghttp2_buffer buffer; - char **outnv = 0; - const char hdname[] = "method"; - - nvbufptr = nvbuf; - nghttp2_frame_put_nv_len(nvbufptr, 1, len_size); - nvbufptr += len_size; - nghttp2_frame_put_nv_len(nvbufptr, sizeof(hdname)-1, len_size); - nvbufptr += len_size; - memcpy(nvbufptr, hdname, sizeof(hdname)-1); - nvbufptr += sizeof(hdname)-1; - nghttp2_frame_put_nv_len(nvbufptr, 4, len_size); - nvbufptr += len_size; - /* Copy including terminating NULL */ - memcpy(nvbufptr, "GET", 4); - nvbufptr += 4; - nvbuflen = nvbufptr - nvbuf; - - nghttp2_buffer_init(&buffer, 32); - - nghttp2_buffer_write(&buffer, nvbuf, nvbuflen); - CU_ASSERT(NGHTTP2_ERR_INVALID_HEADER_BLOCK == - nghttp2_frame_unpack_nv(&outnv, &buffer, len_size)); - - nghttp2_frame_nv_del(outnv); - nghttp2_buffer_free(&buffer); -} - -void test_nghttp2_frame_unpack_nv_last_empty_value_spdy2(void) -{ - test_nghttp2_frame_unpack_nv_last_empty_value_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY2)); -} - -void test_nghttp2_frame_unpack_nv_last_empty_value_spdy3(void) -{ - test_nghttp2_frame_unpack_nv_last_empty_value_with - (nghttp2_frame_get_len_size(NGHTTP2_PROTO_SPDY3)); -} - -void test_nghttp2_frame_nv_set_origin(void) -{ - nghttp2_origin origin; - const char *nv1[] = { - ":host", "example.org", - ":scheme", "https", - NULL - }; - const char *nv2[] = { - ":host", "example.org:8443", - ":scheme", "https", - NULL - }; - const char *nv3[] = { - ":host", "example.org:0", - ":scheme", "https", - NULL - }; - const char *nv4[] = { - ":host", "example.org", - NULL - }; - const char *nv5[] = { - ":scheme", "https", - NULL - }; - CU_ASSERT(0 == nghttp2_frame_nv_set_origin((char**)nv1, &origin)); - CU_ASSERT(strcmp("https", origin.scheme) == 0); - CU_ASSERT(strcmp("example.org", origin.host) == 0); - CU_ASSERT(443 == origin.port); - - CU_ASSERT(0 == nghttp2_frame_nv_set_origin((char**)nv2, &origin)); - CU_ASSERT(strcmp("https", origin.scheme) == 0); - CU_ASSERT(strcmp("example.org", origin.host) == 0); - CU_ASSERT(8443 == origin.port); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_frame_nv_set_origin((char**)nv3, &origin)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_frame_nv_set_origin((char**)nv4, &origin)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_frame_nv_set_origin((char**)nv5, &origin)); -} - void test_nghttp2_frame_nv_check_null(void) { const char *headers1[] = { "path", "/", "host", "a", NULL }; @@ -1000,3 +469,242 @@ void test_nghttp2_frame_nv_check_null(void) CU_ASSERT(0 == nghttp2_frame_nv_check_null(headers3)); CU_ASSERT(0 == nghttp2_frame_nv_check_null(headers4)); } + +static void check_frame_header(uint16_t length, uint8_t type, uint8_t flags, + int32_t stream_id, nghttp2_frame_hd *hd) +{ + CU_ASSERT(length == hd->length); + CU_ASSERT(type == hd->type); + CU_ASSERT(flags == hd->flags); + CU_ASSERT(stream_id == hd->stream_id); +} + +void test_nghttp2_frame_pack_headers() +{ + nghttp2_zlib deflater, inflater; + nghttp2_headers frame, oframe; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + ssize_t framelen; + nghttp2_zlib_deflate_hd_init(&deflater, 1, 0); + nghttp2_zlib_inflate_hd_init(&inflater, 0); + nghttp2_frame_headers_init(&frame, NGHTTP2_FLAG_END_STREAM, 1000000007, + 1 << 20, nghttp2_frame_nv_copy(headers)); + framelen = nghttp2_frame_pack_headers(&buf, &buflen, &nvbuf, &nvbuflen, + &frame, &deflater); + + CU_ASSERT(0 == unpack_frame_with_nv_block((nghttp2_frame*)&oframe, + NGHTTP2_HEADERS, + &inflater, + buf, framelen)); + check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM, 1000000007, &oframe.hd); + /* We didn't include PRIORITY flag so priority is not packed */ + CU_ASSERT(1 << 30 == oframe.pri); + CU_ASSERT(strcmp("method", oframe.nv[0]) == 0); + CU_ASSERT(strcmp("GET", oframe.nv[1]) == 0); + CU_ASSERT(NULL == oframe.nv[14]); + + nghttp2_frame_headers_free(&oframe); + memset(&oframe, 0, sizeof(oframe)); + /* Next, include PRIORITY flag */ + frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; + framelen = nghttp2_frame_pack_headers(&buf, &buflen, &nvbuf, &nvbuflen, + &frame, &deflater); + + CU_ASSERT(0 == unpack_frame_with_nv_block((nghttp2_frame*)&oframe, + NGHTTP2_HEADERS, + &inflater, + buf, framelen)); + check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY, + 1000000007, &oframe.hd); + CU_ASSERT(1 << 20 == oframe.pri); + CU_ASSERT(strcmp("method", oframe.nv[0]) == 0); + + free(buf); + free(nvbuf); + nghttp2_frame_headers_free(&oframe); + nghttp2_frame_headers_free(&frame); + nghttp2_zlib_inflate_free(&inflater); + nghttp2_zlib_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_headers_frame_too_large(void) +{ + nghttp2_zlib deflater; + nghttp2_headers frame; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + ssize_t framelen; + size_t big_vallen = 1 << 16; + char *big_val = malloc(big_vallen + 1); + const char *big_hds[] = { "header", big_val, NULL }; + memset(big_val, '0', big_vallen); + big_val[big_vallen] = '\0'; + /* No compression */ + nghttp2_zlib_deflate_hd_init(&deflater, 0, 0); + nghttp2_frame_headers_init(&frame, NGHTTP2_FLAG_END_STREAM, 1000000007, + 0, nghttp2_frame_nv_copy(big_hds)); + framelen = nghttp2_frame_pack_headers(&buf, &buflen, + &nvbuf, &nvbuflen, + &frame, &deflater); + CU_ASSERT_EQUAL(NGHTTP2_ERR_FRAME_TOO_LARGE, framelen); + + nghttp2_frame_headers_free(&frame); + free(buf); + free(nvbuf); + free(big_val); + nghttp2_zlib_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_priority(void) +{ + nghttp2_priority frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + nghttp2_frame_priority_init(&frame, 1000000007, 1 << 30); + framelen = nghttp2_frame_pack_priority(&buf, &buflen, &frame); + CU_ASSERT(0 == nghttp2_frame_unpack_priority + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen - NGHTTP2_FRAME_HEAD_LENGTH)); + check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(1 << 30 == oframe.pri); + free(buf); + nghttp2_frame_priority_free(&oframe); + nghttp2_frame_priority_free(&frame); +} + +void test_nghttp2_frame_pack_rst_stream(void) +{ + nghttp2_rst_stream frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); + framelen = nghttp2_frame_pack_rst_stream(&buf, &buflen, &frame); + CU_ASSERT(0 == nghttp2_frame_unpack_rst_stream + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen - NGHTTP2_FRAME_HEAD_LENGTH)); + check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + free(buf); + nghttp2_frame_rst_stream_free(&oframe); + nghttp2_frame_rst_stream_free(&frame); +} + +void test_nghttp2_frame_pack_settings() +{ + nghttp2_settings frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + int i; + nghttp2_settings_entry iv[3]; + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 256; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16384; + iv[2].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS; + iv[2].value = 1; + + nghttp2_frame_settings_init(&frame, nghttp2_frame_iv_copy(iv, 3), 3); + framelen = nghttp2_frame_pack_settings(&buf, &buflen, &frame); + CU_ASSERT(NGHTTP2_FRAME_HEAD_LENGTH+3*8 == framelen); + + CU_ASSERT(0 == nghttp2_frame_unpack_settings + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen - NGHTTP2_FRAME_HEAD_LENGTH)); + + check_frame_header(3*8, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(3 == oframe.niv); + for(i = 0; i < 3; ++i) { + CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id); + CU_ASSERT(iv[i].value == oframe.iv[i].value); + } + + free(buf); + nghttp2_frame_settings_free(&frame); + nghttp2_frame_settings_free(&oframe); +} + +void test_nghttp2_frame_pack_ping(void) +{ + nghttp2_ping frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + const uint8_t opaque_data[] = "01234567"; + nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_PONG, opaque_data); + framelen = nghttp2_frame_pack_ping(&buf, &buflen, &frame); + CU_ASSERT(0 == nghttp2_frame_unpack_ping + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen - NGHTTP2_FRAME_HEAD_LENGTH)); + check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_PONG, 0, &oframe.hd); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) + == 0); + free(buf); + nghttp2_frame_ping_free(&oframe); + nghttp2_frame_ping_free(&frame); +} + +void test_nghttp2_frame_pack_goaway() +{ + nghttp2_goaway frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + size_t opaque_data_len = 16; + uint8_t *opaque_data = malloc(opaque_data_len); + memcpy(opaque_data, "0123456789abcdef", opaque_data_len); + nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, + opaque_data, opaque_data_len); + framelen = nghttp2_frame_pack_goaway(&buf, &buflen, &frame); + CU_ASSERT(0 == nghttp2_frame_unpack_goaway + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen-NGHTTP2_FRAME_HEAD_LENGTH)); + check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(1000000007 == oframe.last_stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + CU_ASSERT(opaque_data_len == oframe.opaque_data_len); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); + free(buf); + nghttp2_frame_goaway_free(&oframe); + nghttp2_frame_goaway_free(&frame); +} + +void test_nghttp2_frame_pack_window_update(void) +{ + nghttp2_window_update frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_END_FLOW_CONTROL, + 1000000007, 4096); + framelen = nghttp2_frame_pack_window_update(&buf, &buflen, + &frame); + CU_ASSERT(0 == nghttp2_frame_unpack_window_update + (&oframe, + &buf[0], NGHTTP2_FRAME_HEAD_LENGTH, + &buf[NGHTTP2_FRAME_HEAD_LENGTH], + framelen - NGHTTP2_FRAME_HEAD_LENGTH)); + check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_END_FLOW_CONTROL, + 1000000007, &oframe.hd); + CU_ASSERT(4096 == oframe.window_size_increment); + free(buf); + nghttp2_frame_window_update_free(&oframe); + nghttp2_frame_window_update_free(&frame); +} diff --git a/tests/nghttp2_frame_test.h b/tests/nghttp2_frame_test.h index 349cf724..7f47203c 100644 --- a/tests/nghttp2_frame_test.h +++ b/tests/nghttp2_frame_test.h @@ -25,36 +25,23 @@ #ifndef NGHTTP2_FRAME_TEST_H #define NGHTTP2_FRAME_TEST_H -void test_nghttp2_frame_unpack_nv_spdy2(void); -void test_nghttp2_frame_unpack_nv_spdy3(void); +void test_nghttp2_frame_unpack_nv(void); +void test_nghttp2_frame_unpack_nv_check_name(void); +void test_nghttp2_frame_unpack_nv_last_empty_value(void); void test_nghttp2_frame_pack_nv_duplicate_keys(void); -void test_nghttp2_frame_pack_nv_empty_value_spdy2(void); -void test_nghttp2_frame_pack_nv_empty_value_spdy3(void); void test_nghttp2_frame_count_nv_space(void); +void test_nghttp2_frame_pack_nv_empty_value(void); void test_nghttp2_frame_count_unpack_nv_space(void); -void test_nghttp2_frame_pack_ping(void); -void test_nghttp2_frame_pack_goaway_spdy2(void); -void test_nghttp2_frame_pack_goaway_spdy3(void); -void test_nghttp2_frame_pack_syn_stream_spdy2(void); -void test_nghttp2_frame_pack_syn_stream_spdy3(void); -void test_nghttp2_frame_pack_syn_stream_frame_too_large(void); -void test_nghttp2_frame_pack_syn_reply_spdy2(void); -void test_nghttp2_frame_pack_syn_reply_spdy3(void); -void test_nghttp2_frame_pack_headers_spdy2(void); -void test_nghttp2_frame_pack_headers_spdy3(void); -void test_nghttp2_frame_pack_window_update(void); -void test_nghttp2_frame_pack_settings_spdy2(void); -void test_nghttp2_frame_pack_settings_spdy3(void); -void test_nghttp2_frame_pack_credential(void); void test_nghttp2_frame_nv_sort(void); void test_nghttp2_frame_nv_downcase(void); -void test_nghttp2_frame_nv_2to3(void); -void test_nghttp2_frame_nv_3to2(void); -void test_nghttp2_frame_unpack_nv_check_name_spdy2(void); -void test_nghttp2_frame_unpack_nv_check_name_spdy3(void); -void test_nghttp2_frame_unpack_nv_last_empty_value_spdy2(void); -void test_nghttp2_frame_unpack_nv_last_empty_value_spdy3(void); -void test_nghttp2_frame_nv_set_origin(void); void test_nghttp2_frame_nv_check_null(void); +void test_nghttp2_frame_pack_headers(void); +void test_nghttp2_frame_pack_headers_frame_too_large(void); +void test_nghttp2_frame_pack_priority(void); +void test_nghttp2_frame_pack_rst_stream(void); +void test_nghttp2_frame_pack_settings(void); +void test_nghttp2_frame_pack_ping(void); +void test_nghttp2_frame_pack_goaway(void); +void test_nghttp2_frame_pack_window_update(void); #endif /* NGHTTP2_FRAME_TEST_H */ diff --git a/tests/nghttp2_npn_test.c b/tests/nghttp2_npn_test.c index 29ddc217..1eeae532 100644 --- a/tests/nghttp2_npn_test.c +++ b/tests/nghttp2_npn_test.c @@ -28,19 +28,19 @@ #include #include -static void spdy2(void) +static void http2(void) { - const unsigned char spdy[] = { + const unsigned char p[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '1', - 6, 's', 'p', 'd', 'y', '/', '2', + 17, 'H', 'T', 'T', 'P', '-', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', + '2', '.', '0', 6, 's', 'p', 'd', 'y', '/', '3' }; unsigned char outlen; unsigned char* out; - CU_ASSERT(NGHTTP2_PROTO_SPDY2 == - nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); - CU_ASSERT(6 == outlen); - CU_ASSERT(memcmp("spdy/2", out, outlen) == 0); + CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p))); + CU_ASSERT(17 == outlen); + CU_ASSERT(memcmp("HTTP-draft-04/2.0", out, outlen) == 0); } static void http11(void) @@ -75,21 +75,7 @@ static void no_overlap(void) void test_nghttp2_npn(void) { - spdy2(); + http2(); http11(); no_overlap(); } - -void test_nghttp2_npn_get_proto_list(void) -{ - size_t len; - const nghttp2_npn_proto *list = nghttp2_npn_get_proto_list(&len); - CU_ASSERT_EQUAL(2, len); - CU_ASSERT_STRING_EQUAL("spdy/3", list[0].proto); - CU_ASSERT_EQUAL(6, list[0].len); - CU_ASSERT_EQUAL(NGHTTP2_PROTO_SPDY3, list[0].version); - - CU_ASSERT_STRING_EQUAL("spdy/2", list[1].proto); - CU_ASSERT_EQUAL(6, list[1].len); - CU_ASSERT_EQUAL(NGHTTP2_PROTO_SPDY2, list[1].version); -} diff --git a/tests/nghttp2_npn_test.h b/tests/nghttp2_npn_test.h index 1061b7ce..de9a3960 100644 --- a/tests/nghttp2_npn_test.h +++ b/tests/nghttp2_npn_test.h @@ -26,6 +26,5 @@ #define NGHTTP2_NPN_TEST_H void test_nghttp2_npn(void); -void test_nghttp2_npn_get_proto_list(void); #endif /* NGHTTP2_NPN_TEST_H */ diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 2de432c8..5eed30e9 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -1,7 +1,7 @@ /* * nghttp2 - HTTP/2.0 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -55,10 +55,10 @@ typedef struct { typedef struct { accumulator *acc; scripted_data_feed *df; - int ctrl_recv_cb_called, invalid_ctrl_recv_cb_called; - int ctrl_send_cb_called; + int frame_recv_cb_called, invalid_frame_recv_cb_called; + int frame_send_cb_called; nghttp2_frame_type sent_frame_type; - int ctrl_not_send_cb_called; + int frame_not_send_cb_called; nghttp2_frame_type not_sent_frame_type; int not_sent_error; int stream_close_cb_called; @@ -127,45 +127,41 @@ static ssize_t accumulator_send_callback(nghttp2_session *session, return len; } -static void on_ctrl_recv_callback(nghttp2_session *session, - nghttp2_frame_type type, - nghttp2_frame *frame, - void *user_data) +static void on_frame_recv_callback(nghttp2_session *session, + nghttp2_frame *frame, + void *user_data) { my_user_data *ud = (my_user_data*)user_data; - ++ud->ctrl_recv_cb_called; + ++ud->frame_recv_cb_called; } -static void on_invalid_ctrl_recv_callback(nghttp2_session *session, - nghttp2_frame_type type, - nghttp2_frame *frame, - uint32_t status_code, - void *user_data) +static void on_invalid_frame_recv_callback(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_error_code error_code, + void *user_data) { my_user_data *ud = (my_user_data*)user_data; - ++ud->invalid_ctrl_recv_cb_called; + ++ud->invalid_frame_recv_cb_called; } -static void on_ctrl_send_callback(nghttp2_session *session, - nghttp2_frame_type type, - nghttp2_frame *frame, - void *user_data) +static void on_frame_send_callback(nghttp2_session *session, + nghttp2_frame *frame, + void *user_data) { my_user_data *ud = (my_user_data*)user_data; - ++ud->ctrl_send_cb_called; - ud->sent_frame_type = type; + ++ud->frame_send_cb_called; + ud->sent_frame_type = frame->hd.type; } -static void on_ctrl_not_send_callback(nghttp2_session *session, - nghttp2_frame_type type, - nghttp2_frame *frame, - int error, - void *user_data) +static void on_frame_not_send_callback(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error, + void *user_data) { my_user_data *ud = (my_user_data*)user_data; - ++ud->ctrl_not_send_cb_called; - ud->not_sent_frame_type = type; - ud->not_sent_error = error; + ++ud->frame_not_send_cb_called; + ud->not_sent_frame_type = frame->hd.type; + ud->not_sent_error = lib_error; } static void on_data_chunk_recv_callback(nghttp2_session *session, @@ -228,14 +224,48 @@ static void on_request_recv_callback(nghttp2_session *session, ud->stream_id = stream_id; } -static void no_stream_user_data_stream_close_callback -(nghttp2_session *session, - int32_t stream_id, - nghttp2_status_code status_code, - void *user_data) +/* static void no_stream_user_data_stream_close_callback */ +/* (nghttp2_session *session, */ +/* int32_t stream_id, */ +/* nghttp2_error_code error_code, */ +/* void *user_data) */ +/* { */ +/* my_user_data* my_data = (my_user_data*)user_data; */ +/* ++my_data->stream_close_cb_called; */ +/* } */ + +static ssize_t block_count_send_callback(nghttp2_session* session, + const uint8_t *data, size_t len, + int flags, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + int r; + if(ud->block_count == 0) { + r = NGHTTP2_ERR_WOULDBLOCK; + } else { + --ud->block_count; + r = len; + } + return r; +} + +static ssize_t defer_data_source_read_callback +(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t len, int *eof, + nghttp2_data_source *source, void *user_data) +{ + return NGHTTP2_ERR_DEFERRED; +} + +static void stream_close_callback(nghttp2_session *session, int32_t stream_id, + nghttp2_error_code error_code, + void *user_data) { my_user_data* my_data = (my_user_data*)user_data; + void *stream_data = nghttp2_session_get_stream_user_data(session, stream_id); ++my_data->stream_close_cb_called; + CU_ASSERT(stream_data != NULL); } static char** dup_nv(const char **src) @@ -269,6 +299,9 @@ void test_nghttp2_session_recv(void) const char *upcase_nv[] = { "URL", "/", NULL }; + const char *empty_nv[] = { + NULL + }; const char *mid_nv[] = { "method", "GET", "scheme", "https", @@ -289,253 +322,133 @@ void test_nghttp2_session_recv(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; callbacks.recv_callback = scripted_recv_callback; - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; user_data.df = &df; - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - 1, 0, 3, dup_nv(nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 1, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.headers, + &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); /* Send 1 byte per each read */ for(i = 0; i < framelen; ++i) { df.feedseq[i] = 1; } - nghttp2_frame_syn_stream_free(&frame.syn_stream); + nghttp2_frame_headers_free(&frame.headers); - user_data.ctrl_recv_cb_called = 0; + user_data.frame_recv_cb_called = 0; while((ssize_t)df.seqidx < framelen) { CU_ASSERT(0 == nghttp2_session_recv(session)); } - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Receive HEADERS with invalid header block */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 3, NGHTTP2_PRI_DEFAULT, dup_nv(upcase_nv)); + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.headers, + &session->hd_deflater); + nghttp2_frame_headers_free(&frame.headers); - /* Receive SYN_STREAM with invalid header block */ - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - 3, 0, 3, dup_nv(upcase_nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); - nghttp2_frame_syn_stream_free(&frame.syn_stream); scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; + user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); + CU_ASSERT(0 == user_data.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.error_code); CU_ASSERT(0 == nghttp2_session_send(session)); - /* Received SYN_STREAM without name/value header block */ - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - 5, 0, 3, dup_nv(upcase_nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - /* Use bytes that come before name/value header block */ - nghttp2_put_uint32be(&framedata[4], - NGHTTP2_SYN_STREAM_NV_OFFSET - NGHTTP2_HEAD_LEN); - scripted_data_feed_init(&df, framedata, NGHTTP2_SYN_STREAM_NV_OFFSET); - user_data.ctrl_recv_cb_called = 0; + /* Received HEADERS without header block, which is valid */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 5, NGHTTP2_PRI_DEFAULT, dup_nv(empty_nv)); + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.headers, + &session->hd_deflater); + nghttp2_frame_headers_free(&frame.headers); + + scripted_data_feed_init(&df, framedata, framelen); + user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); nghttp2_session_del(session); /* Some tests for frame too large */ - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, - &user_data); + nghttp2_session_server_new(&session, &callbacks, &user_data); /* made max buffer small to cause error intentionally */ - /* Inflated wire format of mid_nv will be 111 in SPDY/3. So payload - length will be 121. Setting max buffer size to 110 will cause - error while inflating name/value header block. */ - session->max_recv_ctrl_frame_buf = 110; - - /* Receive SYN_STREAM with too large payload */ - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY3, - NGHTTP2_CTRL_FLAG_NONE, - 1, 0, 3, dup_nv(mid_nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); - CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* For SYN_REPLY and SYN_HEADERS, make max buffer even smaller */ session->max_recv_ctrl_frame_buf = 8; - /* Receive SYN_REPLY with too large payload */ - nghttp2_frame_syn_reply_init(&frame.syn_reply, NGHTTP2_PROTO_SPDY3, - NGHTTP2_CTRL_FLAG_NONE, - 1, dup_nv(mid_nv)); - framelen = nghttp2_frame_pack_syn_reply(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_reply, - &session->hd_deflater); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); - CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - /* Receive HEADERS with too large payload */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_PROTO_SPDY3, - NGHTTP2_CTRL_FLAG_NONE, - 1, dup_nv(mid_nv)); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 1, NGHTTP2_PRI_DEFAULT, dup_nv(mid_nv)); framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &nvbuf, &nvbuflen, &frame.headers, &session->hd_deflater); nghttp2_frame_headers_free(&frame.headers); + scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; + user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); + CU_ASSERT(0 == user_data.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); - CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); + CU_ASSERT(NGHTTP2_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.error_code); + CU_ASSERT(1 == OB_CTRL(item)->hd.stream_id); CU_ASSERT(0 == nghttp2_session_send(session)); /* Receive PING with too large payload */ - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_PROTO_SPDY3, 1); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); nghttp2_reserve_buffer(&framedata, &framedatalen, 77); framelen = nghttp2_frame_pack_ping(&framedata, &framedatalen, &frame.ping); nghttp2_frame_ping_free(&frame.ping); - nghttp2_put_uint32be(&framedata[4], framedatalen - NGHTTP2_HEAD_LEN); + + nghttp2_put_uint16be(&framedata[0], + framedatalen - NGHTTP2_FRAME_HEAD_LENGTH); scripted_data_feed_init(&df, framedata, framedatalen); - user_data.ctrl_recv_cb_called = 0; + user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); + CU_ASSERT(0 == user_data.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_GOAWAY_PROTOCOL_ERROR == - OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->goaway.error_code); CU_ASSERT(0 == nghttp2_session_send(session)); nghttp2_session_del(session); - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - /* Receive SYN_REPLY with invalid header block */ - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_syn_reply_init(&frame.syn_reply, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, dup_nv(upcase_nv)); - framelen = nghttp2_frame_pack_syn_reply(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_reply, - &session->hd_deflater); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code); - - CU_ASSERT(0 == nghttp2_session_send(session)); - + nghttp2_session_client_new(&session, &callbacks, &user_data); /* Receive HEADERS with invalid header block */ - nghttp2_session_open_stream(session, 3, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 3, dup_nv(upcase_nv)); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_PRI_DEFAULT, dup_nv(upcase_nv)); framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &nvbuf, &nvbuflen, &frame.headers, &session->hd_deflater); nghttp2_frame_headers_free(&frame.headers); + scripted_data_feed_init(&df, framedata, framelen); - user_data.ctrl_recv_cb_called = 0; + user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.ctrl_recv_cb_called); + CU_ASSERT(0 == user_data.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.error_code); + + CU_ASSERT(0 == nghttp2_session_send(session)); free(framedata); free(nvbuf); nghttp2_session_del(session); } -void test_nghttp2_session_add_frame(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - my_user_data user_data; - const char *nv[] = { - "method", "GET", - "scheme", "https", - "url", "/", - "version", "HTTP/1.1", - NULL - }; - nghttp2_frame *frame; - nghttp2_syn_stream_aux_data *aux_data = - malloc(sizeof(nghttp2_syn_stream_aux_data)); - const uint8_t hd_ans1[] = { - 0x80, 0x02, 0x00, 0x01 - }; - uint32_t temp32; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - memset(aux_data, 0, sizeof(nghttp2_syn_stream_aux_data)); - acc.length = 0; - user_data.acc = &acc; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &user_data)); - - frame = malloc(sizeof(nghttp2_frame)); - nghttp2_frame_syn_stream_init(&frame->syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 0, 0, 3, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, - aux_data)); - CU_ASSERT(0 == nghttp2_pq_empty(&session->ob_ss_pq)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(memcmp(hd_ans1, acc.buf, 4) == 0); - /* check stream id */ - memcpy(&temp32, &acc.buf[8], 4); - temp32 = ntohl(temp32); - CU_ASSERT(1 == temp32); - /* check assoc stream id */ - memcpy(&temp32, &acc.buf[12], 4); - temp32 = ntohl(temp32); - CU_ASSERT(0 == temp32); - /* check pri */ - temp32 = (acc.buf[16] >> 6) & 0x3; - CU_ASSERT(3 == temp32); - - nghttp2_session_del(session); -} - void test_nghttp2_session_recv_invalid_stream_id(void) { nghttp2_session *session; @@ -549,1267 +462,28 @@ void test_nghttp2_session_recv_invalid_stream_id(void) nghttp2_frame frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; user_data.df = &df; - user_data.invalid_ctrl_recv_cb_called = 0; - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); + user_data.invalid_frame_recv_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.headers, + &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); - nghttp2_frame_syn_stream_free(&frame.syn_stream); + nghttp2_frame_headers_free(&frame.headers); CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_syn_reply_init(&frame.syn_reply, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 100, dup_nv(nv)); - framelen = nghttp2_frame_pack_syn_reply(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_reply, - &session->hd_deflater); - scripted_data_feed_init(&df, framedata, framelen); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); free(framedata); free(nvbuf); nghttp2_session_del(session); } -void test_nghttp2_session_on_syn_stream_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const char *nv[] = { NULL }; - nghttp2_frame frame; - nghttp2_stream *stream; - int32_t stream_id = 1; - uint8_t pri = 3; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - stream_id, 0, pri, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - stream = nghttp2_session_get_stream(session, stream_id); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - CU_ASSERT(pri == stream->pri); - - /* Same stream ID twice leads stream error */ - user_data.invalid_ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state); - - /* assoc_stream_id != 0 from client is invalid. */ - frame.syn_stream.stream_id = 3; - frame.syn_stream.assoc_stream_id = 1; - user_data.invalid_ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - - /* More than max concurrent streams leads REFUSED_STREAM */ - session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - 5, 0, 3, dup_nv(nv)); - user_data.invalid_ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = - NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; - - /* Stream ID less than previouly received SYN_STREAM leads session - error */ - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, - 1, 0, 3, dup_nv(nv)); - user_data.invalid_ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_syn_stream_received_with_push(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const char *nv[] = { NULL }; - nghttp2_frame frame; - nghttp2_stream *stream; - int32_t stream_id = 2; - int32_t assoc_stream_id = 1; - uint8_t pri = 3; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_session_open_stream(session, assoc_stream_id, NGHTTP2_CTRL_FLAG_NONE, - pri, NGHTTP2_STREAM_OPENED, NULL); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, - stream_id, assoc_stream_id, pri, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - stream = nghttp2_session_get_stream(session, stream_id); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - - /* assoc_stream_id == 0 is invalid */ - frame.syn_stream.stream_id = 4; - frame.syn_stream.assoc_stream_id = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - /* Push without NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL is invalid */ - frame.syn_stream.stream_id = 6; - frame.syn_stream.assoc_stream_id = 1; - frame.syn_stream.hd.flags = NGHTTP2_CTRL_FLAG_FIN; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); - - /* Push to non-existent stream is invalid */ - frame.syn_stream.stream_id = 8; - frame.syn_stream.assoc_stream_id = 3; - frame.syn_stream.hd.flags = NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL; - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_syn_reply_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const char *nv[] = { NULL }; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_outbound_item *item; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_syn_reply_init(&frame.syn_reply, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENED == - ((nghttp2_stream*)nghttp2_map_find(&session->streams, 1))->state); - - CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - CU_ASSERT(NGHTTP2_STREAM_CLOSING == - ((nghttp2_stream*)nghttp2_map_find(&session->streams, 1))->state); - - /* Check the situation when SYN_REPLY is received after peer sends - FIN */ - stream = nghttp2_session_open_stream(session, 3, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - frame.syn_reply.stream_id = 3; - - CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame)); - CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_syn_reply_free(&frame.syn_reply); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, - &user_data); - - /* Multiple SYN_REPLY frames for the same active stream ID */ - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_frame_syn_reply_init(&frame.syn_reply, NGHTTP2_PROTO_SPDY3, - NGHTTP2_CTRL_FLAG_NONE, 1, dup_nv(nv)); - user_data.invalid_ctrl_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_STREAM_IN_USE == OB_CTRL(item)->rst_stream.status_code); - - nghttp2_frame_syn_reply_free(&frame.syn_reply); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_syn_stream(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - nghttp2_frame *frame = malloc(sizeof(nghttp2_frame)); - nghttp2_stream *stream; - nghttp2_syn_stream_aux_data *aux_data = - malloc(sizeof(nghttp2_syn_stream_aux_data)); - memset(aux_data, 0, sizeof(nghttp2_syn_stream_aux_data)); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - nghttp2_frame_syn_stream_init(&frame->syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 0, 0, 3, dup_nv(nv)); - nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, aux_data); - CU_ASSERT(0 == nghttp2_session_send(session)); - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_syn_reply(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - nghttp2_frame *frame = malloc(sizeof(nghttp2_frame)); - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, NULL)); - nghttp2_session_open_stream(session, 2, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_syn_reply_init(&frame->syn_reply, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 2, dup_nv(nv)); - nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); - CU_ASSERT(0 == nghttp2_session_send(session)); - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_response(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { "Content-Length", "1024", NULL }; - int32_t stream_id = 2; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 64*1024; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - nghttp2_session_open_stream(session, stream_id, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, stream_id, nv, &data_prd)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("content-length", OB_CTRL(item)->syn_reply.nv[0])); - CU_ASSERT(0 == nghttp2_session_send(session)); - nghttp2_session_del(session); -} - -void test_nghttp2_submit_response_with_null_data_read_callback(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - const char *nv[] = { ":Version", "HTTP/1.1", NULL }; - nghttp2_data_provider data_prd = {{-1}, NULL}; - nghttp2_outbound_item *item; - my_user_data ud; - nghttp2_frame frame; - - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = accumulator_send_callback; - CU_ASSERT(0 == nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_FIN, 3, - NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, 1, nv, &data_prd)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->syn_reply.nv[0])); - CU_ASSERT(OB_CTRL(item)->syn_reply.hd.flags & NGHTTP2_CTRL_FLAG_FIN); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_SYN_REPLY, - NGHTTP2_PROTO_SPDY2, - &frame, - &session->hd_inflater, - acc.buf, acc.length)); - CU_ASSERT(0 == strcmp("version", frame.syn_reply.nv[0])); - nghttp2_frame_syn_reply_free(&frame.syn_reply); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_request_with_data(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { "Version", "HTTP/1.1", NULL }; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 64*1024; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_request(session, 3, nv, &data_prd, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", OB_CTRL(item)->syn_stream.nv[0])); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.data_source_length); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_request_with_null_data_read_callback(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - const char *nv[] = { ":Version", "HTTP/1.1", NULL }; - nghttp2_data_provider data_prd = {{-1}, NULL}; - nghttp2_outbound_item *item; - my_user_data ud; - nghttp2_frame frame; - - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_request(session, 3, nv, &data_prd, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->syn_stream.nv[0])); - CU_ASSERT(OB_CTRL(item)->syn_stream.hd.flags & NGHTTP2_CTRL_FLAG_FIN); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_SYN_STREAM, - NGHTTP2_PROTO_SPDY2, - &frame, - &session->hd_inflater, - acc.buf, acc.length)); - CU_ASSERT(0 == strcmp("version", frame.syn_stream.nv[0])); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_syn_stream(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { "version", "HTTP/1.1", NULL }; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, NULL)); - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 1, 3, - nv, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", OB_CTRL(item)->syn_stream.nv[0])); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == OB_CTRL(item)->syn_stream.hd.flags); - /* See assoc-stream-ID is ignored */ - CU_ASSERT(0 == OB_CTRL(item)->syn_stream.assoc_stream_id); - CU_ASSERT(3 == OB_CTRL(item)->syn_stream.pri); - - nghttp2_session_del(session); - - CU_ASSERT(0 == nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, NULL)); - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 1, 3, - nv, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", OB_CTRL(item)->syn_stream.nv[0])); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == OB_CTRL(item)->syn_stream.hd.flags); - CU_ASSERT(1 == OB_CTRL(item)->syn_stream.assoc_stream_id); - CU_ASSERT(3 == OB_CTRL(item)->syn_stream.pri); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_syn_reply(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { "version", "HTTP/1.1", NULL }; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - - CU_ASSERT(0 == nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 1, - nv)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", OB_CTRL(item)->syn_reply.nv[0])); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == OB_CTRL(item)->syn_reply.hd.flags); - - ud.ctrl_send_cb_called = 0; - ud.sent_frame_type = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.ctrl_send_cb_called); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(0 == nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 1, - nv)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.ctrl_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_REPLY == ud.sent_frame_type); - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { ":Version", "HTTP/1.1", NULL }; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - accumulator acc; - nghttp2_frame frame; - - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_FIN, 1, nv)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); - CU_ASSERT(NGHTTP2_CTRL_FLAG_FIN == OB_CTRL(item)->headers.hd.flags); - - ud.ctrl_send_cb_called = 0; - ud.sent_frame_type = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.ctrl_send_cb_called); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_FIN, 1, nv)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.ctrl_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - CU_ASSERT(0 == unpack_frame_with_nv_block(NGHTTP2_HEADERS, - NGHTTP2_PROTO_SPDY2, - &frame, - &session->hd_inflater, - acc.buf, acc.length)); - CU_ASSERT(0 == strcmp("version", frame.headers.nv[0])); - nghttp2_frame_headers_free(&frame.headers); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_invalid_nv(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, NULL)); - - /* nghttp2_submit_request */ - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_request(session, 3, empty_name_nv, NULL, NULL)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_request(session, 3, null_val_nv, NULL, NULL)); - - /* nghttp2_submit_response */ - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_response(session, 2, empty_name_nv, NULL)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_response(session, 2, null_val_nv, NULL)); - - /* nghttp2_submit_syn_stream */ - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_NONE, 0, - 0, empty_name_nv, NULL)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_NONE, 0, - 0, null_val_nv, NULL)); - - /* nghttp2_submit_syn_reply */ - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_NONE, 2, - empty_name_nv)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_NONE, 2, - null_val_nv)); - - /* nghttp2_submit_headers */ - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_NONE, 2, - empty_name_nv)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_NONE, 2, - null_val_nv)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_reply_fail(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - int32_t stream_id = 2; - nghttp2_data_provider data_prd; - my_user_data ud; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = fail_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 4*1024; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_response(session, stream_id, nv, &data_prd)); - CU_ASSERT(0 == nghttp2_session_send(session)); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_headers_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const char *nv[] = { NULL }; - nghttp2_frame frame; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_stream_shutdown(nghttp2_session_get_stream(session, 1), - NGHTTP2_SHUT_WR); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENED == - nghttp2_session_get_stream(session, 1)->state); - - frame.headers.hd.flags |= NGHTTP2_CTRL_FLAG_FIN; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(2 == user_data.ctrl_recv_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is - discarded. */ - nghttp2_session_open_stream(session, 3, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_CLOSING, NULL); - frame.headers.stream_id = 3; - frame.headers.hd.flags = NGHTTP2_CTRL_FLAG_NONE; - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(2 == user_data.ctrl_recv_cb_called); - CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); - - /* Server initiated stream */ - nghttp2_session_open_stream(session, 2, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENING, NULL); - - frame.headers.hd.flags = NGHTTP2_CTRL_FLAG_FIN; - frame.headers.stream_id = 2; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(3 == user_data.ctrl_recv_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENING == - nghttp2_session_get_stream(session, 2)->state); - CU_ASSERT(nghttp2_session_get_stream(session, 2)->shut_flags & - NGHTTP2_SHUT_RD); - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); - - nghttp2_frame_headers_free(&frame.headers); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_window_update_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_outbound_item *data_item; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, - &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_PROTO_SPDY3, - 1, 16*1024); - - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - CU_ASSERT(64*1024+16*1024 == stream->window_size); - - data_item = malloc(sizeof(nghttp2_outbound_item)); - memset(data_item, 0, sizeof(nghttp2_outbound_item)); - data_item->frame_cat = NGHTTP2_DATA; - nghttp2_stream_defer_data(stream, data_item, NGHTTP2_DEFERRED_FLOW_CONTROL); - - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(2 == user_data.ctrl_recv_cb_called); - CU_ASSERT(64*1024+16*1024*2 == stream->window_size); - CU_ASSERT(NULL == stream->deferred_data); - - nghttp2_frame_window_update_free(&frame.window_update); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_ping_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_outbound_item *top; - uint32_t unique_id; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - unique_id = 2; - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_PROTO_SPDY2, unique_id); - - CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - top = nghttp2_session_get_ob_pq_top(session); - CU_ASSERT(NGHTTP2_PING == OB_CTRL_TYPE(top)); - CU_ASSERT(unique_id == OB_CTRL(top)->ping.unique_id); - - session->last_ping_unique_id = 1; - frame.ping.unique_id = 1; - - CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); - CU_ASSERT(2 == user_data.ctrl_recv_cb_called); - - nghttp2_frame_ping_free(&frame.ping); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_goaway_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - int32_t stream_id = 1000000007; - user_data.ctrl_recv_cb_called = 0; - user_data.invalid_ctrl_recv_cb_called = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_goaway_init(&frame.goaway, NGHTTP2_PROTO_SPDY2, stream_id, - NGHTTP2_GOAWAY_OK); - - CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); - CU_ASSERT(1 == user_data.ctrl_recv_cb_called); - CU_ASSERT(session->goaway_flags == NGHTTP2_GOAWAY_RECV); - - nghttp2_frame_goaway_free(&frame.goaway); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_data_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_outbound_item *top; - int32_t stream_id = 2; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - stream = nghttp2_session_open_stream(session, stream_id, - NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_session_on_data_received(session, - NGHTTP2_DATA_FLAG_NONE, - 4096, stream_id)); - CU_ASSERT(0 == stream->shut_flags); - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, - NGHTTP2_DATA_FLAG_FIN, - 4096, stream_id)); - CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); - - /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ - stream_id = 4; - - nghttp2_session_open_stream(session, stream_id, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_CLOSING, NULL); - CU_ASSERT(0 == nghttp2_session_on_data_received(session, - NGHTTP2_DATA_FLAG_NONE, - 4096, stream_id)); - CU_ASSERT(NULL == nghttp2_session_get_ob_pq_top(session)); - - /* Check INVALID_STREAM case: DATA frame with stream ID which does - not exist. */ - stream_id = 6; - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, - NGHTTP2_DATA_FLAG_NONE, - 4096, stream_id)); - top = nghttp2_session_get_ob_pq_top(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(top)); - CU_ASSERT(NGHTTP2_INVALID_STREAM == OB_CTRL(top)->rst_stream.status_code); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_is_my_stream_id(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1)); - CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2)); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); - CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1)); - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_rst_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream; - nghttp2_frame frame; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - /* server push */ - nghttp2_session_open_stream(session, 2, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - nghttp2_stream_add_pushed_stream(stream, 2); - nghttp2_session_open_stream(session, 4, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - nghttp2_stream_add_pushed_stream(stream, 4); - - nghttp2_frame_rst_stream_init(&frame.rst_stream, NGHTTP2_PROTO_SPDY2, 1, - NGHTTP2_CANCEL); - - CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); - - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 2)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4)); - - nghttp2_frame_rst_stream_free(&frame.rst_stream); - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_rst_stream(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream; - nghttp2_frame *frame; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - /* server push */ - nghttp2_session_open_stream(session, 2, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - nghttp2_stream_add_pushed_stream(stream, 2); - nghttp2_session_open_stream(session, 4, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - nghttp2_stream_add_pushed_stream(stream, 4); - - frame = malloc(sizeof(nghttp2_frame)); - nghttp2_frame_rst_stream_init(&frame->rst_stream, NGHTTP2_PROTO_SPDY2, 1, - NGHTTP2_CANCEL); - nghttp2_session_add_frame(session, NGHTTP2_CTRL, frame, NULL); - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 2)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_get_next_ob_item(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - nghttp2_submit_ping(session); - CU_ASSERT(NGHTTP2_PING == - OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - - nghttp2_submit_request(session, 0, nv, NULL, NULL); - CU_ASSERT(NGHTTP2_PING == - OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - /* Incoming stream does not affect the number of outgoing max - concurrent streams. */ - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_submit_request(session, 0, nv, NULL, NULL); - CU_ASSERT(NGHTTP2_SYN_STREAM == - OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_submit_request(session, 0, nv, NULL, NULL); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 3; - - CU_ASSERT(NGHTTP2_SYN_STREAM == - OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_pop_next_ob_item(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - nghttp2_outbound_item *item; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - nghttp2_submit_ping(session); - nghttp2_submit_request(session, 1, nv, NULL, NULL); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_PING == OB_CTRL_TYPE(item)); - nghttp2_outbound_item_free(item); - free(item); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_SYN_STREAM == OB_CTRL_TYPE(item)); - nghttp2_outbound_item_free(item); - free(item); - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - - /* Incoming stream does not affect the number of outgoing max - concurrent streams. */ - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - - /* In-flight outgoing stream */ - nghttp2_session_open_stream(session, 4, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_submit_request(session, 0, nv, NULL, NULL); - nghttp2_submit_response(session, 1, nv, NULL); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_SYN_REPLY == OB_CTRL_TYPE(item)); - nghttp2_outbound_item_free(item); - free(item); - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - - session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_SYN_STREAM == OB_CTRL_TYPE(item)); - nghttp2_outbound_item_free(item); - free(item); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_request_recv_callback(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const char *nv[] = { NULL }; - nghttp2_frame frame; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_request_recv_callback = on_request_recv_callback; - user_data.stream_id = 0; - - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(0 == user_data.stream_id); - - frame.syn_stream.stream_id = 3; - frame.syn_stream.hd.flags |= NGHTTP2_CTRL_FLAG_FIN; - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(3 == user_data.stream_id); - - user_data.stream_id = 0; - - frame.syn_stream.stream_id = 0; - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(0 == user_data.stream_id); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - user_data.stream_id = 0; - - nghttp2_session_open_stream(session, 5, NGHTTP2_CTRL_FLAG_NONE, 0, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 5, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(0 == user_data.stream_id); - - frame.headers.hd.flags |= NGHTTP2_CTRL_FLAG_FIN; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(5 == user_data.stream_id); - - user_data.stream_id = 0; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame)); - CU_ASSERT(0 == user_data.stream_id); - - nghttp2_frame_headers_free(&frame.headers); - nghttp2_session_del(session); -} - -static void stream_close_callback(nghttp2_session *session, int32_t stream_id, - nghttp2_status_code status_code, - void *user_data) -{ - my_user_data* my_data = (my_user_data*)user_data; - void *stream_data = nghttp2_session_get_stream_user_data(session, stream_id); - ++my_data->stream_close_cb_called; - CU_ASSERT(stream_data != NULL); -} - -void test_nghttp2_session_on_stream_close(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream; - int32_t stream_id = 1; - uint8_t pri = 3; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_stream_close_callback = stream_close_callback; - user_data.stream_close_cb_called = 0; - - CU_ASSERT(nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &user_data) == 0); - stream = nghttp2_session_open_stream(session, stream_id, - NGHTTP2_CTRL_FLAG_NONE, - pri, NGHTTP2_STREAM_OPENED, &user_data); - CU_ASSERT(stream != NULL); - CU_ASSERT(nghttp2_session_close_stream(session, stream_id, NGHTTP2_OK) == 0); - CU_ASSERT(user_data.stream_close_cb_called == 1); - nghttp2_session_del(session); -} - -void test_nghttp2_session_max_concurrent_streams(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_frame frame; - const char *nv[] = { NULL }; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, NULL); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENED, NULL); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 3, 0, 3, dup_nv(nv)); - session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - - item = nghttp2_session_get_ob_pq_top(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_REFUSED_STREAM == OB_CTRL(item)->rst_stream.status_code) - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - nghttp2_session_del(session); -} - -static ssize_t block_count_send_callback(nghttp2_session* session, - const uint8_t *data, size_t len, - int flags, - void *user_data) -{ - my_user_data *ud = (my_user_data*)user_data; - int r; - if(ud->block_count == 0) { - r = NGHTTP2_ERR_WOULDBLOCK; - } else { - --ud->block_count; - r = len; - } - return r; -} - -void test_nghttp2_session_data_backoff_by_high_pri_frame(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.ctrl_send_cb_called = 0; - ud.data_source_length = 16*1024; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); - nghttp2_submit_request(session, 3, nv, &data_prd, NULL); - - ud.block_count = 2; - /* Sends SYN_STREAM + DATA[0] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_SYN_STREAM == ud.sent_frame_type); - /* data for DATA[1] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == 8*1024); - - nghttp2_submit_ping(session); - ud.block_count = 2; - /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); - /* data for DATA[2] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == 4*1024); - - ud.block_count = 2; - /* Sends DATA[2..3] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stop_data_with_rst_stream(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_frame frame; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - callbacks.send_callback = block_count_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.ctrl_send_cb_called = 0; - ud.data_source_length = 16*1024; - - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_submit_response(session, 1, nv, &data_prd); - - ud.block_count = 2; - /* Sends SYN_REPLY + DATA[0] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_SYN_REPLY == ud.sent_frame_type); - /* data for DATA[1] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == 8*1024); - - nghttp2_frame_rst_stream_init(&frame.rst_stream, NGHTTP2_PROTO_SPDY2, 1, - NGHTTP2_CANCEL); - CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); - nghttp2_frame_rst_stream_free(&frame.rst_stream); - - /* Big enough number to send all DATA frames potentially. */ - ud.block_count = 100; - /* Nothing will be sent in the following call. */ - CU_ASSERT(0 == nghttp2_session_send(session)); - /* With RST_STREAM, stream is canceled and further DATA on that - stream are not sent. */ - CU_ASSERT(ud.data_source_length == 8*1024); - - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - - nghttp2_session_del(session); -} - -/* - * Check that on_stream_close_callback is called when server pushed - * SYN_STREAM have NGHTTP2_CTRL_FLAG_FIN. - */ -void test_nghttp2_session_stream_close_on_syn_stream(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { NULL }; - my_user_data ud; - nghttp2_frame frame; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_stream_close_callback = - no_stream_user_data_stream_close_callback; - ud.stream_close_cb_called = 0; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_FIN | - NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, - 2, 1, 3, dup_nv(nv)); - - CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - nghttp2_session_del(session); -} - void test_nghttp2_session_recv_invalid_frame(void) { nghttp2_session *session; @@ -1827,45 +501,1296 @@ void test_nghttp2_session_recv_invalid_frame(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; callbacks.send_callback = null_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; user_data.df = &df; - user_data.ctrl_send_cb_called = 0; - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, - &user_data); - nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, - NGHTTP2_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); - framelen = nghttp2_frame_pack_syn_stream(&framedata, &framedatalen, - &nvbuf, &nvbuflen, - &frame.syn_stream, - &session->hd_deflater); + user_data.frame_send_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.headers, + &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == user_data.ctrl_send_cb_called); + CU_ASSERT(0 == user_data.frame_send_cb_called); /* Receive exactly same bytes of SYN_STREAM causes error */ scripted_data_feed_init(&df, framedata, framelen); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_send_cb_called); + CU_ASSERT(1 == user_data.frame_send_cb_called); CU_ASSERT(NGHTTP2_GOAWAY == user_data.sent_frame_type); free(framedata); free(nvbuf); - nghttp2_frame_syn_stream_free(&frame.syn_stream); + nghttp2_frame_headers_free(&frame.headers); nghttp2_session_del(session); } -static ssize_t defer_data_source_read_callback -(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t len, int *eof, - nghttp2_data_source *source, void *user_data) +void test_nghttp2_session_recv_eof(void) { - return NGHTTP2_ERR_DEFERRED; + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.recv_callback = eof_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_data(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[8092]; + int rv; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + nghttp2_frame_hd hd; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_data_recv_callback = on_data_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + /* Create DATA frame with length 4KiB */ + memset(data, 0, sizeof(data)); + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 1; + nghttp2_frame_pack_frame_hd(data, &hd); + + /* stream 1 is not opened, so it must be responded with RST_STREAM, + well actually, this is not mandated by the spec */ + ud.data_chunk_recv_cb_called = 0; + ud.data_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, 8+4096); + CU_ASSERT(8+4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.data_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Create stream 1 with CLOSING state. DATA is ignored. */ + stream = nghttp2_session_open_stream(session, 1, + NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_CLOSING, NULL); + + ud.data_chunk_recv_cb_called = 0; + ud.data_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, 8+4096); + CU_ASSERT(8+4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.data_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL == item); + + /* This is normal case. DATA is acceptable. */ + stream->state = NGHTTP2_STREAM_OPENED; + + ud.data_chunk_recv_cb_called = 0; + ud.data_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, 8+4096); + CU_ASSERT(8+4096 == rv); + + CU_ASSERT(1 == ud.data_chunk_recv_cb_called); + CU_ASSERT(1 == ud.data_recv_cb_called); + + /* Reception of DATA with stream ID = 0 causes connection error */ + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 0; + nghttp2_frame_pack_frame_hd(data, &hd); + + ud.data_chunk_recv_cb_called = 0; + ud.data_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, 8+4096); + CU_ASSERT(8+4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.data_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item)); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(item)->goaway.error_code); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_add_frame(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + my_user_data user_data; + const char *nv[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "version", "HTTP/1.1", + NULL + }; + nghttp2_frame *frame; + nghttp2_headers_aux_data *aux_data = + malloc(sizeof(nghttp2_headers_aux_data)); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + memset(aux_data, 0, sizeof(nghttp2_headers_aux_data)); + acc.length = 0; + user_data.acc = &acc; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); + + frame = malloc(sizeof(nghttp2_frame)); + nghttp2_frame_headers_init(&frame->headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + -1, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + + CU_ASSERT(0 == nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, + aux_data)); + CU_ASSERT(0 == nghttp2_pq_empty(&session->ob_ss_pq)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == acc.buf[2]); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[3]); + /* check stream id */ + CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[4])); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_syn_stream_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + nghttp2_frame frame; + nghttp2_stream *stream; + int32_t stream_id = 1; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + stream_id, 1 << 20, dup_nv(nv)); + + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + stream = nghttp2_session_get_stream(session, stream_id); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(1 << 20 == stream->pri); + + nghttp2_frame_headers_free(&frame.headers); + + /* More than max concurrent streams leads REFUSED_STREAM */ + session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 3, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers); + session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + + /* Stream ID less than or equal to the previouly received SYN_STREAM + leads to connection error */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 3, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_syn_reply_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + + CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame, + stream)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + /* Check the situation when response HEADERS is received after peer + sends FIN */ + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, NULL); + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + frame.hd.stream_id = 3; + + CU_ASSERT(0 == nghttp2_session_on_syn_reply_received(session, &frame, + stream)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); + CU_ASSERT(NGHTTP2_STREAM_CLOSED == OB_CTRL(item)->rst_stream.error_code); + + nghttp2_frame_headers_free(&frame.headers); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_headers_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + nghttp2_frame frame; + nghttp2_stream *stream; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, NULL); + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + /* stream closed */ + frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is + discarded. */ + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_CLOSING, NULL); + frame.hd.stream_id = 3; + frame.hd.flags = NGHTTP2_FLAG_NONE; + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + /* See no counters are updated */ + CU_ASSERT(2 == user_data.frame_recv_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + /* Server initiated stream */ + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + /* half closed (remote) */ + frame.hd.flags = NGHTTP2_FLAG_END_STREAM; + frame.hd.stream_id = 2; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(3 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_RD); + /* Further reception of HEADERS is subject to stream error */ + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_headers_free(&frame.headers); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_rst_stream_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_frame_rst_stream_free(&frame.rst_stream); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_settings_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream1, *stream2; + nghttp2_frame frame; + const size_t niv = 5; + nghttp2_settings_entry iv[255]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 1000000009; + + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 50; + + iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].value = 64*1024; + + iv[3].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS; + iv[3].value = 1; + + /* Unknown settings ID */ + iv[4].settings_id = 999; + iv[4].value = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_client_new(&session, &callbacks, &user_data); + session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024; + + stream1 = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + stream2 = nghttp2_session_open_stream(session, 2, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + /* Set window size for each streams and will see how settings + updates these values */ + stream1->window_size = 16*1024; + stream2->window_size = -48*1024; + + nghttp2_frame_settings_init(&frame.settings, dup_iv(iv, niv), niv); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame)); + CU_ASSERT(1000000009 == + session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(64*1024 == + session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + CU_ASSERT(1 == + session->remote_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]); + + CU_ASSERT(64*1024 == stream1->window_size); + CU_ASSERT(0 == stream2->window_size); + + frame.settings.iv[2].value = 16*1024; + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame)); + + CU_ASSERT(16*1024 == stream1->window_size); + CU_ASSERT(-48*1024 == stream2->window_size); + + nghttp2_frame_settings_free(&frame.settings); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_ping_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_outbound_item *top; + const uint8_t opaque_data[] = "01234567"; + + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_PONG, opaque_data); + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Since this ping frame has PONG flag set, no further action is + performed. */ + CU_ASSERT(NULL == nghttp2_session_get_ob_pq_top(session)); + + /* Clear the flag, and receive it again */ + frame.hd.flags = NGHTTP2_FLAG_NONE; + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + top = nghttp2_session_get_ob_pq_top(session); + CU_ASSERT(NGHTTP2_PING == OB_CTRL_TYPE(top)); + CU_ASSERT(NGHTTP2_FLAG_PONG == OB_CTRL(top)->hd.flags); + CU_ASSERT(memcmp(opaque_data, OB_CTRL(top)->ping.opaque_data, 8) == 0); + + nghttp2_frame_ping_free(&frame.ping); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_goaway_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_frame_goaway_init(&frame.goaway, 1, NGHTTP2_PROTOCOL_ERROR, NULL, 0); + + CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(session->goaway_flags == NGHTTP2_GOAWAY_RECV); + + nghttp2_frame_goaway_free(&frame.goaway); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_window_update_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *data_item; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, NULL); + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, + 1, 16*1024); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(64*1024+16*1024 == stream->window_size); + + data_item = malloc(sizeof(nghttp2_outbound_item)); + memset(data_item, 0, sizeof(nghttp2_outbound_item)); + data_item->frame_cat = NGHTTP2_CAT_DATA; + nghttp2_stream_defer_data(stream, data_item, NGHTTP2_DEFERRED_FLOW_CONTROL); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + CU_ASSERT(64*1024+16*1024*2 == stream->window_size); + CU_ASSERT(NULL == stream->deferred_data); + + nghttp2_frame_window_update_free(&frame.window_update); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_data_received(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_outbound_item *top; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_session_on_data_received(session, 4096, + NGHTTP2_FLAG_NONE, 2)); + CU_ASSERT(0 == stream->shut_flags); + CU_ASSERT(0 == nghttp2_session_on_data_received(session, 4096, + NGHTTP2_FLAG_END_STREAM, 2)); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + + /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_CLOSING, NULL); + CU_ASSERT(0 == nghttp2_session_on_data_received(session, 4096, + NGHTTP2_FLAG_NONE, 4)); + CU_ASSERT(NULL == nghttp2_session_get_ob_pq_top(session)); + + /* Check INVALID_STREAM case: DATA frame with stream ID which does + not exist. */ + CU_ASSERT(0 == nghttp2_session_on_data_received(session, 4096, + NGHTTP2_FLAG_NONE, 6)); + top = nghttp2_session_get_ob_pq_top(session); + CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(top)); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == OB_CTRL(top)->rst_stream.error_code); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_start_stream(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + nghttp2_frame *frame = malloc(sizeof(nghttp2_frame)); + nghttp2_stream *stream; + nghttp2_headers_aux_data *aux_data = + malloc(sizeof(nghttp2_headers_aux_data)); + memset(aux_data, 0, sizeof(nghttp2_headers_aux_data)); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, -1, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_reply(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + nghttp2_frame *frame = malloc(sizeof(nghttp2_frame)); + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + nghttp2_session_open_stream(session, 2, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_rst_stream(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame *frame; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + frame = malloc(sizeof(nghttp2_frame)); + nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_is_my_stream_id(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { "Content-Length", "1024", NULL }; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64*1024; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 2, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 2, nv, &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("content-length", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT(0 == nghttp2_session_send(session)); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response_without_data(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, nv, &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT(OB_CTRL(item)->hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame_with_nv_block(&frame, NGHTTP2_HEADERS, + &session->hd_inflater, + acc.buf, acc.length)); + CU_ASSERT(0 == strcmp(":version", frame.headers.nv[0])); + nghttp2_frame_headers_free(&frame.headers); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_with_data(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64*1024; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nv, + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_without_data(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nv, + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT(OB_CTRL(item)->hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame_with_nv_block(&frame, NGHTTP2_HEADERS, + &session->hd_inflater, + acc.buf, acc.length)); + CU_ASSERT(0 == strcmp(":version", frame.headers.nv[0])); + nghttp2_frame_headers_free(&frame.headers); + + nghttp2_session_del(session); +} + + +void test_nghttp2_submit_headers_start_stream(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { ":version", "HTTP/1.1", NULL }; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + -1, NGHTTP2_PRI_DEFAULT, + nv, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM) == + OB_CTRL(item)->hd.flags); + CU_ASSERT(NGHTTP2_PRI_DEFAULT == OB_CTRL(item)->headers.pri); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_reply(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { ":version", "HTTP/1.1", NULL }; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + 1, NGHTTP2_PRI_DEFAULT, + nv, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + OB_CTRL(item)->hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + 1, NGHTTP2_PRI_DEFAULT, + nv, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + nghttp2_session_del(session); +} + + +void test_nghttp2_submit_headers(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + accumulator acc; + nghttp2_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + 1, NGHTTP2_PRI_DEFAULT, + nv, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", OB_CTRL(item)->headers.nv[0])); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + OB_CTRL(item)->hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + 1, NGHTTP2_PRI_DEFAULT, + nv, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + CU_ASSERT(0 == unpack_frame_with_nv_block(&frame, + NGHTTP2_HEADERS, + &session->hd_inflater, + acc.buf, acc.length)); + CU_ASSERT(0 == strcmp(":version", frame.headers.nv[0])); + nghttp2_frame_headers_free(&frame.headers); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_settings(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry iv[3]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 50; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16*1024; + + /* This is duplicate entry */ + iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[2].value = 150; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_settings(session, iv, 3)); + + /* Make sure that local settings are not changed */ + CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == + session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == + session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + + /* Now sends without 3rd one */ + CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 2)); + + /* Make sure that local settings are changed */ + CU_ASSERT(50 == + session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(16*1024 == + session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_SETTINGS == OB_CTRL_TYPE(item)); + + frame = item->frame; + CU_ASSERT(2 == frame->settings.niv); + CU_ASSERT(50 == frame->settings.iv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == + frame->settings.iv[0].settings_id); + + CU_ASSERT(16*1024 == frame->settings.iv[1].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == + frame->settings.iv[1].settings_id); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_window_update(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 2, + NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, NULL); + stream->recv_window_size = 4096; + + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + 1024)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); + CU_ASSERT(1024 == OB_CTRL(item)->window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(3072 == stream->recv_window_size); + + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); + CU_ASSERT(4096 == OB_CTRL(item)->window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); + CU_ASSERT(4096 == OB_CTRL(item)->window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0)); + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, -1)); + CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_invalid_nv(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + + /* nghttp2_submit_request */ + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, + empty_name_nv, NULL, NULL)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, + null_val_nv, NULL, NULL)); + + /* nghttp2_submit_response */ + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_response(session, 2, empty_name_nv, NULL)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_response(session, 2, null_val_nv, NULL)); + + /* nghttp2_submit_headers */ + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, + NGHTTP2_PRI_DEFAULT, empty_name_nv, NULL)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, + NGHTTP2_PRI_DEFAULT, null_val_nv, NULL)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_next_ob_item(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + nghttp2_submit_ping(session, NULL); + CU_ASSERT(NGHTTP2_PING == + OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); + + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nv, NULL, NULL); + CU_ASSERT(NGHTTP2_PING == + OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_submit_request(session, 0, nv, NULL, NULL); + CU_ASSERT(NGHTTP2_HEADERS == + OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_submit_request(session, 0, nv, NULL, NULL); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 3; + + CU_ASSERT(NGHTTP2_HEADERS == + OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_pop_next_ob_item(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + nghttp2_outbound_item *item; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + nghttp2_submit_ping(session, NULL); + nghttp2_submit_request(session, 1, nv, NULL, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_PING == OB_CTRL_TYPE(item)); + nghttp2_outbound_item_free(item); + free(item); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == OB_CTRL_TYPE(item)); + nghttp2_outbound_item_free(item); + free(item); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + 3, NGHTTP2_STREAM_OPENING, NULL); + /* In-flight outgoing stream */ + nghttp2_session_open_stream(session, 4, NGHTTP2_FLAG_NONE, + 3, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_submit_request(session, 0, nv, NULL, NULL); + nghttp2_submit_response(session, 1, nv, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == OB_CTRL_TYPE(item)); + CU_ASSERT(1 == OB_CTRL(item)->hd.stream_id); + nghttp2_outbound_item_free(item); + free(item); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == OB_CTRL_TYPE(item)); + nghttp2_outbound_item_free(item); + free(item); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_reply_fail(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + nghttp2_data_provider data_prd; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = fail_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 4*1024; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, nv, &data_prd)); + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_max_concurrent_streams(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_frame frame; + const char *nv[] = { NULL }; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 3, + NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; + + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + + item = nghttp2_session_get_ob_pq_top(session); + CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == OB_CTRL(item)->rst_stream.error_code) + + nghttp2_frame_headers_free(&frame.headers); + nghttp2_session_del(session); +} + +/* + * Check that on_stream_close_callback is called when server pushed + * SYN_STREAM have NGHTTP2_CTRL_FLAG_FIN. + */ +void test_nghttp2_session_stream_close_on_headers_push(void) +{ + /* nghttp2_session *session; */ + /* nghttp2_session_callbacks callbacks; */ + /* const char *nv[] = { NULL }; */ + /* my_user_data ud; */ + /* nghttp2_frame frame; */ + + /* memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); */ + /* callbacks.on_stream_close_callback = */ + /* no_stream_user_data_stream_close_callback; */ + /* ud.stream_close_cb_called = 0; */ + + /* nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); */ + /* nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, */ + /* NGHTTP2_STREAM_OPENING, NULL); */ + /* nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, */ + /* NGHTTP2_CTRL_FLAG_FIN | */ + /* NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, */ + /* 2, 1, 3, dup_nv(nv)); */ + + /* CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); */ + + /* nghttp2_frame_syn_stream_free(&frame.syn_stream); */ + /* nghttp2_session_del(session); */ +} + +void test_nghttp2_session_stop_data_with_rst_stream(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = 16*1024; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_submit_response(session, 1, nv, &data_prd); + + ud.block_count = 2; + /* Sends SYN_REPLY + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 8*1024); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + + /* Big enough number to send all DATA frames potentially. */ + ud.block_count = 100; + /* Nothing will be sent in the following call. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + /* With RST_STREAM, stream is canceled and further DATA on that + stream are not sent. */ + CU_ASSERT(ud.data_source_length == 8*1024); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); } void test_nghttp2_session_defer_data(void) @@ -1878,27 +1803,28 @@ void test_nghttp2_session_defer_data(void) nghttp2_outbound_item *item; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.send_callback = block_count_send_callback; data_prd.read_callback = defer_data_source_read_callback; - ud.ctrl_send_cb_called = 0; + ud.frame_send_cb_called = 0; ud.data_source_length = 16*1024; - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING, NULL); nghttp2_submit_response(session, 1, nv, &data_prd); ud.block_count = 1; - /* Sends SYN_REPLY */ + /* Sends HEADERS reply */ CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_SYN_REPLY == ud.sent_frame_type); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); /* No data is read */ CU_ASSERT(ud.data_source_length == 16*1024); ud.block_count = 1; - nghttp2_submit_ping(session); + nghttp2_submit_ping(session, NULL); /* Sends PING */ CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); @@ -1950,22 +1876,22 @@ void test_nghttp2_session_flow_control(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; - ud.ctrl_send_cb_called = 0; + ud.frame_send_cb_called = 0; ud.data_source_length = 128*1024; /* Initial window size is 64KiB */ - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, &ud); - nghttp2_submit_request(session, 3, nv, &data_prd, NULL); + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nv, &data_prd, NULL); /* Sends 64KiB data */ CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(64*1024 == ud.data_source_length); /* Back 32KiB */ - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_PROTO_SPDY3, + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, 32*1024); nghttp2_session_on_window_update_received(session, &frame); @@ -1985,7 +1911,7 @@ void test_nghttp2_session_flow_control(void) CU_ASSERT(-48*1024 == stream->window_size); /* Back 48KiB */ - frame.window_update.delta_window_size = 48*1024; + frame.window_update.window_size_increment = 48*1024; nghttp2_session_on_window_update_received(session, &frame); /* Nothing is sent because window_size is less than 0 */ @@ -1993,7 +1919,7 @@ void test_nghttp2_session_flow_control(void) CU_ASSERT(32*1024 == ud.data_source_length); /* Back 16KiB */ - frame.window_update.delta_window_size = 16*1024; + frame.window_update.window_size_increment = 16*1024; nghttp2_session_on_window_update_received(session, &frame); /* Sends another 16KiB data */ @@ -2002,11 +1928,9 @@ void test_nghttp2_session_flow_control(void) /* Increase initial window size to 32KiB */ iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[0].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; iv[0].value = 32*1024; - nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_PROTO_SPDY3, - NGHTTP2_FLAG_SETTINGS_NONE, dup_iv(iv, 1), 1); + nghttp2_frame_settings_init(&settings_frame.settings, dup_iv(iv, 1), 1); nghttp2_session_on_settings_received(session, &settings_frame); nghttp2_frame_settings_free(&settings_frame.settings); @@ -2020,6 +1944,128 @@ void test_nghttp2_session_flow_control(void) nghttp2_session_del(session); } +void test_nghttp2_session_data_read_temporal_failure(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + nghttp2_data *data_frame; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.data_source_length = 128*1024; + + /* Initial window size is 64KiB */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, 3, nv, &data_prd, NULL); + + /* Sends 64KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64*1024 == ud.data_source_length); + + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(NULL != stream->deferred_data); + CU_ASSERT(NGHTTP2_CAT_DATA == stream->deferred_data->frame_cat); + data_frame = (nghttp2_data*)stream->deferred_data->frame; + data_frame->data_prd.read_callback = + temporal_failure_data_source_read_callback; + + /* Back 64KiB */ + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, + 1, 64*1024); + nghttp2_session_on_window_update_received(session, &frame); + nghttp2_frame_window_update_free(&frame.window_update); + + /* Sending data will fail (soft fail) and treated as stream error */ + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64*1024 == ud.data_source_length); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type); + + data_prd.read_callback = fail_data_source_read_callback; + nghttp2_submit_request(session, 3, nv, &data_prd, NULL); + /* Sending data will fail (hard fail) and session tear down */ + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_request_recv_callback(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + nghttp2_frame frame; + nghttp2_stream *stream; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_request_recv_callback = on_request_recv_callback; + user_data.stream_id = 0; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 1, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + frame.hd.stream_id = 3; + frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.stream_id); + + nghttp2_frame_headers_free(&frame.headers); + + user_data.stream_id = 0; + + stream = nghttp2_session_open_stream(session, 5, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, + 5, NGHTTP2_PRI_DEFAULT, dup_nv(nv)); + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(0 == user_data.stream_id); + + frame.headers.hd.flags |= NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(5 == user_data.stream_id); + + nghttp2_frame_headers_free(&frame.headers); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_stream_close(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_stream_close_callback = stream_close_callback; + user_data.stream_close_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, &user_data); + CU_ASSERT(stream != NULL); + CU_ASSERT(nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR) == 0); + CU_ASSERT(user_data.stream_close_cb_called == 1); + nghttp2_session_del(session); +} + void test_nghttp2_session_on_ctrl_not_send(void) { nghttp2_session *session; @@ -2029,263 +2075,90 @@ void test_nghttp2_session_on_ctrl_not_send(void) const char *nv[] = { NULL }; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; callbacks.send_callback = null_send_callback; - user_data.ctrl_not_send_cb_called = 0; + user_data.frame_not_send_cb_called = 0; user_data.not_sent_frame_type = 0; user_data.not_sent_error = 0; - CU_ASSERT(nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &user_data) == 0); - stream = nghttp2_session_open_stream(session, 1, - NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENED, &user_data); - /* Check SYN_STREAM */ - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 3, 3, - nv, NULL)); - - user_data.ctrl_not_send_cb_called = 0; - /* Associated stream closed */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_STREAM == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error); + nghttp2_session_server_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, &user_data); /* Check SYN_REPLY */ - CU_ASSERT(0 == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 1, nv)); - user_data.ctrl_not_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_REPLY == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_INVALID_STREAM_STATE == user_data.not_sent_error); - - stream->state = NGHTTP2_STREAM_OPENING; - user_data.ctrl_not_send_cb_called = 0; /* Send bogus stream ID */ CU_ASSERT(0 == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 3, nv)); + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error); - user_data.ctrl_not_send_cb_called = 0; + user_data.frame_not_send_cb_called = 0; /* Shutdown transmission */ stream->shut_flags |= NGHTTP2_SHUT_WR; CU_ASSERT(0 == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 1, nv)); + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); CU_ASSERT(NGHTTP2_ERR_STREAM_SHUT_WR == user_data.not_sent_error); stream->shut_flags = NGHTTP2_SHUT_NONE; - user_data.ctrl_not_send_cb_called = 0; + user_data.frame_not_send_cb_called = 0; /* Queue RST_STREAM */ CU_ASSERT(0 == - nghttp2_submit_syn_reply(session, NGHTTP2_CTRL_FLAG_FIN, 1, nv)); + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_submit_rst_stream(session, 1, NGHTTP2_INTERNAL_ERROR)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error); - stream = nghttp2_session_open_stream(session, 3, - NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENED, &user_data); + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENED, &user_data); /* Check HEADERS */ - user_data.ctrl_not_send_cb_called = 0; - stream->state = NGHTTP2_STREAM_OPENING; - CU_ASSERT(0 == - nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_FIN, 3, nv)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_INVALID_STREAM_STATE == user_data.not_sent_error); - - stream->state = NGHTTP2_STREAM_OPENED; - user_data.ctrl_not_send_cb_called = 0; + user_data.frame_not_send_cb_called = 0; /* Queue RST_STREAM */ CU_ASSERT(0 == - nghttp2_submit_headers(session, NGHTTP2_CTRL_FLAG_FIN, 3, nv)); + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_submit_rst_stream(session, 3, NGHTTP2_INTERNAL_ERROR)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error); nghttp2_session_del(session); /* Check SYN_STREAM */ - user_data.ctrl_not_send_cb_called = 0; - CU_ASSERT(nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, - &callbacks, &user_data) == 0); + user_data.frame_not_send_cb_called = 0; + CU_ASSERT(nghttp2_session_client_new(&session, &callbacks, &user_data) == 0); /* Maximum Stream ID is reached */ session->next_stream_id = (1u << 31)+1; - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 0, - 3, nv, NULL)); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_STREAM == user_data.not_sent_frame_type); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); CU_ASSERT(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE == user_data.not_sent_error); session->next_stream_id = 1; - user_data.ctrl_not_send_cb_called = 0; + user_data.frame_not_send_cb_called = 0; /* Send GOAWAY */ - CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_GOAWAY_OK)); - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 0, - 3, nv, NULL)); + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_NO_ERROR, NULL, 0)); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NGHTTP2_PRI_DEFAULT, nv, NULL)); CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_STREAM == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_SYN_STREAM_NOT_ALLOWED == user_data.not_sent_error); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_settings_received(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream1, *stream2; - nghttp2_frame frame; - const size_t niv = 5; - nghttp2_settings_entry iv[255]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 1000000009; - iv[0].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].value = 50; - iv[1].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[2].value = 64*1024; - iv[2].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - iv[3].settings_id = NGHTTP2_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE; - iv[3].value = 512; - iv[3].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - iv[4].settings_id = 999; - iv[4].value = 0; - iv[4].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, - &user_data); - session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024; - - stream1 = nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - stream2 = nghttp2_session_open_stream(session, 2, NGHTTP2_CTRL_FLAG_NONE, - 3, NGHTTP2_STREAM_OPENING, NULL); - stream1->window_size = 16*1024; - stream2->window_size = -48*1024; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_PROTO_SPDY3, - NGHTTP2_FLAG_SETTINGS_NONE, dup_iv(iv, niv), niv); - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame)); - - CU_ASSERT(1000000009 == - session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); - CU_ASSERT(64*1024 == - session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); - /* We limit certificate vector in reasonable size. */ - CU_ASSERT(NGHTTP2_MAX_CLIENT_CERT_VECTOR_LENGTH == - session->remote_settings - [NGHTTP2_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE]); - CU_ASSERT(NGHTTP2_MAX_CLIENT_CERT_VECTOR_LENGTH == session->cli_certvec.size); - CU_ASSERT(64*1024 == stream1->window_size); - CU_ASSERT(0 == stream2->window_size); - - frame.settings.iv[2].value = 16*1024; - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame)); - - CU_ASSERT(16*1024 == stream1->window_size); - CU_ASSERT(-48*1024 == stream2->window_size); - - nghttp2_frame_settings_free(&frame.settings); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_settings(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_settings_entry iv[3]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 50; - iv[0].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16*1024; - iv[1].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - /* This is duplicate entry */ - iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[2].value = 150; - iv[2].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - nghttp2_session_server_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, &ud); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_settings(session, NGHTTP2_FLAG_SETTINGS_NONE, - iv, 3)); - - /* Make sure that local settings are not changed */ - CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == - session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == - session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); - - CU_ASSERT(0 == nghttp2_submit_settings(session, - NGHTTP2_FLAG_SETTINGS_CLEAR_SETTINGS, - iv, 2)); - - /* Make sure that local settings are changed */ - CU_ASSERT(50 == - session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); - CU_ASSERT(16*1024 == - session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_SETTINGS == OB_CTRL_TYPE(item)); - - frame = item->frame; - CU_ASSERT(2 == frame->settings.niv); - CU_ASSERT(NGHTTP2_FLAG_SETTINGS_CLEAR_SETTINGS == frame->settings.hd.flags); - - CU_ASSERT(50 == frame->settings.iv[0].value); - CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == - frame->settings.iv[0].settings_id); - CU_ASSERT(NGHTTP2_FLAG_SETTINGS_NONE == - frame->settings.iv[0].flags); - - CU_ASSERT(16*1024 == frame->settings.iv[1].value); - CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == - frame->settings.iv[1].settings_id); - CU_ASSERT(NGHTTP2_FLAG_SETTINGS_NONE == - frame->settings.iv[1].flags); - - ud.ctrl_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.ctrl_send_cb_called); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED == user_data.not_sent_error); nghttp2_session_del(session); } @@ -2294,177 +2167,20 @@ void test_nghttp2_session_get_outbound_queue_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; - const char *nv[] = { "version", "HTTP/1.1", NULL }; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, NULL)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); CU_ASSERT(0 == nghttp2_session_get_outbound_queue_size(session)); - CU_ASSERT(0 == nghttp2_submit_syn_stream(session, NGHTTP2_CTRL_FLAG_FIN, 1, 7, - nv, NULL)); + CU_ASSERT(0 == nghttp2_submit_ping(session, NULL)); CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session)); - CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_GOAWAY_OK)); + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_NO_ERROR, NULL, 0)); CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session)); nghttp2_session_del(session); } -static ssize_t get_credential_ncerts(nghttp2_session *session, - const nghttp2_origin *origin, - void *user_data) -{ - if(strcmp("example.org", origin->host) == 0 && - strcmp("https", origin->scheme) == 0 && - 443 == origin->port) { - return 2; - } else { - return 0; - } -} - -static ssize_t get_credential_cert(nghttp2_session *session, - const nghttp2_origin *origin, - size_t idx, - uint8_t *cert, size_t certlen, - void *user_data) -{ - size_t len = strlen(origin->host); - if(certlen == 0) { - return len; - } else { - assert(certlen == len); - memcpy(cert, origin->host, len); - return 0; - } -} - -static ssize_t get_credential_proof(nghttp2_session *session, - const nghttp2_origin *origin, - uint8_t *proof, size_t prooflen, - void *uer_data) -{ - size_t len = strlen(origin->scheme); - if(prooflen == 0) { - return len; - } else { - assert(prooflen == len); - memcpy(proof, origin->scheme, len); - return 0; - } -} - -void test_nghttp2_session_prep_credential(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { ":host", "example.org", - ":scheme", "https", - NULL }; - const char *nv_nocert[] = { ":host", "nocert", - ":scheme", "https", - NULL }; - nghttp2_frame frame, *cred_frame; - nghttp2_outbound_item *item; - size_t i; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.get_credential_ncerts = get_credential_ncerts; - callbacks.get_credential_cert = get_credential_cert; - callbacks.get_credential_proof = get_credential_proof; - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, NULL)); - nghttp2_frame_syn_stream_init(&frame.syn_stream, session->version, - NGHTTP2_CTRL_FLAG_NONE, 1, 0, 0, - dup_nv(nv)); - CU_ASSERT(NGHTTP2_ERR_CREDENTIAL_PENDING == - nghttp2_session_prep_credential(session, &frame.syn_stream)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_CREDENTIAL == OB_CTRL_TYPE(item)); - CU_ASSERT(NGHTTP2_OB_PRI_CREDENTIAL == item->pri); - cred_frame = OB_CTRL(item); - CU_ASSERT(strlen("https") == cred_frame->credential.proof.length); - CU_ASSERT(memcmp("https", cred_frame->credential.proof.data, - cred_frame->credential.proof.length) == 0); - CU_ASSERT(2 == cred_frame->credential.ncerts); - for(i = 0; i < cred_frame->credential.ncerts; ++i) { - CU_ASSERT(strlen("example.org") == cred_frame->credential.certs[i].length); - CU_ASSERT(memcmp("example.org", cred_frame->credential.certs[i].data, - cred_frame->credential.certs[i].length) == 0); - } - /* Next nghttp2_session_get_next_ob_item() call returns slot index */ - CU_ASSERT(1 == nghttp2_session_prep_credential(session, &frame.syn_stream)); - - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - nghttp2_frame_syn_stream_init(&frame.syn_stream, session->version, - NGHTTP2_CTRL_FLAG_NONE, 1, 0, 0, - dup_nv(nv_nocert)); - CU_ASSERT(0 == nghttp2_session_prep_credential(session, &frame.syn_stream)); - nghttp2_frame_syn_stream_free(&frame.syn_stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_syn_stream_with_credential(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const char *nv[] = { ":host", "example.org", - ":scheme", "https", - NULL }; - my_user_data ud; - accumulator acc; - - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; - callbacks.get_credential_ncerts = get_credential_ncerts; - callbacks.get_credential_cert = get_credential_cert; - callbacks.get_credential_proof = get_credential_proof; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, &ud)); - - CU_ASSERT(0 == nghttp2_submit_request(session, 0, nv, NULL, NULL)); - - ud.block_count = 1; - ud.ctrl_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.ctrl_send_cb_called); - CU_ASSERT(NGHTTP2_CREDENTIAL == ud.sent_frame_type); - - session->callbacks.send_callback = accumulator_send_callback; - acc.length = 0; - ud.ctrl_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.ctrl_send_cb_called); - CU_ASSERT(NGHTTP2_SYN_STREAM == ud.sent_frame_type); - /* Check slot */ - CU_ASSERT(1 == acc.buf[17]); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_set_initial_client_cert_origin(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const nghttp2_origin *origin; - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, NULL); - CU_ASSERT(0 == nghttp2_session_set_initial_client_cert_origin - (session, "https", "example.org", 443)); - origin = nghttp2_session_get_client_cert_origin(session, 1); - CU_ASSERT(NULL != origin); - CU_ASSERT(strcmp("https", nghttp2_origin_get_scheme(origin)) == 0); - CU_ASSERT(strcmp("example.org", nghttp2_origin_get_host(origin)) == 0); - CU_ASSERT(443 == nghttp2_origin_get_port(origin)); - - nghttp2_session_del(session); -} - void test_nghttp2_session_set_option(void) { nghttp2_session* session; @@ -2472,7 +2188,7 @@ void test_nghttp2_session_set_option(void) int intval; char charval; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, NULL); + nghttp2_session_client_new(&session, &callbacks, NULL); intval = 1; CU_ASSERT(0 == @@ -2501,188 +2217,47 @@ void test_nghttp2_session_set_option(void) nghttp2_session_del(session); } -void test_nghttp2_submit_window_update(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - int32_t stream_id = 2; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, &ud); - stream = nghttp2_session_open_stream(session, stream_id, - NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_OPENED, NULL); - stream->recv_window_size = 4096; - - CU_ASSERT(0 == nghttp2_submit_window_update(session, stream_id, 1024)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); - CU_ASSERT(1024 == OB_CTRL(item)->window_update.delta_window_size); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(3072 == stream->recv_window_size); - - CU_ASSERT(0 == nghttp2_submit_window_update(session, stream_id, 4096)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); - CU_ASSERT(4096 == OB_CTRL(item)->window_update.delta_window_size); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == stream->recv_window_size); - - CU_ASSERT(0 == nghttp2_submit_window_update(session, stream_id, 4096)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item)); - CU_ASSERT(4096 == OB_CTRL(item)->window_update.delta_window_size); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == stream->recv_window_size); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_window_update(session, stream_id, 0)); - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_window_update(session, stream_id, -1)); - CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == - nghttp2_submit_window_update(session, 4, 4096)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_data_read_temporal_failure(void) +void test_nghttp2_session_data_backoff_by_high_pri_frame(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; const char *nv[] = { NULL }; my_user_data ud; nghttp2_data_provider data_prd; - nghttp2_frame frame; - nghttp2_data *data_frame; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.send_callback = block_count_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 128*1024; + ud.frame_send_cb_called = 0; + ud.data_source_length = 16*1024; - /* Initial window size is 64KiB */ - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, &ud); - nghttp2_submit_request(session, 3, nv, &data_prd, NULL); + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nv, &data_prd, NULL); - /* Sends 64KiB data */ + ud.block_count = 2; + /* Sends SYN_STREAM + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 8*1024); + + nghttp2_submit_ping(session, NULL); + ud.block_count = 2; + /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); + /* data for DATA[2] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 4*1024); + + ud.block_count = 2; + /* Sends DATA[2..3] */ CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(64*1024 == ud.data_source_length); stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(NULL != stream->deferred_data); - CU_ASSERT(NGHTTP2_DATA == stream->deferred_data->frame_cat); - data_frame = (nghttp2_data*)stream->deferred_data->frame; - data_frame->data_prd.read_callback = - temporal_failure_data_source_read_callback; - - /* Back 64KiB */ - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_PROTO_SPDY3, - 1, 64*1024); - nghttp2_session_on_window_update_received(session, &frame); - nghttp2_frame_window_update_free(&frame.window_update); - - /* Sending data will fail */ - ud.ctrl_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(64*1024 == ud.data_source_length); - - CU_ASSERT(1 == ud.ctrl_send_cb_called); - CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type); - - data_prd.read_callback = fail_data_source_read_callback; - nghttp2_submit_request(session, 3, nv, &data_prd, NULL); - /* Sending data will fail */ - CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_eof(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.recv_callback = eof_recv_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, - &callbacks, NULL); - - CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_data(void) -{ - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - uint8_t data[8092]; - int rv; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - callbacks.on_data_recv_callback = on_data_recv_callback; - - nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY3, &callbacks, &ud); - - /* Create DATA frame with length 4KiB */ - memset(data, 0, sizeof(data)); - nghttp2_put_uint32be(data, 1); - nghttp2_put_uint32be(data+4, 4096); - - /* stream 1 is not opened, so it must be responded with RST_STREAM */ - ud.data_chunk_recv_cb_called = 0; - ud.data_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, 8+4096); - CU_ASSERT(8+4096 == rv); - - CU_ASSERT(0 == ud.data_chunk_recv_cb_called); - CU_ASSERT(0 == ud.data_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item)); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Create stream 1 with CLOSING state. It is ignored. */ - stream = nghttp2_session_open_stream(session, 1, - NGHTTP2_CTRL_FLAG_NONE, 3, - NGHTTP2_STREAM_CLOSING, NULL); - - ud.data_chunk_recv_cb_called = 0; - ud.data_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, 8+4096); - CU_ASSERT(8+4096 == rv); - - CU_ASSERT(0 == ud.data_chunk_recv_cb_called); - CU_ASSERT(0 == ud.data_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NULL == item); - - /* This is normal case. DATA is acceptable. */ - stream->state = NGHTTP2_STREAM_OPENED; - - ud.data_chunk_recv_cb_called = 0; - ud.data_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, 8+4096); - CU_ASSERT(8+4096 == rv); - - CU_ASSERT(1 == ud.data_chunk_recv_cb_called); - CU_ASSERT(1 == ud.data_recv_cb_called); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); nghttp2_session_del(session); } diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 9bbc8238..1bbfc248 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -27,51 +27,47 @@ void test_nghttp2_session_recv(void); void test_nghttp2_session_recv_invalid_stream_id(void); -void test_nghttp2_session_add_frame(void); -void test_nghttp2_session_on_syn_stream_received(void); -void test_nghttp2_session_on_syn_stream_received_with_push(void); -void test_nghttp2_session_on_syn_reply_received(void); -void test_nghttp2_session_on_window_update_received(void); -void test_nghttp2_session_send_syn_stream(void); -void test_nghttp2_session_send_syn_reply(void); -void test_nghttp2_submit_response(void); -void test_nghttp2_submit_response_with_null_data_read_callback(void); -void test_nghttp2_submit_request_with_data(void); -void test_nghttp2_submit_request_with_null_data_read_callback(void); -void test_nghttp2_submit_syn_stream(void); -void test_nghttp2_submit_syn_reply(void); -void test_nghttp2_submit_headers(void); -void test_nghttp2_submit_invalid_nv(void); -void test_nghttp2_session_reply_fail(void); -void test_nghttp2_session_on_headers_received(void); -void test_nghttp2_session_on_ping_received(void); -void test_nghttp2_session_on_goaway_received(void); -void test_nghttp2_session_on_data_received(void); -void test_nghttp2_session_on_rst_received(void); -void test_nghttp2_session_is_my_stream_id(void); -void test_nghttp2_session_send_rst_stream(void); -void test_nghttp2_session_get_next_ob_item(void); -void test_nghttp2_session_pop_next_ob_item(void); -void test_nghttp2_session_on_request_recv_callback(void); -void test_nghttp2_session_on_stream_close(void); -void test_nghttp2_session_max_concurrent_streams(void); -void test_nghttp2_session_data_backoff_by_high_pri_frame(void); -void test_nghttp2_session_stop_data_with_rst_stream(void); -void test_nghttp2_session_stream_close_on_syn_stream(void); void test_nghttp2_session_recv_invalid_frame(void); -void test_nghttp2_session_defer_data(void); -void test_nghttp2_session_flow_control(void); -void test_nghttp2_session_on_ctrl_not_send(void); -void test_nghttp2_session_on_settings_received(void); -void test_nghttp2_submit_settings(void); -void test_nghttp2_session_get_outbound_queue_size(void); -void test_nghttp2_session_prep_credential(void); -void test_nghttp2_submit_syn_stream_with_credential(void); -void test_nghttp2_session_set_initial_client_cert_origin(void); -void test_nghttp2_session_set_option(void); -void test_nghttp2_submit_window_update(void); -void test_nghttp2_session_data_read_temporal_failure(void); void test_nghttp2_session_recv_eof(void); void test_nghttp2_session_recv_data(void); +void test_nghttp2_session_add_frame(void); +void test_nghttp2_session_on_syn_stream_received(void); +void test_nghttp2_session_on_syn_reply_received(void); +void test_nghttp2_session_on_headers_received(void); +void test_nghttp2_session_on_rst_stream_received(void); +void test_nghttp2_session_on_settings_received(void); +void test_nghttp2_session_on_ping_received(void); +void test_nghttp2_session_on_goaway_received(void); +void test_nghttp2_session_on_window_update_received(void); +void test_nghttp2_session_on_data_received(void); +void test_nghttp2_session_send_headers_start_stream(void); +void test_nghttp2_session_send_headers_reply(void); +void test_nghttp2_session_send_rst_stream(void); +void test_nghttp2_session_is_my_stream_id(void); +void test_nghttp2_submit_response(void); +void test_nghttp2_submit_response_without_data(void); +void test_nghttp2_submit_request_with_data(void); +void test_nghttp2_submit_request_without_data(void); +void test_nghttp2_submit_headers_start_stream(void); +void test_nghttp2_submit_headers_reply(void); +void test_nghttp2_submit_headers(void); +void test_nghttp2_submit_settings(void); +void test_nghttp2_submit_window_update(void); +void test_nghttp2_submit_invalid_nv(void); +void test_nghttp2_session_get_next_ob_item(void); +void test_nghttp2_session_pop_next_ob_item(void); +void test_nghttp2_session_reply_fail(void); +void test_nghttp2_session_max_concurrent_streams(void); +void test_nghttp2_session_stream_close_on_headers_push(void); +void test_nghttp2_session_stop_data_with_rst_stream(void); +void test_nghttp2_session_defer_data(void); +void test_nghttp2_session_flow_control(void); +void test_nghttp2_session_data_read_temporal_failure(void); +void test_nghttp2_session_on_request_recv_callback(void); +void test_nghttp2_session_on_stream_close(void); +void test_nghttp2_session_on_ctrl_not_send(void); +void test_nghttp2_session_get_outbound_queue_size(void); +void test_nghttp2_session_set_option(void); +void test_nghttp2_session_data_backoff_by_high_pri_frame(void); #endif /* NGHTTP2_SESSION_TEST_H */ diff --git a/tests/nghttp2_stream_test.c b/tests/nghttp2_stream_test.c index 8ce745ba..a8eb15d2 100644 --- a/tests/nghttp2_stream_test.c +++ b/tests/nghttp2_stream_test.c @@ -32,8 +32,8 @@ void test_nghttp2_stream_add_pushed_stream(void) { nghttp2_stream stream; int i, n; - nghttp2_stream_init(&stream, 1, NGHTTP2_CTRL_FLAG_NONE, 3, 65536, - NGHTTP2_STREAM_OPENING, NULL); + nghttp2_stream_init(&stream, 1, NGHTTP2_FLAG_NONE, 1 << 30, + NGHTTP2_STREAM_OPENING, 1, 1, 65536, NULL); n = 26; for(i = 2; i < n; i += 2) { CU_ASSERT(0 == nghttp2_stream_add_pushed_stream(&stream, i)); @@ -42,5 +42,6 @@ void test_nghttp2_stream_add_pushed_stream(void) for(i = 2; i < n; i += 2) { CU_ASSERT(i == stream.pushed_streams[i/2-1]); } + CU_ASSERT(1 << 30 == stream.pri); nghttp2_stream_free(&stream); } diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c index d535b911..7c68fc7c 100644 --- a/tests/nghttp2_test_helper.c +++ b/tests/nghttp2_test_helper.c @@ -28,44 +28,30 @@ #include -#include "nghttp2_session.h" +/* #include "nghttp2_session.h" */ -ssize_t unpack_frame_with_nv_block(nghttp2_frame_type type, - uint16_t version, - nghttp2_frame *frame, +ssize_t unpack_frame_with_nv_block(nghttp2_frame *frame, + nghttp2_frame_type type, nghttp2_zlib *inflater, const uint8_t *in, size_t len) { nghttp2_buffer buffer; ssize_t rv; ssize_t pnvlen; - pnvlen = nghttp2_frame_nv_offset(type, version) - NGHTTP2_HEAD_LEN; + pnvlen = nghttp2_frame_nv_offset(in); assert(pnvlen > 0); nghttp2_buffer_init(&buffer, 4096); - rv = nghttp2_zlib_inflate_hd(inflater, &buffer, - &in[NGHTTP2_HEAD_LEN + pnvlen], - len - NGHTTP2_HEAD_LEN - pnvlen); + rv = nghttp2_zlib_inflate_hd(inflater, &buffer, &in[pnvlen], len - pnvlen); if(rv < 0) { return rv; } switch(type) { - case NGHTTP2_SYN_STREAM: - rv = nghttp2_frame_unpack_syn_stream(&frame->syn_stream, - &in[0], NGHTTP2_HEAD_LEN, - &in[NGHTTP2_HEAD_LEN], pnvlen, - &buffer); - break; - case NGHTTP2_SYN_REPLY: - rv = nghttp2_frame_unpack_syn_reply(&frame->syn_reply, - &in[0], NGHTTP2_HEAD_LEN, - &in[NGHTTP2_HEAD_LEN], pnvlen, - &buffer); - break; case NGHTTP2_HEADERS: - rv = nghttp2_frame_unpack_headers(&frame->headers, - &in[0], NGHTTP2_HEAD_LEN, - &in[NGHTTP2_HEAD_LEN], pnvlen, + rv = nghttp2_frame_unpack_headers((nghttp2_headers*)frame, + &in[0], NGHTTP2_FRAME_HEAD_LENGTH, + &in[NGHTTP2_FRAME_HEAD_LENGTH], + pnvlen - NGHTTP2_FRAME_HEAD_LENGTH, &buffer); break; default: @@ -75,3 +61,12 @@ ssize_t unpack_frame_with_nv_block(nghttp2_frame_type type, nghttp2_buffer_free(&buffer); return rv; } + +char* strcopy(const char* s) +{ + size_t len = strlen(s); + char *dest = malloc(len+1); + memcpy(dest, s, len); + dest[len] = '\0'; + return dest; +} diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h index ce0663ca..e5aeea3b 100644 --- a/tests/nghttp2_test_helper.h +++ b/tests/nghttp2_test_helper.h @@ -32,10 +32,11 @@ #include "nghttp2_frame.h" #include "nghttp2_zlib.h" -ssize_t unpack_frame_with_nv_block(nghttp2_frame_type type, - uint16_t version, - nghttp2_frame *frame, +ssize_t unpack_frame_with_nv_block(nghttp2_frame *frame, + nghttp2_frame_type type, nghttp2_zlib *inflater, const uint8_t *in, size_t len); +char* strcopy(const char* s); + #endif /* NGHTTP2_TEST_HELPER_H */ diff --git a/tests/nghttp2_zlib_test.c b/tests/nghttp2_zlib_test.c index 59d039b0..476ac596 100644 --- a/tests/nghttp2_zlib_test.c +++ b/tests/nghttp2_zlib_test.c @@ -30,7 +30,7 @@ #include "nghttp2_zlib.h" -static void test_nghttp2_zlib_with(uint16_t version) +void test_nghttp2_zlib(void) { nghttp2_zlib deflater, inflater; const char msg[] = @@ -50,9 +50,8 @@ static void test_nghttp2_zlib_with(uint16_t version) ssize_t deflatebuf_len; nghttp2_buffer_init(&buf, 4096); - CU_ASSERT(0 == nghttp2_zlib_deflate_hd_init(&deflater, 1, - version)); - CU_ASSERT(0 == nghttp2_zlib_inflate_hd_init(&inflater, version)); + CU_ASSERT(0 == nghttp2_zlib_deflate_hd_init(&deflater, 1, 0)); + CU_ASSERT(0 == nghttp2_zlib_inflate_hd_init(&inflater, 0)); deflatebuf_max = nghttp2_zlib_deflate_hd_bound(&deflater, sizeof(msg)); deflatebuf = malloc(deflatebuf_max); @@ -70,13 +69,3 @@ static void test_nghttp2_zlib_with(uint16_t version) nghttp2_buffer_free(&buf); } - -void test_nghttp2_zlib_spdy2(void) -{ - test_nghttp2_zlib_with(NGHTTP2_PROTO_SPDY2); -} - -void test_nghttp2_zlib_spdy3(void) -{ - test_nghttp2_zlib_with(NGHTTP2_PROTO_SPDY3); -} diff --git a/tests/nghttp2_zlib_test.h b/tests/nghttp2_zlib_test.h index 48d4581d..ce6dc474 100644 --- a/tests/nghttp2_zlib_test.h +++ b/tests/nghttp2_zlib_test.h @@ -25,7 +25,6 @@ #ifndef NGHTTP2_ZLIB_TEST_H #define NGHTTP2_ZLIB_TEST_H -void test_nghttp2_zlib_spdy2(void); -void test_nghttp2_zlib_spdy3(void); +void test_nghttp2_zlib(void); #endif /* NGHTTP2_ZLIB_TEST_H */