12c593315Sopenharmony_ci/* 22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library 32c593315Sopenharmony_ci * 42c593315Sopenharmony_ci * Copyright (c) 2015 British Broadcasting Corporation 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_http1_session.h" 262c593315Sopenharmony_ci 272c593315Sopenharmony_ci#include <cassert> 282c593315Sopenharmony_ci#include <cerrno> 292c593315Sopenharmony_ci 302c593315Sopenharmony_ci#include "h2load.h" 312c593315Sopenharmony_ci#include "util.h" 322c593315Sopenharmony_ci#include "template.h" 332c593315Sopenharmony_ci 342c593315Sopenharmony_ci#include <iostream> 352c593315Sopenharmony_ci#include <fstream> 362c593315Sopenharmony_ci 372c593315Sopenharmony_ciusing namespace nghttp2; 382c593315Sopenharmony_ci 392c593315Sopenharmony_cinamespace h2load { 402c593315Sopenharmony_ci 412c593315Sopenharmony_cinamespace { 422c593315Sopenharmony_ci// HTTP response message begin 432c593315Sopenharmony_ciint htp_msg_begincb(llhttp_t *htp) { 442c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 452c593315Sopenharmony_ci 462c593315Sopenharmony_ci if (session->stream_resp_counter_ > session->stream_req_counter_) { 472c593315Sopenharmony_ci return -1; 482c593315Sopenharmony_ci } 492c593315Sopenharmony_ci 502c593315Sopenharmony_ci return 0; 512c593315Sopenharmony_ci} 522c593315Sopenharmony_ci} // namespace 532c593315Sopenharmony_ci 542c593315Sopenharmony_cinamespace { 552c593315Sopenharmony_ci// HTTP response status code 562c593315Sopenharmony_ciint htp_statuscb(llhttp_t *htp, const char *at, size_t length) { 572c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 582c593315Sopenharmony_ci auto client = session->get_client(); 592c593315Sopenharmony_ci 602c593315Sopenharmony_ci if (htp->status_code / 100 == 1) { 612c593315Sopenharmony_ci return 0; 622c593315Sopenharmony_ci } 632c593315Sopenharmony_ci 642c593315Sopenharmony_ci client->on_status_code(session->stream_resp_counter_, htp->status_code); 652c593315Sopenharmony_ci 662c593315Sopenharmony_ci return 0; 672c593315Sopenharmony_ci} 682c593315Sopenharmony_ci} // namespace 692c593315Sopenharmony_ci 702c593315Sopenharmony_cinamespace { 712c593315Sopenharmony_ci// HTTP response message complete 722c593315Sopenharmony_ciint htp_msg_completecb(llhttp_t *htp) { 732c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 742c593315Sopenharmony_ci auto client = session->get_client(); 752c593315Sopenharmony_ci 762c593315Sopenharmony_ci if (htp->status_code / 100 == 1) { 772c593315Sopenharmony_ci return 0; 782c593315Sopenharmony_ci } 792c593315Sopenharmony_ci 802c593315Sopenharmony_ci client->final = llhttp_should_keep_alive(htp) == 0; 812c593315Sopenharmony_ci auto req_stat = client->get_req_stat(session->stream_resp_counter_); 822c593315Sopenharmony_ci 832c593315Sopenharmony_ci assert(req_stat); 842c593315Sopenharmony_ci 852c593315Sopenharmony_ci auto config = client->worker->config; 862c593315Sopenharmony_ci if (req_stat->data_offset >= config->data_length) { 872c593315Sopenharmony_ci client->on_stream_close(session->stream_resp_counter_, true, client->final); 882c593315Sopenharmony_ci } 892c593315Sopenharmony_ci 902c593315Sopenharmony_ci session->stream_resp_counter_ += 2; 912c593315Sopenharmony_ci 922c593315Sopenharmony_ci if (client->final) { 932c593315Sopenharmony_ci session->stream_req_counter_ = session->stream_resp_counter_; 942c593315Sopenharmony_ci 952c593315Sopenharmony_ci // Connection is going down. If we have still request to do, 962c593315Sopenharmony_ci // create new connection and keep on doing the job. 972c593315Sopenharmony_ci if (client->req_left) { 982c593315Sopenharmony_ci client->try_new_connection(); 992c593315Sopenharmony_ci } 1002c593315Sopenharmony_ci 1012c593315Sopenharmony_ci return HPE_PAUSED; 1022c593315Sopenharmony_ci } 1032c593315Sopenharmony_ci 1042c593315Sopenharmony_ci return 0; 1052c593315Sopenharmony_ci} 1062c593315Sopenharmony_ci} // namespace 1072c593315Sopenharmony_ci 1082c593315Sopenharmony_cinamespace { 1092c593315Sopenharmony_ciint htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) { 1102c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 1112c593315Sopenharmony_ci auto client = session->get_client(); 1122c593315Sopenharmony_ci 1132c593315Sopenharmony_ci client->worker->stats.bytes_head += len; 1142c593315Sopenharmony_ci client->worker->stats.bytes_head_decomp += len; 1152c593315Sopenharmony_ci return 0; 1162c593315Sopenharmony_ci} 1172c593315Sopenharmony_ci} // namespace 1182c593315Sopenharmony_ci 1192c593315Sopenharmony_cinamespace { 1202c593315Sopenharmony_ciint htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) { 1212c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 1222c593315Sopenharmony_ci auto client = session->get_client(); 1232c593315Sopenharmony_ci 1242c593315Sopenharmony_ci client->worker->stats.bytes_head += len; 1252c593315Sopenharmony_ci client->worker->stats.bytes_head_decomp += len; 1262c593315Sopenharmony_ci return 0; 1272c593315Sopenharmony_ci} 1282c593315Sopenharmony_ci} // namespace 1292c593315Sopenharmony_ci 1302c593315Sopenharmony_cinamespace { 1312c593315Sopenharmony_ciint htp_hdrs_completecb(llhttp_t *htp) { 1322c593315Sopenharmony_ci return !http2::expect_response_body(htp->status_code); 1332c593315Sopenharmony_ci} 1342c593315Sopenharmony_ci} // namespace 1352c593315Sopenharmony_ci 1362c593315Sopenharmony_cinamespace { 1372c593315Sopenharmony_ciint htp_body_cb(llhttp_t *htp, const char *data, size_t len) { 1382c593315Sopenharmony_ci auto session = static_cast<Http1Session *>(htp->data); 1392c593315Sopenharmony_ci auto client = session->get_client(); 1402c593315Sopenharmony_ci 1412c593315Sopenharmony_ci client->record_ttfb(); 1422c593315Sopenharmony_ci client->worker->stats.bytes_body += len; 1432c593315Sopenharmony_ci 1442c593315Sopenharmony_ci return 0; 1452c593315Sopenharmony_ci} 1462c593315Sopenharmony_ci} // namespace 1472c593315Sopenharmony_ci 1482c593315Sopenharmony_cinamespace { 1492c593315Sopenharmony_ciconstexpr llhttp_settings_t htp_hooks = { 1502c593315Sopenharmony_ci htp_msg_begincb, // llhttp_cb on_message_begin; 1512c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_url; 1522c593315Sopenharmony_ci htp_statuscb, // llhttp_data_cb on_status; 1532c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_method; 1542c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_version; 1552c593315Sopenharmony_ci htp_hdr_keycb, // llhttp_data_cb on_header_field; 1562c593315Sopenharmony_ci htp_hdr_valcb, // llhttp_data_cb on_header_value; 1572c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_chunk_extension_name; 1582c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_chunk_extension_value; 1592c593315Sopenharmony_ci htp_hdrs_completecb, // llhttp_cb on_headers_complete; 1602c593315Sopenharmony_ci htp_body_cb, // llhttp_data_cb on_body; 1612c593315Sopenharmony_ci htp_msg_completecb, // llhttp_cb on_message_complete; 1622c593315Sopenharmony_ci nullptr, // llhttp_cb on_url_complete; 1632c593315Sopenharmony_ci nullptr, // llhttp_cb on_status_complete; 1642c593315Sopenharmony_ci nullptr, // llhttp_cb on_method_complete; 1652c593315Sopenharmony_ci nullptr, // llhttp_cb on_version_complete; 1662c593315Sopenharmony_ci nullptr, // llhttp_cb on_header_field_complete; 1672c593315Sopenharmony_ci nullptr, // llhttp_cb on_header_value_complete; 1682c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_extension_name_complete; 1692c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_extension_value_complete; 1702c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_header; 1712c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_complete; 1722c593315Sopenharmony_ci nullptr, // llhttp_cb on_reset; 1732c593315Sopenharmony_ci}; 1742c593315Sopenharmony_ci} // namespace 1752c593315Sopenharmony_ci 1762c593315Sopenharmony_ciHttp1Session::Http1Session(Client *client) 1772c593315Sopenharmony_ci : stream_req_counter_(1), 1782c593315Sopenharmony_ci stream_resp_counter_(1), 1792c593315Sopenharmony_ci client_(client), 1802c593315Sopenharmony_ci htp_(), 1812c593315Sopenharmony_ci complete_(false) { 1822c593315Sopenharmony_ci llhttp_init(&htp_, HTTP_RESPONSE, &htp_hooks); 1832c593315Sopenharmony_ci htp_.data = this; 1842c593315Sopenharmony_ci} 1852c593315Sopenharmony_ci 1862c593315Sopenharmony_ciHttp1Session::~Http1Session() {} 1872c593315Sopenharmony_ci 1882c593315Sopenharmony_civoid Http1Session::on_connect() { client_->signal_write(); } 1892c593315Sopenharmony_ci 1902c593315Sopenharmony_ciint Http1Session::submit_request() { 1912c593315Sopenharmony_ci auto config = client_->worker->config; 1922c593315Sopenharmony_ci const auto &req = config->h1reqs[client_->reqidx]; 1932c593315Sopenharmony_ci client_->reqidx++; 1942c593315Sopenharmony_ci 1952c593315Sopenharmony_ci if (client_->reqidx == config->h1reqs.size()) { 1962c593315Sopenharmony_ci client_->reqidx = 0; 1972c593315Sopenharmony_ci } 1982c593315Sopenharmony_ci 1992c593315Sopenharmony_ci client_->on_request(stream_req_counter_); 2002c593315Sopenharmony_ci 2012c593315Sopenharmony_ci auto req_stat = client_->get_req_stat(stream_req_counter_); 2022c593315Sopenharmony_ci 2032c593315Sopenharmony_ci client_->record_request_time(req_stat); 2042c593315Sopenharmony_ci client_->wb.append(req); 2052c593315Sopenharmony_ci 2062c593315Sopenharmony_ci if (config->data_fd == -1 || config->data_length == 0) { 2072c593315Sopenharmony_ci // increment for next request 2082c593315Sopenharmony_ci stream_req_counter_ += 2; 2092c593315Sopenharmony_ci 2102c593315Sopenharmony_ci return 0; 2112c593315Sopenharmony_ci } 2122c593315Sopenharmony_ci 2132c593315Sopenharmony_ci return on_write(); 2142c593315Sopenharmony_ci} 2152c593315Sopenharmony_ci 2162c593315Sopenharmony_ciint Http1Session::on_read(const uint8_t *data, size_t len) { 2172c593315Sopenharmony_ci auto htperr = 2182c593315Sopenharmony_ci llhttp_execute(&htp_, reinterpret_cast<const char *>(data), len); 2192c593315Sopenharmony_ci auto nread = htperr == HPE_OK 2202c593315Sopenharmony_ci ? len 2212c593315Sopenharmony_ci : static_cast<size_t>(reinterpret_cast<const uint8_t *>( 2222c593315Sopenharmony_ci llhttp_get_error_pos(&htp_)) - 2232c593315Sopenharmony_ci data); 2242c593315Sopenharmony_ci 2252c593315Sopenharmony_ci if (client_->worker->config->verbose) { 2262c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(data), nread); 2272c593315Sopenharmony_ci } 2282c593315Sopenharmony_ci 2292c593315Sopenharmony_ci if (htperr == HPE_PAUSED) { 2302c593315Sopenharmony_ci // pause is done only when connection: close is requested 2312c593315Sopenharmony_ci return -1; 2322c593315Sopenharmony_ci } 2332c593315Sopenharmony_ci 2342c593315Sopenharmony_ci if (htperr != HPE_OK) { 2352c593315Sopenharmony_ci std::cerr << "[ERROR] HTTP parse error: " 2362c593315Sopenharmony_ci << "(" << llhttp_errno_name(htperr) << ") " 2372c593315Sopenharmony_ci << llhttp_get_error_reason(&htp_) << std::endl; 2382c593315Sopenharmony_ci return -1; 2392c593315Sopenharmony_ci } 2402c593315Sopenharmony_ci 2412c593315Sopenharmony_ci return 0; 2422c593315Sopenharmony_ci} 2432c593315Sopenharmony_ci 2442c593315Sopenharmony_ciint Http1Session::on_write() { 2452c593315Sopenharmony_ci if (complete_) { 2462c593315Sopenharmony_ci return -1; 2472c593315Sopenharmony_ci } 2482c593315Sopenharmony_ci 2492c593315Sopenharmony_ci auto config = client_->worker->config; 2502c593315Sopenharmony_ci auto req_stat = client_->get_req_stat(stream_req_counter_); 2512c593315Sopenharmony_ci if (!req_stat) { 2522c593315Sopenharmony_ci return 0; 2532c593315Sopenharmony_ci } 2542c593315Sopenharmony_ci 2552c593315Sopenharmony_ci if (req_stat->data_offset < config->data_length) { 2562c593315Sopenharmony_ci auto req_stat = client_->get_req_stat(stream_req_counter_); 2572c593315Sopenharmony_ci auto &wb = client_->wb; 2582c593315Sopenharmony_ci 2592c593315Sopenharmony_ci // TODO unfortunately, wb has no interface to use with read(2) 2602c593315Sopenharmony_ci // family functions. 2612c593315Sopenharmony_ci std::array<uint8_t, 16_k> buf; 2622c593315Sopenharmony_ci 2632c593315Sopenharmony_ci ssize_t nread; 2642c593315Sopenharmony_ci while ((nread = pread(config->data_fd, buf.data(), buf.size(), 2652c593315Sopenharmony_ci req_stat->data_offset)) == -1 && 2662c593315Sopenharmony_ci errno == EINTR) 2672c593315Sopenharmony_ci ; 2682c593315Sopenharmony_ci 2692c593315Sopenharmony_ci if (nread == -1) { 2702c593315Sopenharmony_ci return -1; 2712c593315Sopenharmony_ci } 2722c593315Sopenharmony_ci 2732c593315Sopenharmony_ci req_stat->data_offset += nread; 2742c593315Sopenharmony_ci 2752c593315Sopenharmony_ci wb.append(buf.data(), nread); 2762c593315Sopenharmony_ci 2772c593315Sopenharmony_ci if (client_->worker->config->verbose) { 2782c593315Sopenharmony_ci std::cout << "[send " << nread << " byte(s)]" << std::endl; 2792c593315Sopenharmony_ci } 2802c593315Sopenharmony_ci 2812c593315Sopenharmony_ci if (req_stat->data_offset == config->data_length) { 2822c593315Sopenharmony_ci // increment for next request 2832c593315Sopenharmony_ci stream_req_counter_ += 2; 2842c593315Sopenharmony_ci 2852c593315Sopenharmony_ci if (stream_resp_counter_ == stream_req_counter_) { 2862c593315Sopenharmony_ci // Response has already been received 2872c593315Sopenharmony_ci client_->on_stream_close(stream_resp_counter_ - 2, true, 2882c593315Sopenharmony_ci client_->final); 2892c593315Sopenharmony_ci } 2902c593315Sopenharmony_ci } 2912c593315Sopenharmony_ci } 2922c593315Sopenharmony_ci 2932c593315Sopenharmony_ci return 0; 2942c593315Sopenharmony_ci} 2952c593315Sopenharmony_ci 2962c593315Sopenharmony_civoid Http1Session::terminate() { complete_ = true; } 2972c593315Sopenharmony_ci 2982c593315Sopenharmony_ciClient *Http1Session::get_client() { return client_; } 2992c593315Sopenharmony_ci 3002c593315Sopenharmony_cisize_t Http1Session::max_concurrent_streams() { 3012c593315Sopenharmony_ci auto config = client_->worker->config; 3022c593315Sopenharmony_ci 3032c593315Sopenharmony_ci return config->data_fd == -1 ? config->max_concurrent_streams : 1; 3042c593315Sopenharmony_ci} 3052c593315Sopenharmony_ci 3062c593315Sopenharmony_ci} // namespace h2load 307