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