/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_rate_limit.h" #include #include "shrpx_connection.h" #include "shrpx_log.h" namespace shrpx { namespace { void regencb(struct ev_loop *loop, ev_timer *w, int revents) { auto r = static_cast(w->data); r->regen(); } } // namespace RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, Connection *conn) : w_(w), loop_(loop), conn_(conn), rate_(rate), burst_(burst), avail_(burst), startw_req_(false) { ev_timer_init(&t_, regencb, 0., 1.); t_.data = this; if (rate_ > 0) { ev_timer_again(loop_, &t_); } } RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); } size_t RateLimit::avail() const { if (rate_ == 0) { return std::numeric_limits::max(); } return avail_; } void RateLimit::drain(size_t n) { if (rate_ == 0) { return; } n = std::min(avail_, n); avail_ -= n; if (avail_ == 0) { ev_io_stop(loop_, w_); } } void RateLimit::regen() { if (rate_ == 0) { return; } if (avail_ + rate_ > burst_) { avail_ = burst_; } else { avail_ += rate_; } if (w_->fd >= 0 && avail_ > 0 && startw_req_) { ev_io_start(loop_, w_); handle_tls_pending_read(); } } void RateLimit::startw() { if (w_->fd < 0) { return; } startw_req_ = true; if (rate_ == 0 || avail_ > 0) { ev_io_start(loop_, w_); handle_tls_pending_read(); return; } } void RateLimit::stopw() { startw_req_ = false; ev_io_stop(loop_, w_); } void RateLimit::handle_tls_pending_read() { if (!conn_ || !conn_->tls.ssl || (SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 && (!conn_->tls.initial_handshake_done || conn_->tls.earlybuf.rleft() == 0))) { return; } // Note that ev_feed_event works without starting watcher, but we // only call this function if watcher is active. ev_feed_event(loop_, w_, EV_READ); } } // namespace shrpx