Merge branch 'trailer'
This commit is contained in:
commit
5b5034f19c
|
@ -682,7 +682,14 @@ typedef enum {
|
||||||
/**
|
/**
|
||||||
* Indicates EOF was sensed.
|
* Indicates EOF was sensed.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_DATA_FLAG_EOF = 0x01
|
NGHTTP2_DATA_FLAG_EOF = 0x01,
|
||||||
|
/**
|
||||||
|
* Indicates that END_STREAM flag must not be set even if
|
||||||
|
* NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send
|
||||||
|
* trailer header fields with `nghttp2_submit_request()` or
|
||||||
|
* `nghttp2_submit_response()`.
|
||||||
|
*/
|
||||||
|
NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02,
|
||||||
} nghttp2_data_flag;
|
} nghttp2_data_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -695,6 +702,21 @@ typedef enum {
|
||||||
* them in |buf| and return number of data stored in |buf|. If EOF is
|
* them in |buf| and return number of data stored in |buf|. If EOF is
|
||||||
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
|
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
|
||||||
*
|
*
|
||||||
|
* If this callback is set by `nghttp2_submit_request()`,
|
||||||
|
* `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
|
||||||
|
* `nghttp2_submit_data()` with flag parameter
|
||||||
|
* :enum:`NGHTTP2_FLAG_END_STREAM` set, and
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
|
||||||
|
* frame will have END_STREAM flag set. Usually, this is expected
|
||||||
|
* behaviour and all are fine. One exception is send trailer header
|
||||||
|
* fields. You cannot send trailers after sending frame with
|
||||||
|
* END_STREAM set. To avoid this problem, one can set
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
|
||||||
|
* END_STREAM in DATA frame. Then application can use
|
||||||
|
* `nghttp2_submit_trailer()` to send trailers.
|
||||||
|
* `nghttp2_submit_trailer()` can be called inside this callback.
|
||||||
|
*
|
||||||
* If the application wants to postpone DATA frames (e.g.,
|
* If the application wants to postpone DATA frames (e.g.,
|
||||||
* asynchronous I/O, or reading data blocks for long time), it is
|
* asynchronous I/O, or reading data blocks for long time), it is
|
||||||
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
|
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
|
||||||
|
@ -2834,6 +2856,53 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
const nghttp2_data_provider *data_prd);
|
const nghttp2_data_provider *data_prd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Submits trailer HEADERS against the stream |stream_id|.
|
||||||
|
*
|
||||||
|
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
|
||||||
|
* |nvlen| elements. The application is responsible not to include
|
||||||
|
* required pseudo-header fields (header field whose name starts with
|
||||||
|
* ":") in |nva|.
|
||||||
|
*
|
||||||
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
|
* |nva| is preserved.
|
||||||
|
*
|
||||||
|
* For server, trailer must be followed by response HEADERS or
|
||||||
|
* response DATA. The library does not check that response HEADERS
|
||||||
|
* has already sent and if `nghttp2_submit_trailer()` is called before
|
||||||
|
* any response HEADERS submission (usually by
|
||||||
|
* `nghttp2_submit_response()`), the content of |nva| will be sent as
|
||||||
|
* reponse headers, which will result in error.
|
||||||
|
*
|
||||||
|
* This function has the same effect with `nghttp2_submit_headers()`,
|
||||||
|
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
|
||||||
|
* stream_user_data to NULL.
|
||||||
|
*
|
||||||
|
* To submit trailer after `nghttp2_submit_response()` is called, the
|
||||||
|
* application has to specify :type:`nghttp2_data_provider` to
|
||||||
|
* `nghttp2_submit_response()`. In side
|
||||||
|
* :type:`nghttp2_data_source_read_callback`, when setting
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the
|
||||||
|
* application can send trailer using `nghttp2_submit_trailer()`.
|
||||||
|
* `nghttp2_submit_trailer()` can be used inside
|
||||||
|
* :type:`nghttp2_data_source_read_callback`.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds and |stream_id| is -1.
|
||||||
|
* Otherwise, 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_ARGUMENT`
|
||||||
|
* The |stream_id| is 0.
|
||||||
|
*/
|
||||||
|
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||||
|
const nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
|
|
@ -6222,7 +6222,10 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||||
|
|
||||||
if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
|
if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
|
||||||
aux_data->eof = 1;
|
aux_data->eof = 1;
|
||||||
if (aux_data->flags & NGHTTP2_FLAG_END_STREAM) {
|
/* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
|
||||||
|
NGHTTP2_FLAG_END_STREAM */
|
||||||
|
if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
|
||||||
|
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
|
||||||
frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
|
frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,12 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
|
||||||
attach_stream);
|
attach_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||||
|
const nghttp2_nv *nva, size_t nvlen) {
|
||||||
|
return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
|
||||||
|
NULL, nva, nvlen, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
|
|
|
@ -809,6 +809,7 @@ void Http2Handler::terminate_session(uint32_t error_code) {
|
||||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data) {
|
nghttp2_data_source *source, void *user_data) {
|
||||||
|
int rv;
|
||||||
auto hd = static_cast<Http2Handler *>(user_data);
|
auto hd = static_cast<Http2Handler *>(user_data);
|
||||||
auto stream = hd->get_stream(stream_id);
|
auto stream = hd->get_stream(stream_id);
|
||||||
|
|
||||||
|
@ -829,6 +830,20 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
if (nread == 0 || stream->body_left <= 0) {
|
if (nread == 0 || stream->body_left <= 0) {
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
|
|
||||||
|
auto config = hd->get_config();
|
||||||
|
if (!config->trailer.empty()) {
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
std::vector<nghttp2_nv> nva;
|
||||||
|
nva.reserve(config->trailer.size());
|
||||||
|
for (auto &kv : config->trailer) {
|
||||||
|
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||||
|
}
|
||||||
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||||
|
if (rv != 0) {
|
||||||
|
*data_flags &= ~NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
||||||
remove_stream_read_timeout(stream);
|
remove_stream_read_timeout(stream);
|
||||||
remove_stream_write_timeout(stream);
|
remove_stream_write_timeout(stream);
|
||||||
|
|
|
@ -50,6 +50,7 @@ namespace nghttp2 {
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
std::map<std::string, std::vector<std::string>> push;
|
std::map<std::string, std::vector<std::string>> push;
|
||||||
|
Headers trailer;
|
||||||
std::string htdocs;
|
std::string htdocs;
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string private_key_file;
|
std::string private_key_file;
|
||||||
|
|
|
@ -2110,6 +2110,7 @@ namespace {
|
||||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data) {
|
nghttp2_data_source *source, void *user_data) {
|
||||||
|
int rv;
|
||||||
auto req = static_cast<Request *>(
|
auto req = static_cast<Request *>(
|
||||||
nghttp2_session_get_stream_user_data(session, stream_id));
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||||
assert(req);
|
assert(req);
|
||||||
|
@ -2126,6 +2127,18 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
|
|
||||||
if (nread == 0) {
|
if (nread == 0) {
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
|
if (!config.trailer.empty()) {
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
std::vector<nghttp2_nv> nva;
|
||||||
|
nva.reserve(config.trailer.size());
|
||||||
|
for (auto &kv : config.trailer) {
|
||||||
|
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||||
|
}
|
||||||
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||||
|
if (rv != 0) {
|
||||||
|
*data_flags &= ~NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
req->data_offset += nread;
|
req->data_offset += nread;
|
||||||
}
|
}
|
||||||
|
@ -2347,6 +2360,11 @@ Options:
|
||||||
-s, --stat Print statistics.
|
-s, --stat Print statistics.
|
||||||
-H, --header=<HEADER>
|
-H, --header=<HEADER>
|
||||||
Add a header to the requests. Example: -H':method: PUT'
|
Add a header to the requests. Example: -H':method: PUT'
|
||||||
|
--trailer=<HEADER>
|
||||||
|
Add a trailer header to the requests. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). To send trailer, one must use -d option to
|
||||||
|
send request body. Example: --trailer 'foo: bar'.
|
||||||
--cert=<CERT>
|
--cert=<CERT>
|
||||||
Use the specified client certificate file. The file
|
Use the specified client certificate file. The file
|
||||||
must be in PEM format.
|
must be in PEM format.
|
||||||
|
@ -2425,6 +2443,7 @@ int main(int argc, char **argv) {
|
||||||
{"no-content-length", no_argument, &flag, 6},
|
{"no-content-length", no_argument, &flag, 6},
|
||||||
{"no-dep", no_argument, &flag, 7},
|
{"no-dep", no_argument, &flag, 7},
|
||||||
{"dep-idle", no_argument, &flag, 8},
|
{"dep-idle", no_argument, &flag, 8},
|
||||||
|
{"trailer", required_argument, &flag, 9},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
|
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
|
||||||
|
@ -2587,6 +2606,30 @@ int main(int argc, char **argv) {
|
||||||
// dep-idle option
|
// dep-idle option
|
||||||
config.dep_idle = true;
|
config.dep_idle = true;
|
||||||
break;
|
break;
|
||||||
|
case 9: {
|
||||||
|
// trailer option
|
||||||
|
auto header = optarg;
|
||||||
|
auto value = strchr(optarg, ':');
|
||||||
|
if (!value) {
|
||||||
|
std::cerr << "--trailer: invalid header: " << optarg << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
*value = 0;
|
||||||
|
value++;
|
||||||
|
while (isspace(*value)) {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
if (*value == 0) {
|
||||||
|
// This could also be a valid case for suppressing a header
|
||||||
|
// similar to curl
|
||||||
|
std::cerr << "--trailer: invalid header - value missing: " << optarg
|
||||||
|
<< std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
config.trailer.emplace_back(header, value, false);
|
||||||
|
util::inp_strlower(config.trailer.back().name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -58,6 +58,7 @@ struct Config {
|
||||||
~Config();
|
~Config();
|
||||||
|
|
||||||
Headers headers;
|
Headers headers;
|
||||||
|
Headers trailer;
|
||||||
std::string certfile;
|
std::string certfile;
|
||||||
std::string keyfile;
|
std::string keyfile;
|
||||||
std::string datafile;
|
std::string datafile;
|
||||||
|
|
|
@ -140,6 +140,11 @@ Options:
|
||||||
--early-response
|
--early-response
|
||||||
Start sending response when request HEADERS is received,
|
Start sending response when request HEADERS is received,
|
||||||
rather than complete request is received.
|
rather than complete request is received.
|
||||||
|
--trailer=<HEADER>
|
||||||
|
Add a trailer header to a response. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). The trailer is sent only if a response has
|
||||||
|
body part. Example: --trailer 'foo: bar'.
|
||||||
--version Display version information and exit.
|
--version Display version information and exit.
|
||||||
-h, --help Display this help and exit.
|
-h, --help Display this help and exit.
|
||||||
|
|
||||||
|
@ -170,6 +175,7 @@ int main(int argc, char **argv) {
|
||||||
{"version", no_argument, &flag, 3},
|
{"version", no_argument, &flag, 3},
|
||||||
{"dh-param-file", required_argument, &flag, 4},
|
{"dh-param-file", required_argument, &flag, 4},
|
||||||
{"early-response", no_argument, &flag, 5},
|
{"early-response", no_argument, &flag, 5},
|
||||||
|
{"trailer", required_argument, &flag, 6},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options,
|
int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options,
|
||||||
|
@ -254,6 +260,30 @@ int main(int argc, char **argv) {
|
||||||
// early-response
|
// early-response
|
||||||
config.early_response = true;
|
config.early_response = true;
|
||||||
break;
|
break;
|
||||||
|
case 6: {
|
||||||
|
// trailer option
|
||||||
|
auto header = optarg;
|
||||||
|
auto value = strchr(optarg, ':');
|
||||||
|
if (!value) {
|
||||||
|
std::cerr << "--trailer: invalid header: " << optarg << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
*value = 0;
|
||||||
|
value++;
|
||||||
|
while (isspace(*value)) {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
if (*value == 0) {
|
||||||
|
// This could also be a valid case for suppressing a header
|
||||||
|
// similar to curl
|
||||||
|
std::cerr << "--trailer: invalid header - value missing: " << optarg
|
||||||
|
<< std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
config.trailer.emplace_back(header, value, false);
|
||||||
|
util::inp_strlower(config.trailer.back().name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -154,6 +154,7 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_submit_response_with_data) ||
|
test_nghttp2_submit_response_with_data) ||
|
||||||
!CU_add_test(pSuite, "submit_response_without_data",
|
!CU_add_test(pSuite, "submit_response_without_data",
|
||||||
test_nghttp2_submit_response_without_data) ||
|
test_nghttp2_submit_response_without_data) ||
|
||||||
|
!CU_add_test(pSuite, "submit_trailer", test_nghttp2_submit_trailer) ||
|
||||||
!CU_add_test(pSuite, "submit_headers_start_stream",
|
!CU_add_test(pSuite, "submit_headers_start_stream",
|
||||||
test_nghttp2_submit_headers_start_stream) ||
|
test_nghttp2_submit_headers_start_stream) ||
|
||||||
!CU_add_test(pSuite, "submit_headers_reply",
|
!CU_add_test(pSuite, "submit_headers_reply",
|
||||||
|
|
|
@ -88,6 +88,13 @@ static const nghttp2_nv resnv[] = {
|
||||||
MAKE_NV(":status", "200"),
|
MAKE_NV(":status", "200"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const nghttp2_nv trailernv[] = {
|
||||||
|
// from http://tools.ietf.org/html/rfc6249#section-7
|
||||||
|
MAKE_NV("digest", "SHA-256="
|
||||||
|
"MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz"
|
||||||
|
"YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="),
|
||||||
|
};
|
||||||
|
|
||||||
static void scripted_data_feed_init2(scripted_data_feed *df,
|
static void scripted_data_feed_init2(scripted_data_feed *df,
|
||||||
nghttp2_bufs *bufs) {
|
nghttp2_bufs *bufs) {
|
||||||
nghttp2_buf_chain *ci;
|
nghttp2_buf_chain *ci;
|
||||||
|
@ -279,6 +286,14 @@ static ssize_t fail_data_source_read_callback(nghttp2_session *session _U_,
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t no_end_stream_data_source_read_callback(
|
||||||
|
nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_,
|
||||||
|
size_t len _U_, uint32_t *data_flags, nghttp2_data_source *source _U_,
|
||||||
|
void *user_data _U_) {
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* static void no_stream_user_data_stream_close_callback */
|
/* static void no_stream_user_data_stream_close_callback */
|
||||||
/* (nghttp2_session *session, */
|
/* (nghttp2_session *session, */
|
||||||
/* int32_t stream_id, */
|
/* int32_t stream_id, */
|
||||||
|
@ -3472,6 +3487,63 @@ void test_nghttp2_submit_response_without_data(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_submit_trailer(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
accumulator acc;
|
||||||
|
nghttp2_data_provider data_prd;
|
||||||
|
nghttp2_outbound_item *item;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_frame frame;
|
||||||
|
nghttp2_hd_inflater inflater;
|
||||||
|
nva_out out;
|
||||||
|
nghttp2_bufs bufs;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
frame_pack_bufs_init(&bufs);
|
||||||
|
|
||||||
|
data_prd.read_callback = no_end_stream_data_source_read_callback;
|
||||||
|
nva_out_init(&out);
|
||||||
|
acc.length = 0;
|
||||||
|
ud.acc = &acc;
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
callbacks.send_callback = null_send_callback;
|
||||||
|
CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
|
||||||
|
|
||||||
|
nghttp2_hd_inflate_init(&inflater, mem);
|
||||||
|
nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM,
|
||||||
|
&pri_spec_default, NGHTTP2_STREAM_OPENING, NULL);
|
||||||
|
CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv),
|
||||||
|
&data_prd));
|
||||||
|
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||||
|
|
||||||
|
CU_ASSERT(0 ==
|
||||||
|
nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv)));
|
||||||
|
|
||||||
|
session->callbacks.send_callback = accumulator_send_callback;
|
||||||
|
|
||||||
|
item = nghttp2_session_get_next_ob_item(session);
|
||||||
|
CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
|
||||||
|
CU_ASSERT(NGHTTP2_HCAT_HEADERS == item->frame.headers.cat);
|
||||||
|
CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||||
|
CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length));
|
||||||
|
|
||||||
|
nghttp2_bufs_add(&bufs, acc.buf, acc.length);
|
||||||
|
inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem);
|
||||||
|
|
||||||
|
CU_ASSERT(ARRLEN(trailernv) == out.nvlen);
|
||||||
|
assert_nv_equal(trailernv, out.nva, out.nvlen, mem);
|
||||||
|
|
||||||
|
nva_out_reset(&out, mem);
|
||||||
|
nghttp2_bufs_free(&bufs);
|
||||||
|
nghttp2_frame_headers_free(&frame.headers, mem);
|
||||||
|
nghttp2_hd_inflate_free(&inflater);
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_submit_headers_start_stream(void) {
|
void test_nghttp2_submit_headers_start_stream(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
|
|
@ -69,6 +69,7 @@ void test_nghttp2_submit_request_with_data(void);
|
||||||
void test_nghttp2_submit_request_without_data(void);
|
void test_nghttp2_submit_request_without_data(void);
|
||||||
void test_nghttp2_submit_response_with_data(void);
|
void test_nghttp2_submit_response_with_data(void);
|
||||||
void test_nghttp2_submit_response_without_data(void);
|
void test_nghttp2_submit_response_without_data(void);
|
||||||
|
void test_nghttp2_submit_trailer(void);
|
||||||
void test_nghttp2_submit_headers_start_stream(void);
|
void test_nghttp2_submit_headers_start_stream(void);
|
||||||
void test_nghttp2_submit_headers_reply(void);
|
void test_nghttp2_submit_headers_reply(void);
|
||||||
void test_nghttp2_submit_headers_push_reply(void);
|
void test_nghttp2_submit_headers_push_reply(void);
|
||||||
|
|
Loading…
Reference in New Issue