2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2019 nghttp2 contributors
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "h2load_http3_session.h"
29 #include <ngtcp2/ngtcp2.h>
35 Http3Session::Http3Session(Client *client)
36 : client_(client), conn_(nullptr), npending_request_(0), reqidx_(0) {}
38 Http3Session::~Http3Session() { nghttp3_conn_del(conn_); }
40 void Http3Session::on_connect() {}
42 int Http3Session::submit_request() {
43 if (npending_request_) {
48 auto config = client_->worker->config;
49 reqidx_ = client_->reqidx;
51 if (++client_->reqidx == config->nva.size()) {
55 auto stream_id = submit_request_internal();
57 if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
68 nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
69 size_t veccnt, uint32_t *pflags, void *user_data,
70 void *stream_user_data) {
71 auto s = static_cast<Http3Session *>(user_data);
73 s->read_data(vec, veccnt, pflags);
79 void Http3Session::read_data(nghttp3_vec *vec, size_t veccnt,
83 auto config = client_->worker->config;
85 vec[0].base = config->data;
86 vec[0].len = config->data_length;
87 *pflags |= NGHTTP3_DATA_FLAG_EOF;
90 int64_t Http3Session::submit_request_internal() {
94 auto config = client_->worker->config;
95 auto &nva = config->nva[reqidx_];
97 rv = ngtcp2_conn_open_bidi_stream(client_->quic.conn, &stream_id, nullptr);
102 nghttp3_data_reader dr{};
103 dr.read_data = h2load::read_data;
105 rv = nghttp3_conn_submit_request(
106 conn_, stream_id, reinterpret_cast<nghttp3_nv *>(nva.data()), nva.size(),
107 config->data_fd == -1 ? nullptr : &dr, nullptr);
112 client_->on_request(stream_id);
113 auto req_stat = client_->get_req_stat(stream_id);
115 client_->record_request_time(req_stat);
120 int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
122 int Http3Session::on_write() { return -1; }
124 void Http3Session::terminate() {}
126 size_t Http3Session::max_concurrent_streams() {
127 return (size_t)client_->worker->config->max_concurrent_streams;
131 int stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
132 void *user_data, void *stream_user_data) {
133 auto s = static_cast<Http3Session *>(user_data);
134 if (s->stream_close(stream_id, app_error_code) != 0) {
135 return NGHTTP3_ERR_CALLBACK_FAILURE;
141 int Http3Session::stream_close(int64_t stream_id, uint64_t app_error_code) {
142 if (!ngtcp2_is_bidi_stream(stream_id)) {
143 assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
144 ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
146 client_->on_stream_close(stream_id, app_error_code == NGHTTP3_H3_NO_ERROR);
151 int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
152 size_t datalen, void *user_data, void *stream_user_data) {
153 auto s = static_cast<Http3Session *>(user_data);
154 s->recv_data(stream_id, data, datalen);
159 void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
161 client_->record_ttfb();
162 client_->worker->stats.bytes_body += datalen;
163 consume(stream_id, datalen);
167 int deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed,
168 void *user_data, void *stream_user_data) {
169 auto s = static_cast<Http3Session *>(user_data);
170 s->consume(stream_id, nconsumed);
175 void Http3Session::consume(int64_t stream_id, size_t nconsumed) {
176 ngtcp2_conn_extend_max_stream_offset(client_->quic.conn, stream_id,
178 ngtcp2_conn_extend_max_offset(client_->quic.conn, nconsumed);
182 int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
183 void *stream_user_data) {
184 auto s = static_cast<Http3Session *>(user_data);
185 s->begin_headers(stream_id);
190 void Http3Session::begin_headers(int64_t stream_id) {
191 auto payloadlen = nghttp3_conn_get_frame_payload_left(conn_, stream_id);
192 assert(payloadlen > 0);
194 client_->worker->stats.bytes_head += payloadlen;
198 int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
199 nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
200 void *user_data, void *stream_user_data) {
201 auto s = static_cast<Http3Session *>(user_data);
202 auto k = nghttp3_rcbuf_get_buf(name);
203 auto v = nghttp3_rcbuf_get_buf(value);
204 s->recv_header(stream_id, &k, &v);
209 void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name,
210 const nghttp3_vec *value) {
211 client_->on_header(stream_id, name->base, name->len, value->base, value->len);
212 client_->worker->stats.bytes_head_decomp += name->len + value->len;
216 int send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
217 uint64_t app_error_code, void *user_data,
218 void *stream_user_data) {
219 auto s = static_cast<Http3Session *>(user_data);
220 if (s->send_stop_sending(stream_id, app_error_code) != 0) {
221 return NGHTTP3_ERR_CALLBACK_FAILURE;
227 int Http3Session::send_stop_sending(int64_t stream_id,
228 uint64_t app_error_code) {
229 auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, stream_id,
232 std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
239 int Http3Session::close_stream(int64_t stream_id, uint64_t app_error_code) {
240 auto rv = nghttp3_conn_close_stream(conn_, stream_id, app_error_code);
244 case NGHTTP3_ERR_STREAM_NOT_FOUND:
245 if (!ngtcp2_is_bidi_stream(stream_id)) {
246 assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
247 ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
255 int Http3Session::shutdown_stream_read(int64_t stream_id) {
256 auto rv = nghttp3_conn_shutdown_stream_read(conn_, stream_id);
263 int Http3Session::extend_max_local_streams() {
264 auto config = client_->worker->config;
266 for (; npending_request_; --npending_request_) {
267 auto stream_id = submit_request_internal();
269 if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
275 if (++reqidx_ == config->nva.size()) {
283 int Http3Session::init_conn() {
286 assert(conn_ == nullptr);
288 if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
292 nghttp3_callbacks callbacks{
293 nullptr, // acked_stream_data
294 h2load::stream_close,
296 h2load::deferred_consume,
297 h2load::begin_headers,
299 nullptr, // end_headers
300 nullptr, // begin_trailers
302 nullptr, // end_trailers
303 h2load::send_stop_sending,
306 auto config = client_->worker->config;
308 nghttp3_settings settings;
309 nghttp3_settings_default(&settings);
310 settings.qpack_max_table_capacity = config->header_table_size;
311 settings.qpack_blocked_streams = 100;
313 auto mem = nghttp3_mem_default();
315 rv = nghttp3_conn_client_new(&conn_, &callbacks, &settings, mem, this);
317 std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv)
322 int64_t ctrl_stream_id;
324 rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &ctrl_stream_id, NULL);
326 std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
331 rv = nghttp3_conn_bind_control_stream(conn_, ctrl_stream_id);
333 std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
338 int64_t qpack_enc_stream_id, qpack_dec_stream_id;
340 rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_enc_stream_id,
343 std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
348 rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_dec_stream_id,
351 std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
356 rv = nghttp3_conn_bind_qpack_streams(conn_, qpack_enc_stream_id,
357 qpack_dec_stream_id);
359 std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
367 ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
368 const uint8_t *data, size_t datalen) {
369 auto nconsumed = nghttp3_conn_read_stream(
370 conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
372 std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
374 client_->quic.last_error = quic::err_application(nconsumed);
380 ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin,
381 nghttp3_vec *vec, size_t veccnt) {
383 nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
385 client_->quic.last_error = quic::err_application(sveccnt);
391 int Http3Session::block_stream(int64_t stream_id) {
392 auto rv = nghttp3_conn_block_stream(conn_, stream_id);
394 client_->quic.last_error = quic::err_application(rv);
400 int Http3Session::shutdown_stream_write(int64_t stream_id) {
401 auto rv = nghttp3_conn_shutdown_stream_write(conn_, stream_id);
403 client_->quic.last_error = quic::err_application(rv);
409 int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
410 auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
412 client_->quic.last_error = quic::err_application(rv);
418 int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
419 auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
421 client_->quic.last_error = quic::err_application(rv);
427 } // namespace h2load