tizen 2.4 release
[external/nghttp2.git] / src / h2load_http2_session.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2014 Tatsuhiro Tsujikawa
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
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.
24  */
25 #include "h2load_http2_session.h"
26
27 #include <cassert>
28
29 #include "h2load.h"
30 #include "util.h"
31 #include "template.h"
32
33 using namespace nghttp2;
34
35 namespace h2load {
36
37 Http2Session::Http2Session(Client *client)
38     : client_(client), session_(nullptr) {}
39
40 Http2Session::~Http2Session() { nghttp2_session_del(session_); }
41
42 namespace {
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,
46                        void *user_data) {
47   auto client = static_cast<Client *>(user_data);
48   if (frame->hd.type != NGHTTP2_HEADERS ||
49       frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
50     return 0;
51   }
52   client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
53   return 0;
54 }
55 } // namespace
56
57 namespace {
58 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
59                            void *user_data) {
60   auto client = static_cast<Client *>(user_data);
61   if (frame->hd.type != NGHTTP2_HEADERS ||
62       frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
63     return 0;
64   }
65   client->worker->stats.bytes_head += frame->hd.length;
66   return 0;
67 }
68 } // namespace
69
70 namespace {
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;
76   return 0;
77 }
78 } // namespace
79
80 namespace {
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);
87   return 0;
88 }
89 } // namespace
90
91 namespace {
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) {
96     return 0;
97   }
98
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);
104
105   return 0;
106 }
107 } // namespace
108
109 namespace {
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;
114
115   if (wb.wleft() == 0) {
116     return NGHTTP2_ERR_WOULDBLOCK;
117   }
118
119   return wb.write(data, length);
120 }
121 } // namespace
122
123 void Http2Session::on_connect() {
124   int rv;
125
126   nghttp2_session_callbacks *callbacks;
127
128   nghttp2_session_callbacks_new(&callbacks);
129
130   auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks);
131
132   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
133                                                        on_frame_recv_callback);
134
135   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
136       callbacks, on_data_chunk_recv_callback);
137
138   nghttp2_session_callbacks_set_on_stream_close_callback(
139       callbacks, on_stream_close_callback);
140
141   nghttp2_session_callbacks_set_on_header_callback(callbacks,
142                                                    on_header_callback);
143
144   nghttp2_session_callbacks_set_before_frame_send_callback(
145       callbacks, before_frame_send_callback);
146
147   nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
148
149   nghttp2_session_client_new(&session_, callbacks, client_);
150
151   std::array<nghttp2_settings_entry, 2> iv;
152   iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
153   iv[0].value = 0;
154   iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
155   iv[1].value = (1 << client_->worker->config->window_bits) - 1;
156
157   rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(),
158                                iv.size());
159
160   assert(rv == 0);
161
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);
168   }
169
170   auto &wb = client_->wb;
171   assert(wb.wleft() >= NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
172
173   wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
174            NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
175
176   client_->signal_write();
177 }
178
179 void Http2Session::submit_request(RequestStat *req_stat) {
180   auto config = client_->worker->config;
181   auto &nva = config->nva[client_->reqidx++];
182
183   if (client_->reqidx == config->nva.size()) {
184     client_->reqidx = 0;
185   }
186
187   auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
188                                           nva.size(), nullptr, req_stat);
189   assert(stream_id > 0);
190 }
191
192 int Http2Session::on_read(const uint8_t *data, size_t len) {
193   auto rv = nghttp2_session_mem_recv(session_, data, len);
194   if (rv < 0) {
195     return -1;
196   }
197
198   assert(static_cast<size_t>(rv) == len);
199
200   if (nghttp2_session_want_read(session_) == 0 &&
201       nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
202     return -1;
203   }
204
205   client_->signal_write();
206
207   return 0;
208 }
209
210 int Http2Session::on_write() {
211   auto rv = nghttp2_session_send(session_);
212   if (rv != 0) {
213     return -1;
214   }
215
216   if (nghttp2_session_want_read(session_) == 0 &&
217       nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
218     return -1;
219   }
220
221   return 0;
222 }
223
224 void Http2Session::terminate() {
225   nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
226 }
227
228 } // namespace h2load