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