2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
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_http2_session.h"
33 using namespace nghttp2;
37 Http2Session::Http2Session(Client *client)
38 : client_(client), session_(nullptr) {}
40 Http2Session::~Http2Session() { nghttp2_session_del(session_); }
43 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
44 const uint8_t *name, size_t namelen,
45 const uint8_t *value, size_t valuelen, uint8_t flags,
47 auto client = static_cast<Client *>(user_data);
48 if (frame->hd.type != NGHTTP2_HEADERS ||
49 frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
52 client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
58 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
60 auto client = static_cast<Client *>(user_data);
61 if (frame->hd.type != NGHTTP2_HEADERS ||
62 frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
65 client->worker->stats.bytes_head += frame->hd.length;
71 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
72 int32_t stream_id, const uint8_t *data,
73 size_t len, void *user_data) {
74 auto client = static_cast<Client *>(user_data);
75 client->worker->stats.bytes_body += len;
81 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
82 uint32_t error_code, void *user_data) {
83 auto client = static_cast<Client *>(user_data);
84 auto req_stat = static_cast<RequestStat *>(
85 nghttp2_session_get_stream_user_data(session, stream_id));
86 client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR, req_stat);
92 int before_frame_send_callback(nghttp2_session *session,
93 const nghttp2_frame *frame, void *user_data) {
94 if (frame->hd.type != NGHTTP2_HEADERS ||
95 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
99 auto client = static_cast<Client *>(user_data);
100 client->on_request(frame->hd.stream_id);
101 auto req_stat = static_cast<RequestStat *>(
102 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
103 client->record_request_time(req_stat);
110 ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
111 size_t length, int flags, void *user_data) {
112 auto client = static_cast<Client *>(user_data);
113 auto &wb = client->wb;
115 if (wb.wleft() == 0) {
116 return NGHTTP2_ERR_WOULDBLOCK;
119 return wb.write(data, length);
123 void Http2Session::on_connect() {
126 nghttp2_session_callbacks *callbacks;
128 nghttp2_session_callbacks_new(&callbacks);
130 auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks);
132 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
133 on_frame_recv_callback);
135 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
136 callbacks, on_data_chunk_recv_callback);
138 nghttp2_session_callbacks_set_on_stream_close_callback(
139 callbacks, on_stream_close_callback);
141 nghttp2_session_callbacks_set_on_header_callback(callbacks,
144 nghttp2_session_callbacks_set_before_frame_send_callback(
145 callbacks, before_frame_send_callback);
147 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
149 nghttp2_session_client_new(&session_, callbacks, client_);
151 std::array<nghttp2_settings_entry, 2> iv;
152 iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
154 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
155 iv[1].value = (1 << client_->worker->config->window_bits) - 1;
157 rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(),
162 auto extra_connection_window =
163 (1 << client_->worker->config->connection_window_bits) - 1 -
164 NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
165 if (extra_connection_window != 0) {
166 nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0,
167 extra_connection_window);
170 auto &wb = client_->wb;
171 assert(wb.wleft() >= NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
173 wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
174 NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
176 client_->signal_write();
179 void Http2Session::submit_request(RequestStat *req_stat) {
180 auto config = client_->worker->config;
181 auto &nva = config->nva[client_->reqidx++];
183 if (client_->reqidx == config->nva.size()) {
187 auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
188 nva.size(), nullptr, req_stat);
189 assert(stream_id > 0);
192 int Http2Session::on_read(const uint8_t *data, size_t len) {
193 auto rv = nghttp2_session_mem_recv(session_, data, len);
198 assert(static_cast<size_t>(rv) == len);
200 if (nghttp2_session_want_read(session_) == 0 &&
201 nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
205 client_->signal_write();
210 int Http2Session::on_write() {
211 auto rv = nghttp2_session_send(session_);
216 if (nghttp2_session_want_read(session_) == 0 &&
217 nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
224 void Http2Session::terminate() {
225 nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
228 } // namespace h2load