Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / shrpx_connection.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2015 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 "shrpx_connection.h"
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif // HAVE_UNISTD_H
30
31 #include <limits>
32
33 #include <openssl/err.h>
34
35 #include "memchunk.h"
36
37 using namespace nghttp2;
38
39 namespace shrpx {
40 Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
41                        ev_tstamp write_timeout, ev_tstamp read_timeout,
42                        size_t write_rate, size_t write_burst, size_t read_rate,
43                        size_t read_burst, IOCb writecb, IOCb readcb,
44                        TimerCb timeoutcb, void *data)
45     : tls{ssl}, wlimit(loop, &wev, write_rate, write_burst),
46       rlimit(loop, &rev, read_rate, read_burst, ssl), writecb(writecb),
47       readcb(readcb), timeoutcb(timeoutcb), loop(loop), data(data), fd(fd) {
48
49   ev_io_init(&wev, writecb, fd, EV_WRITE);
50   ev_io_init(&rev, readcb, fd, EV_READ);
51
52   wev.data = this;
53   rev.data = this;
54
55   ev_timer_init(&wt, timeoutcb, 0., write_timeout);
56   ev_timer_init(&rt, timeoutcb, 0., read_timeout);
57
58   wt.data = this;
59   rt.data = this;
60
61   // set 0. to double field explicitly just in case
62   tls.last_write_time = 0.;
63 }
64
65 Connection::~Connection() { disconnect(); }
66
67 void Connection::disconnect() {
68   ev_timer_stop(loop, &rt);
69   ev_timer_stop(loop, &wt);
70
71   rlimit.stopw();
72   wlimit.stopw();
73
74   if (tls.ssl) {
75     SSL_set_app_data(tls.ssl, nullptr);
76     SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN);
77     ERR_clear_error();
78     SSL_shutdown(tls.ssl);
79     SSL_free(tls.ssl);
80     tls.ssl = nullptr;
81   }
82
83   if (fd != -1) {
84     shutdown(fd, SHUT_WR);
85     close(fd);
86     fd = -1;
87   }
88 }
89
90 int Connection::tls_handshake() {
91   auto rv = SSL_do_handshake(tls.ssl);
92
93   if (rv == 0) {
94     return SHRPX_ERR_NETWORK;
95   }
96
97   if (rv < 0) {
98     auto err = SSL_get_error(tls.ssl, rv);
99     switch (err) {
100     case SSL_ERROR_WANT_READ:
101       wlimit.stopw();
102       ev_timer_stop(loop, &wt);
103       return SHRPX_ERR_INPROGRESS;
104     case SSL_ERROR_WANT_WRITE:
105       wlimit.startw();
106       ev_timer_again(loop, &wt);
107       return SHRPX_ERR_INPROGRESS;
108     default:
109       return SHRPX_ERR_NETWORK;
110     }
111   }
112
113   wlimit.stopw();
114   ev_timer_stop(loop, &wt);
115
116   tls.initial_handshake_done = true;
117
118   if (LOG_ENABLED(INFO)) {
119     LOG(INFO) << "SSL/TLS handshake completed";
120     if (SSL_session_reused(tls.ssl)) {
121       LOG(INFO) << "SSL/TLS session reused";
122     }
123   }
124
125   return 0;
126 }
127
128 namespace {
129 const size_t SHRPX_SMALL_WRITE_LIMIT = 1300;
130 const size_t SHRPX_WARMUP_THRESHOLD = 1 << 20;
131 } // namespace
132
133 size_t Connection::get_tls_write_limit() {
134   auto t = ev_now(loop);
135
136   if (t - tls.last_write_time > 1.) {
137     // Time out, use small record size
138     tls.warmup_writelen = 0;
139     return SHRPX_SMALL_WRITE_LIMIT;
140   }
141
142   if (tls.warmup_writelen >= SHRPX_WARMUP_THRESHOLD) {
143     return std::numeric_limits<ssize_t>::max();
144   }
145
146   return SHRPX_SMALL_WRITE_LIMIT;
147 }
148
149 void Connection::update_tls_warmup_writelen(size_t n) {
150   if (tls.warmup_writelen < SHRPX_WARMUP_THRESHOLD) {
151     tls.warmup_writelen += n;
152   }
153 }
154
155 ssize_t Connection::write_tls(const void *data, size_t len) {
156   // SSL_write requires the same arguments (buf pointer and its
157   // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
158   // get_write_limit() may return smaller length than previously
159   // passed to SSL_write, which violates OpenSSL assumption.  To avoid
160   // this, we keep last legnth passed to SSL_write to
161   // tls.last_writelen if SSL_write indicated I/O blocking.
162   if (tls.last_writelen == 0) {
163     len = std::min(len, wlimit.avail());
164     len = std::min(len, get_tls_write_limit());
165     if (len == 0) {
166       return 0;
167     }
168   } else {
169     len = tls.last_writelen;
170     tls.last_writelen = 0;
171   }
172
173   auto rv = SSL_write(tls.ssl, data, len);
174
175   if (rv == 0) {
176     return SHRPX_ERR_NETWORK;
177   }
178
179   tls.last_write_time = ev_now(loop);
180
181   if (rv < 0) {
182     auto err = SSL_get_error(tls.ssl, rv);
183     switch (err) {
184     case SSL_ERROR_WANT_READ:
185       if (LOG_ENABLED(INFO)) {
186         LOG(INFO) << "Close connection due to TLS renegotiation";
187       }
188       return SHRPX_ERR_NETWORK;
189     case SSL_ERROR_WANT_WRITE:
190       tls.last_writelen = len;
191       wlimit.startw();
192       ev_timer_again(loop, &wt);
193       return 0;
194     default:
195       if (LOG_ENABLED(INFO)) {
196         LOG(INFO) << "SSL_write: SSL_get_error returned " << err;
197       }
198       return SHRPX_ERR_NETWORK;
199     }
200   }
201
202   wlimit.drain(rv);
203
204   update_tls_warmup_writelen(rv);
205
206   return rv;
207 }
208
209 ssize_t Connection::read_tls(void *data, size_t len) {
210   // SSL_read requires the same arguments (buf pointer and its
211   // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
212   // rlimit_.avail() or rlimit_.avail() may return different length
213   // than the length previously passed to SSL_read, which violates
214   // OpenSSL assumption.  To avoid this, we keep last legnth passed
215   // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
216   // blocking.
217   if (tls.last_readlen == 0) {
218     len = std::min(len, rlimit.avail());
219     if (len == 0) {
220       return 0;
221     }
222   } else {
223     len = tls.last_readlen;
224     tls.last_readlen = 0;
225   }
226
227   auto rv = SSL_read(tls.ssl, data, len);
228
229   if (rv <= 0) {
230     auto err = SSL_get_error(tls.ssl, rv);
231     switch (err) {
232     case SSL_ERROR_WANT_READ:
233       tls.last_readlen = len;
234       return 0;
235     case SSL_ERROR_WANT_WRITE:
236       if (LOG_ENABLED(INFO)) {
237         LOG(INFO) << "Close connection due to TLS renegotiation";
238       }
239       return SHRPX_ERR_NETWORK;
240     case SSL_ERROR_ZERO_RETURN:
241       return SHRPX_ERR_EOF;
242     default:
243       if (LOG_ENABLED(INFO)) {
244         LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
245       }
246       return SHRPX_ERR_NETWORK;
247     }
248   }
249
250   rlimit.drain(rv);
251
252   return rv;
253 }
254
255 ssize_t Connection::write_clear(const void *data, size_t len) {
256   len = std::min(len, wlimit.avail());
257   if (len == 0) {
258     return 0;
259   }
260
261   ssize_t nwrite;
262   while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR)
263     ;
264   if (nwrite == -1) {
265     if (errno == EAGAIN || errno == EWOULDBLOCK) {
266       wlimit.startw();
267       ev_timer_again(loop, &wt);
268       return 0;
269     }
270     return SHRPX_ERR_NETWORK;
271   }
272
273   wlimit.drain(nwrite);
274
275   return nwrite;
276 }
277
278 ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) {
279   iovcnt = limit_iovec(iov, iovcnt, wlimit.avail());
280   if (iovcnt == 0) {
281     return 0;
282   }
283
284   ssize_t nwrite;
285   while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR)
286     ;
287   if (nwrite == -1) {
288     if (errno == EAGAIN || errno == EWOULDBLOCK) {
289       wlimit.startw();
290       ev_timer_again(loop, &wt);
291       return 0;
292     }
293     return SHRPX_ERR_NETWORK;
294   }
295
296   wlimit.drain(nwrite);
297
298   return nwrite;
299 }
300
301 ssize_t Connection::read_clear(void *data, size_t len) {
302   len = std::min(len, rlimit.avail());
303   if (len == 0) {
304     return 0;
305   }
306
307   ssize_t nread;
308   while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
309     ;
310   if (nread == -1) {
311     if (errno == EAGAIN || errno == EWOULDBLOCK) {
312       return 0;
313     }
314     return SHRPX_ERR_NETWORK;
315   }
316
317   if (nread == 0) {
318     return SHRPX_ERR_EOF;
319   }
320
321   rlimit.drain(nread);
322
323   return nread;
324 }
325
326 void Connection::handle_tls_pending_read() {
327   if (!ev_is_active(&rev)) {
328     return;
329   }
330   rlimit.handle_tls_pending_read();
331 }
332
333 } // namespace shrpx