123 lines
3.1 KiB
C++
123 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 || !conn_->tls.initial_handshake_done ||
|
|
(SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 &&
|
|
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
|