12c593315Sopenharmony_ci/* 22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library 32c593315Sopenharmony_ci * 42c593315Sopenharmony_ci * Copyright (c) 2014 Tatsuhiro Tsujikawa 52c593315Sopenharmony_ci * 62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the 82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 122c593315Sopenharmony_ci * the following conditions: 132c593315Sopenharmony_ci * 142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be 152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software. 162c593315Sopenharmony_ci * 172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 242c593315Sopenharmony_ci */ 252c593315Sopenharmony_ci#include "h2load_http2_session.h" 262c593315Sopenharmony_ci 272c593315Sopenharmony_ci#include <cassert> 282c593315Sopenharmony_ci#include <cerrno> 292c593315Sopenharmony_ci#include <iostream> 302c593315Sopenharmony_ci 312c593315Sopenharmony_ci#include "h2load.h" 322c593315Sopenharmony_ci#include "util.h" 332c593315Sopenharmony_ci#include "template.h" 342c593315Sopenharmony_ci 352c593315Sopenharmony_ciusing namespace nghttp2; 362c593315Sopenharmony_ci 372c593315Sopenharmony_cinamespace h2load { 382c593315Sopenharmony_ci 392c593315Sopenharmony_ciHttp2Session::Http2Session(Client *client) 402c593315Sopenharmony_ci : client_(client), session_(nullptr) {} 412c593315Sopenharmony_ci 422c593315Sopenharmony_ciHttp2Session::~Http2Session() { nghttp2_session_del(session_); } 432c593315Sopenharmony_ci 442c593315Sopenharmony_cinamespace { 452c593315Sopenharmony_ciint on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, 462c593315Sopenharmony_ci const uint8_t *name, size_t namelen, 472c593315Sopenharmony_ci const uint8_t *value, size_t valuelen, uint8_t flags, 482c593315Sopenharmony_ci void *user_data) { 492c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 502c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 512c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { 522c593315Sopenharmony_ci return 0; 532c593315Sopenharmony_ci } 542c593315Sopenharmony_ci client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); 552c593315Sopenharmony_ci client->worker->stats.bytes_head_decomp += namelen + valuelen; 562c593315Sopenharmony_ci 572c593315Sopenharmony_ci if (client->worker->config->verbose) { 582c593315Sopenharmony_ci std::cout << "[stream_id=" << frame->hd.stream_id << "] "; 592c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(name), namelen); 602c593315Sopenharmony_ci std::cout << ": "; 612c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(value), valuelen); 622c593315Sopenharmony_ci std::cout << "\n"; 632c593315Sopenharmony_ci } 642c593315Sopenharmony_ci 652c593315Sopenharmony_ci return 0; 662c593315Sopenharmony_ci} 672c593315Sopenharmony_ci} // namespace 682c593315Sopenharmony_ci 692c593315Sopenharmony_cinamespace { 702c593315Sopenharmony_ciint on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, 712c593315Sopenharmony_ci void *user_data) { 722c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 732c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 742c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { 752c593315Sopenharmony_ci return 0; 762c593315Sopenharmony_ci } 772c593315Sopenharmony_ci client->worker->stats.bytes_head += 782c593315Sopenharmony_ci frame->hd.length - frame->headers.padlen - 792c593315Sopenharmony_ci ((frame->hd.flags & NGHTTP2_FLAG_PRIORITY) ? 5 : 0); 802c593315Sopenharmony_ci if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { 812c593315Sopenharmony_ci client->record_ttfb(); 822c593315Sopenharmony_ci } 832c593315Sopenharmony_ci return 0; 842c593315Sopenharmony_ci} 852c593315Sopenharmony_ci} // namespace 862c593315Sopenharmony_ci 872c593315Sopenharmony_cinamespace { 882c593315Sopenharmony_ciint on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, 892c593315Sopenharmony_ci int32_t stream_id, const uint8_t *data, 902c593315Sopenharmony_ci size_t len, void *user_data) { 912c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 922c593315Sopenharmony_ci client->record_ttfb(); 932c593315Sopenharmony_ci client->worker->stats.bytes_body += len; 942c593315Sopenharmony_ci return 0; 952c593315Sopenharmony_ci} 962c593315Sopenharmony_ci} // namespace 972c593315Sopenharmony_ci 982c593315Sopenharmony_cinamespace { 992c593315Sopenharmony_ciint on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 1002c593315Sopenharmony_ci uint32_t error_code, void *user_data) { 1012c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 1022c593315Sopenharmony_ci client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR); 1032c593315Sopenharmony_ci 1042c593315Sopenharmony_ci return 0; 1052c593315Sopenharmony_ci} 1062c593315Sopenharmony_ci} // namespace 1072c593315Sopenharmony_ci 1082c593315Sopenharmony_cinamespace { 1092c593315Sopenharmony_ciint before_frame_send_callback(nghttp2_session *session, 1102c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 1112c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 1122c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_REQUEST) { 1132c593315Sopenharmony_ci return 0; 1142c593315Sopenharmony_ci } 1152c593315Sopenharmony_ci 1162c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 1172c593315Sopenharmony_ci auto req_stat = client->get_req_stat(frame->hd.stream_id); 1182c593315Sopenharmony_ci assert(req_stat); 1192c593315Sopenharmony_ci client->record_request_time(req_stat); 1202c593315Sopenharmony_ci 1212c593315Sopenharmony_ci return 0; 1222c593315Sopenharmony_ci} 1232c593315Sopenharmony_ci} // namespace 1242c593315Sopenharmony_ci 1252c593315Sopenharmony_cinamespace { 1262c593315Sopenharmony_cissize_t file_read_callback(nghttp2_session *session, int32_t stream_id, 1272c593315Sopenharmony_ci uint8_t *buf, size_t length, uint32_t *data_flags, 1282c593315Sopenharmony_ci nghttp2_data_source *source, void *user_data) { 1292c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 1302c593315Sopenharmony_ci auto config = client->worker->config; 1312c593315Sopenharmony_ci auto req_stat = client->get_req_stat(stream_id); 1322c593315Sopenharmony_ci assert(req_stat); 1332c593315Sopenharmony_ci ssize_t nread; 1342c593315Sopenharmony_ci while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == 1352c593315Sopenharmony_ci -1 && 1362c593315Sopenharmony_ci errno == EINTR) 1372c593315Sopenharmony_ci ; 1382c593315Sopenharmony_ci 1392c593315Sopenharmony_ci if (nread == -1) { 1402c593315Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 1412c593315Sopenharmony_ci } 1422c593315Sopenharmony_ci 1432c593315Sopenharmony_ci req_stat->data_offset += nread; 1442c593315Sopenharmony_ci 1452c593315Sopenharmony_ci if (req_stat->data_offset == config->data_length) { 1462c593315Sopenharmony_ci *data_flags |= NGHTTP2_DATA_FLAG_EOF; 1472c593315Sopenharmony_ci return nread; 1482c593315Sopenharmony_ci } 1492c593315Sopenharmony_ci 1502c593315Sopenharmony_ci if (req_stat->data_offset > config->data_length || nread == 0) { 1512c593315Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 1522c593315Sopenharmony_ci } 1532c593315Sopenharmony_ci 1542c593315Sopenharmony_ci return nread; 1552c593315Sopenharmony_ci} 1562c593315Sopenharmony_ci 1572c593315Sopenharmony_ci} // namespace 1582c593315Sopenharmony_ci 1592c593315Sopenharmony_cinamespace { 1602c593315Sopenharmony_cissize_t send_callback(nghttp2_session *session, const uint8_t *data, 1612c593315Sopenharmony_ci size_t length, int flags, void *user_data) { 1622c593315Sopenharmony_ci auto client = static_cast<Client *>(user_data); 1632c593315Sopenharmony_ci auto &wb = client->wb; 1642c593315Sopenharmony_ci 1652c593315Sopenharmony_ci if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) { 1662c593315Sopenharmony_ci return NGHTTP2_ERR_WOULDBLOCK; 1672c593315Sopenharmony_ci } 1682c593315Sopenharmony_ci 1692c593315Sopenharmony_ci return wb.append(data, length); 1702c593315Sopenharmony_ci} 1712c593315Sopenharmony_ci} // namespace 1722c593315Sopenharmony_ci 1732c593315Sopenharmony_civoid Http2Session::on_connect() { 1742c593315Sopenharmony_ci int rv; 1752c593315Sopenharmony_ci 1762c593315Sopenharmony_ci // This is required with --disable-assert. 1772c593315Sopenharmony_ci (void)rv; 1782c593315Sopenharmony_ci 1792c593315Sopenharmony_ci nghttp2_session_callbacks *callbacks; 1802c593315Sopenharmony_ci 1812c593315Sopenharmony_ci nghttp2_session_callbacks_new(&callbacks); 1822c593315Sopenharmony_ci 1832c593315Sopenharmony_ci auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks); 1842c593315Sopenharmony_ci 1852c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, 1862c593315Sopenharmony_ci on_frame_recv_callback); 1872c593315Sopenharmony_ci 1882c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 1892c593315Sopenharmony_ci callbacks, on_data_chunk_recv_callback); 1902c593315Sopenharmony_ci 1912c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_stream_close_callback( 1922c593315Sopenharmony_ci callbacks, on_stream_close_callback); 1932c593315Sopenharmony_ci 1942c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_header_callback(callbacks, 1952c593315Sopenharmony_ci on_header_callback); 1962c593315Sopenharmony_ci 1972c593315Sopenharmony_ci nghttp2_session_callbacks_set_before_frame_send_callback( 1982c593315Sopenharmony_ci callbacks, before_frame_send_callback); 1992c593315Sopenharmony_ci 2002c593315Sopenharmony_ci nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 2012c593315Sopenharmony_ci 2022c593315Sopenharmony_ci nghttp2_option *opt; 2032c593315Sopenharmony_ci 2042c593315Sopenharmony_ci rv = nghttp2_option_new(&opt); 2052c593315Sopenharmony_ci assert(rv == 0); 2062c593315Sopenharmony_ci 2072c593315Sopenharmony_ci auto config = client_->worker->config; 2082c593315Sopenharmony_ci 2092c593315Sopenharmony_ci if (config->encoder_header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { 2102c593315Sopenharmony_ci nghttp2_option_set_max_deflate_dynamic_table_size( 2112c593315Sopenharmony_ci opt, config->encoder_header_table_size); 2122c593315Sopenharmony_ci } 2132c593315Sopenharmony_ci 2142c593315Sopenharmony_ci nghttp2_session_client_new2(&session_, callbacks, client_, opt); 2152c593315Sopenharmony_ci 2162c593315Sopenharmony_ci nghttp2_option_del(opt); 2172c593315Sopenharmony_ci 2182c593315Sopenharmony_ci std::array<nghttp2_settings_entry, 4> iv; 2192c593315Sopenharmony_ci size_t niv = 2; 2202c593315Sopenharmony_ci iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 2212c593315Sopenharmony_ci iv[0].value = 0; 2222c593315Sopenharmony_ci iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 2232c593315Sopenharmony_ci iv[1].value = (1 << config->window_bits) - 1; 2242c593315Sopenharmony_ci 2252c593315Sopenharmony_ci if (config->header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { 2262c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; 2272c593315Sopenharmony_ci iv[niv].value = config->header_table_size; 2282c593315Sopenharmony_ci ++niv; 2292c593315Sopenharmony_ci } 2302c593315Sopenharmony_ci if (config->max_frame_size != 16_k) { 2312c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; 2322c593315Sopenharmony_ci iv[niv].value = config->max_frame_size; 2332c593315Sopenharmony_ci ++niv; 2342c593315Sopenharmony_ci } 2352c593315Sopenharmony_ci 2362c593315Sopenharmony_ci rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv); 2372c593315Sopenharmony_ci 2382c593315Sopenharmony_ci assert(rv == 0); 2392c593315Sopenharmony_ci 2402c593315Sopenharmony_ci auto connection_window = (1 << config->connection_window_bits) - 1; 2412c593315Sopenharmony_ci nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0, 2422c593315Sopenharmony_ci connection_window); 2432c593315Sopenharmony_ci 2442c593315Sopenharmony_ci client_->signal_write(); 2452c593315Sopenharmony_ci} 2462c593315Sopenharmony_ci 2472c593315Sopenharmony_ciint Http2Session::submit_request() { 2482c593315Sopenharmony_ci if (nghttp2_session_check_request_allowed(session_) == 0) { 2492c593315Sopenharmony_ci return -1; 2502c593315Sopenharmony_ci } 2512c593315Sopenharmony_ci 2522c593315Sopenharmony_ci auto config = client_->worker->config; 2532c593315Sopenharmony_ci auto &nva = config->nva[client_->reqidx++]; 2542c593315Sopenharmony_ci 2552c593315Sopenharmony_ci if (client_->reqidx == config->nva.size()) { 2562c593315Sopenharmony_ci client_->reqidx = 0; 2572c593315Sopenharmony_ci } 2582c593315Sopenharmony_ci 2592c593315Sopenharmony_ci nghttp2_data_provider prd{{0}, file_read_callback}; 2602c593315Sopenharmony_ci 2612c593315Sopenharmony_ci auto stream_id = 2622c593315Sopenharmony_ci nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(), 2632c593315Sopenharmony_ci config->data_fd == -1 ? nullptr : &prd, nullptr); 2642c593315Sopenharmony_ci if (stream_id < 0) { 2652c593315Sopenharmony_ci return -1; 2662c593315Sopenharmony_ci } 2672c593315Sopenharmony_ci 2682c593315Sopenharmony_ci client_->on_request(stream_id); 2692c593315Sopenharmony_ci 2702c593315Sopenharmony_ci return 0; 2712c593315Sopenharmony_ci} 2722c593315Sopenharmony_ci 2732c593315Sopenharmony_ciint Http2Session::on_read(const uint8_t *data, size_t len) { 2742c593315Sopenharmony_ci auto rv = nghttp2_session_mem_recv(session_, data, len); 2752c593315Sopenharmony_ci if (rv < 0) { 2762c593315Sopenharmony_ci return -1; 2772c593315Sopenharmony_ci } 2782c593315Sopenharmony_ci 2792c593315Sopenharmony_ci assert(static_cast<size_t>(rv) == len); 2802c593315Sopenharmony_ci 2812c593315Sopenharmony_ci if (nghttp2_session_want_read(session_) == 0 && 2822c593315Sopenharmony_ci nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { 2832c593315Sopenharmony_ci return -1; 2842c593315Sopenharmony_ci } 2852c593315Sopenharmony_ci 2862c593315Sopenharmony_ci client_->signal_write(); 2872c593315Sopenharmony_ci 2882c593315Sopenharmony_ci return 0; 2892c593315Sopenharmony_ci} 2902c593315Sopenharmony_ci 2912c593315Sopenharmony_ciint Http2Session::on_write() { 2922c593315Sopenharmony_ci auto rv = nghttp2_session_send(session_); 2932c593315Sopenharmony_ci if (rv != 0) { 2942c593315Sopenharmony_ci return -1; 2952c593315Sopenharmony_ci } 2962c593315Sopenharmony_ci 2972c593315Sopenharmony_ci if (nghttp2_session_want_read(session_) == 0 && 2982c593315Sopenharmony_ci nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { 2992c593315Sopenharmony_ci return -1; 3002c593315Sopenharmony_ci } 3012c593315Sopenharmony_ci 3022c593315Sopenharmony_ci return 0; 3032c593315Sopenharmony_ci} 3042c593315Sopenharmony_ci 3052c593315Sopenharmony_civoid Http2Session::terminate() { 3062c593315Sopenharmony_ci nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); 3072c593315Sopenharmony_ci} 3082c593315Sopenharmony_ci 3092c593315Sopenharmony_cisize_t Http2Session::max_concurrent_streams() { 3102c593315Sopenharmony_ci return (size_t)client_->worker->config->max_concurrent_streams; 3112c593315Sopenharmony_ci} 3122c593315Sopenharmony_ci 3132c593315Sopenharmony_ci} // namespace h2load 314