nghttp2/src/shrpx_rate_limit.cc

124 lines
3.1 KiB
C++

/*
* 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 <limits>
#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<RateLimit *>(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<ssize_t>::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