summaryrefslogtreecommitdiffstats
path: root/src/shrpx_rate_limit.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/shrpx_rate_limit.cc')
-rw-r--r--src/shrpx_rate_limit.cc123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/shrpx_rate_limit.cc b/src/shrpx_rate_limit.cc
new file mode 100644
index 0000000..0d4f921
--- /dev/null
+++ b/src/shrpx_rate_limit.cc
@@ -0,0 +1,123 @@
+/*
+ * 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