feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! RPC middleware to collect prometheus metrics on RPC calls.
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
use jsonrpsee::{types::Request, MethodResponse};
|
||||
use prometheus_endpoint::{
|
||||
register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry,
|
||||
U64,
|
||||
};
|
||||
|
||||
/// Histogram time buckets in microseconds.
|
||||
const HISTOGRAM_BUCKETS: [f64; 11] = [
|
||||
5.0,
|
||||
25.0,
|
||||
100.0,
|
||||
500.0,
|
||||
1_000.0,
|
||||
2_500.0,
|
||||
10_000.0,
|
||||
25_000.0,
|
||||
100_000.0,
|
||||
1_000_000.0,
|
||||
10_000_000.0,
|
||||
];
|
||||
|
||||
/// Metrics for RPC middleware storing information about the number of requests started/completed,
|
||||
/// calls started/completed and their timings.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RpcMetrics {
|
||||
/// Histogram over RPC execution times.
|
||||
calls_time: HistogramVec,
|
||||
/// Number of calls started.
|
||||
calls_started: CounterVec<U64>,
|
||||
/// Number of calls completed.
|
||||
calls_finished: CounterVec<U64>,
|
||||
/// Number of Websocket sessions opened.
|
||||
ws_sessions_opened: Option<Counter<U64>>,
|
||||
/// Number of Websocket sessions closed.
|
||||
ws_sessions_closed: Option<Counter<U64>>,
|
||||
/// Histogram over RPC websocket sessions.
|
||||
ws_sessions_time: HistogramVec,
|
||||
}
|
||||
|
||||
impl RpcMetrics {
|
||||
/// Create an instance of metrics
|
||||
pub fn new(metrics_registry: Option<&Registry>) -> Result<Option<Self>, PrometheusError> {
|
||||
if let Some(metrics_registry) = metrics_registry {
|
||||
Ok(Some(Self {
|
||||
calls_time: register(
|
||||
HistogramVec::new(
|
||||
HistogramOpts::new(
|
||||
"bizinikiwi_rpc_calls_time",
|
||||
"Total time [μs] of processed RPC calls",
|
||||
)
|
||||
.buckets(HISTOGRAM_BUCKETS.to_vec()),
|
||||
&["protocol", "method", "is_rate_limited"],
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?,
|
||||
calls_started: register(
|
||||
CounterVec::new(
|
||||
Opts::new(
|
||||
"bizinikiwi_rpc_calls_started",
|
||||
"Number of received RPC calls (unique un-batched requests)",
|
||||
),
|
||||
&["protocol", "method"],
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?,
|
||||
calls_finished: register(
|
||||
CounterVec::new(
|
||||
Opts::new(
|
||||
"bizinikiwi_rpc_calls_finished",
|
||||
"Number of processed RPC calls (unique un-batched requests)",
|
||||
),
|
||||
&["protocol", "method", "is_error", "is_rate_limited"],
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?,
|
||||
ws_sessions_opened: register(
|
||||
Counter::new(
|
||||
"bizinikiwi_rpc_sessions_opened",
|
||||
"Number of persistent RPC sessions opened",
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?
|
||||
.into(),
|
||||
ws_sessions_closed: register(
|
||||
Counter::new(
|
||||
"bizinikiwi_rpc_sessions_closed",
|
||||
"Number of persistent RPC sessions closed",
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?
|
||||
.into(),
|
||||
ws_sessions_time: register(
|
||||
HistogramVec::new(
|
||||
HistogramOpts::new(
|
||||
"bizinikiwi_rpc_sessions_time",
|
||||
"Total time [s] for each websocket session",
|
||||
)
|
||||
.buckets(HISTOGRAM_BUCKETS.to_vec()),
|
||||
&["protocol"],
|
||||
)?,
|
||||
metrics_registry,
|
||||
)?,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ws_connect(&self) {
|
||||
self.ws_sessions_opened.as_ref().map(|counter| counter.inc());
|
||||
}
|
||||
|
||||
pub(crate) fn ws_disconnect(&self, now: Instant) {
|
||||
let micros = now.elapsed().as_secs();
|
||||
|
||||
self.ws_sessions_closed.as_ref().map(|counter| counter.inc());
|
||||
self.ws_sessions_time.with_label_values(&["ws"]).observe(micros as _);
|
||||
}
|
||||
|
||||
pub(crate) fn on_call(&self, req: &Request, transport_label: &'static str) {
|
||||
log::trace!(
|
||||
target: "rpc_metrics",
|
||||
"[{transport_label}] on_call name={} params={:?}",
|
||||
req.method_name(),
|
||||
req.params(),
|
||||
);
|
||||
|
||||
self.calls_started
|
||||
.with_label_values(&[transport_label, req.method_name()])
|
||||
.inc();
|
||||
}
|
||||
|
||||
pub(crate) fn on_response(
|
||||
&self,
|
||||
req: &Request,
|
||||
rp: &MethodResponse,
|
||||
is_rate_limited: bool,
|
||||
transport_label: &'static str,
|
||||
now: Instant,
|
||||
) {
|
||||
log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now);
|
||||
log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={}", rp.as_result());
|
||||
|
||||
let micros = now.elapsed().as_micros();
|
||||
log::debug!(
|
||||
target: "rpc_metrics",
|
||||
"[{transport_label}] {} call took {} μs",
|
||||
req.method_name(),
|
||||
micros,
|
||||
);
|
||||
self.calls_time
|
||||
.with_label_values(&[
|
||||
transport_label,
|
||||
req.method_name(),
|
||||
if is_rate_limited { "true" } else { "false" },
|
||||
])
|
||||
.observe(micros as _);
|
||||
self.calls_finished
|
||||
.with_label_values(&[
|
||||
transport_label,
|
||||
req.method_name(),
|
||||
// the label "is_error", so `success` should be regarded as false
|
||||
// and vice-versa to be registered correctly.
|
||||
if rp.is_success() { "false" } else { "true" },
|
||||
if is_rate_limited { "true" } else { "false" },
|
||||
])
|
||||
.inc();
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics with transport label.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Metrics {
|
||||
pub(crate) inner: RpcMetrics,
|
||||
pub(crate) transport_label: &'static str,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
/// Create a new [`Metrics`].
|
||||
pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self {
|
||||
Self { inner: metrics, transport_label }
|
||||
}
|
||||
|
||||
pub(crate) fn ws_connect(&self) {
|
||||
self.inner.ws_connect();
|
||||
}
|
||||
|
||||
pub(crate) fn ws_disconnect(&self, now: Instant) {
|
||||
self.inner.ws_disconnect(now)
|
||||
}
|
||||
|
||||
pub(crate) fn on_call(&self, req: &Request) {
|
||||
self.inner.on_call(req, self.transport_label)
|
||||
}
|
||||
|
||||
pub(crate) fn on_response(
|
||||
&self,
|
||||
req: &Request,
|
||||
rp: &MethodResponse,
|
||||
is_rate_limited: bool,
|
||||
now: Instant,
|
||||
) {
|
||||
self.inner.on_response(req, rp, is_rate_limited, self.transport_label, now)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! JSON-RPC specific middleware.
|
||||
|
||||
use std::{
|
||||
num::NonZeroU32,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use futures::future::{BoxFuture, FutureExt};
|
||||
use governor::{clock::Clock, Jitter};
|
||||
use jsonrpsee::{
|
||||
server::middleware::rpc::RpcServiceT,
|
||||
types::{ErrorObject, Id, Request},
|
||||
MethodResponse,
|
||||
};
|
||||
|
||||
mod metrics;
|
||||
mod node_health;
|
||||
mod rate_limit;
|
||||
|
||||
pub use metrics::*;
|
||||
pub use node_health::*;
|
||||
pub use rate_limit::*;
|
||||
|
||||
const MAX_JITTER: Duration = Duration::from_millis(50);
|
||||
const MAX_RETRIES: usize = 10;
|
||||
|
||||
/// JSON-RPC middleware layer.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct MiddlewareLayer {
|
||||
rate_limit: Option<RateLimit>,
|
||||
metrics: Option<Metrics>,
|
||||
}
|
||||
|
||||
impl MiddlewareLayer {
|
||||
/// Create an empty MiddlewareLayer.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Enable new rate limit middleware enforced per minute.
|
||||
pub fn with_rate_limit_per_minute(self, n: NonZeroU32) -> Self {
|
||||
Self { rate_limit: Some(RateLimit::per_minute(n)), metrics: self.metrics }
|
||||
}
|
||||
|
||||
/// Enable metrics middleware.
|
||||
pub fn with_metrics(self, metrics: Metrics) -> Self {
|
||||
Self { rate_limit: self.rate_limit, metrics: Some(metrics) }
|
||||
}
|
||||
|
||||
/// Register a new websocket connection.
|
||||
pub fn ws_connect(&self) {
|
||||
self.metrics.as_ref().map(|m| m.ws_connect());
|
||||
}
|
||||
|
||||
/// Register that a websocket connection was closed.
|
||||
pub fn ws_disconnect(&self, now: Instant) {
|
||||
self.metrics.as_ref().map(|m| m.ws_disconnect(now));
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> tower::Layer<S> for MiddlewareLayer {
|
||||
type Service = Middleware<S>;
|
||||
|
||||
fn layer(&self, service: S) -> Self::Service {
|
||||
Middleware { service, rate_limit: self.rate_limit.clone(), metrics: self.metrics.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON-RPC middleware that handles metrics
|
||||
/// and rate-limiting.
|
||||
///
|
||||
/// These are part of the same middleware
|
||||
/// because the metrics needs to know whether
|
||||
/// a call was rate-limited or not because
|
||||
/// it will impact the roundtrip for a call.
|
||||
pub struct Middleware<S> {
|
||||
service: S,
|
||||
rate_limit: Option<RateLimit>,
|
||||
metrics: Option<Metrics>,
|
||||
}
|
||||
|
||||
impl<'a, S> RpcServiceT<'a> for Middleware<S>
|
||||
where
|
||||
S: Send + Sync + RpcServiceT<'a> + Clone + 'static,
|
||||
{
|
||||
type Future = BoxFuture<'a, MethodResponse>;
|
||||
|
||||
fn call(&self, req: Request<'a>) -> Self::Future {
|
||||
let now = Instant::now();
|
||||
|
||||
self.metrics.as_ref().map(|m| m.on_call(&req));
|
||||
|
||||
let service = self.service.clone();
|
||||
let rate_limit = self.rate_limit.clone();
|
||||
let metrics = self.metrics.clone();
|
||||
|
||||
async move {
|
||||
let mut is_rate_limited = false;
|
||||
|
||||
if let Some(limit) = rate_limit.as_ref() {
|
||||
let mut attempts = 0;
|
||||
let jitter = Jitter::up_to(MAX_JITTER);
|
||||
|
||||
loop {
|
||||
if attempts >= MAX_RETRIES {
|
||||
return reject_too_many_calls(req.id);
|
||||
}
|
||||
|
||||
if let Err(rejected) = limit.inner.check() {
|
||||
tokio::time::sleep(jitter + rejected.wait_time_from(limit.clock.now()))
|
||||
.await;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
is_rate_limited = true;
|
||||
attempts += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let rp = service.call(req.clone()).await;
|
||||
metrics.as_ref().map(|m| m.on_response(&req, &rp, is_rate_limited, now));
|
||||
|
||||
rp
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
fn reject_too_many_calls(id: Id) -> MethodResponse {
|
||||
MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>))
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Middleware for handling `/health` and `/health/readiness` endpoints.
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures::future::FutureExt;
|
||||
use http::{HeaderValue, Method, StatusCode, Uri};
|
||||
use jsonrpsee::{
|
||||
server::{HttpBody, HttpRequest, HttpResponse},
|
||||
types::{Response as RpcResponse, ResponseSuccess as RpcResponseSuccess},
|
||||
};
|
||||
use tower::Service;
|
||||
|
||||
const RPC_SYSTEM_HEALTH_CALL: &str = r#"{"jsonrpc":"2.0","method":"system_health","id":0}"#;
|
||||
const HEADER_VALUE_JSON: HeaderValue = HeaderValue::from_static("application/json; charset=utf-8");
|
||||
|
||||
/// Layer that applies [`NodeHealthProxy`] which
|
||||
/// proxies `/health` and `/health/readiness` endpoints.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct NodeHealthProxyLayer;
|
||||
|
||||
impl<S> tower::Layer<S> for NodeHealthProxyLayer {
|
||||
type Service = NodeHealthProxy<S>;
|
||||
|
||||
fn layer(&self, service: S) -> Self::Service {
|
||||
NodeHealthProxy::new(service)
|
||||
}
|
||||
}
|
||||
|
||||
/// Middleware that proxies `/health` and `/health/readiness` endpoints.
|
||||
pub struct NodeHealthProxy<S>(S);
|
||||
|
||||
impl<S> NodeHealthProxy<S> {
|
||||
/// Creates a new [`NodeHealthProxy`].
|
||||
pub fn new(service: S) -> Self {
|
||||
Self(service)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> tower::Service<http::Request<hyper::body::Incoming>> for NodeHealthProxy<S>
|
||||
where
|
||||
S: Service<HttpRequest, Response = HttpResponse>,
|
||||
S::Response: 'static,
|
||||
S::Error: Into<Box<dyn Error + Send + Sync>> + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = Box<dyn Error + Send + Sync + 'static>;
|
||||
type Future =
|
||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.0.poll_ready(cx).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: http::Request<hyper::body::Incoming>) -> Self::Future {
|
||||
let mut req = req.map(|body| HttpBody::new(body));
|
||||
let maybe_intercept = InterceptRequest::from_http(&req);
|
||||
|
||||
// Modify the request and proxy it to `system_health`
|
||||
if let InterceptRequest::Health | InterceptRequest::Readiness = maybe_intercept {
|
||||
// RPC methods are accessed with `POST`.
|
||||
*req.method_mut() = Method::POST;
|
||||
// Precautionary remove the URI.
|
||||
*req.uri_mut() = Uri::from_static("/");
|
||||
|
||||
// Requests must have the following headers:
|
||||
req.headers_mut().insert(http::header::CONTENT_TYPE, HEADER_VALUE_JSON);
|
||||
req.headers_mut().insert(http::header::ACCEPT, HEADER_VALUE_JSON);
|
||||
|
||||
// Adjust the body to reflect the method call.
|
||||
req = req.map(|_| HttpBody::from(RPC_SYSTEM_HEALTH_CALL));
|
||||
}
|
||||
|
||||
// Call the inner service and get a future that resolves to the response.
|
||||
let fut = self.0.call(req);
|
||||
|
||||
async move {
|
||||
Ok(match maybe_intercept {
|
||||
InterceptRequest::Deny =>
|
||||
http_response(StatusCode::METHOD_NOT_ALLOWED, HttpBody::empty()),
|
||||
InterceptRequest::No => fut.await.map_err(|err| err.into())?,
|
||||
InterceptRequest::Health => {
|
||||
let res = fut.await.map_err(|err| err.into())?;
|
||||
if let Ok(health) = parse_rpc_response(res.into_body()).await {
|
||||
http_ok_response(serde_json::to_string(&health)?)
|
||||
} else {
|
||||
http_internal_error()
|
||||
}
|
||||
},
|
||||
InterceptRequest::Readiness => {
|
||||
let res = fut.await.map_err(|err| err.into())?;
|
||||
match parse_rpc_response(res.into_body()).await {
|
||||
Ok(health)
|
||||
if (!health.is_syncing && health.peers > 0) ||
|
||||
!health.should_have_peers =>
|
||||
http_ok_response(HttpBody::empty()),
|
||||
_ => http_internal_error(),
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This is duplicated here to avoid dependency to the `RPC API`.
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct Health {
|
||||
/// Number of connected peers
|
||||
pub peers: usize,
|
||||
/// Is the node syncing
|
||||
pub is_syncing: bool,
|
||||
/// Should this node have any peers
|
||||
///
|
||||
/// Might be false for local chains or when running without discovery.
|
||||
pub should_have_peers: bool,
|
||||
}
|
||||
|
||||
fn http_ok_response<S: Into<HttpBody>>(body: S) -> HttpResponse {
|
||||
http_response(StatusCode::OK, body)
|
||||
}
|
||||
|
||||
fn http_response<S: Into<HttpBody>>(status_code: StatusCode, body: S) -> HttpResponse {
|
||||
HttpResponse::builder()
|
||||
.status(status_code)
|
||||
.header(http::header::CONTENT_TYPE, HEADER_VALUE_JSON)
|
||||
.body(body.into())
|
||||
.expect("Header is valid; qed")
|
||||
}
|
||||
|
||||
fn http_internal_error() -> HttpResponse {
|
||||
http_response(hyper::StatusCode::INTERNAL_SERVER_ERROR, HttpBody::empty())
|
||||
}
|
||||
|
||||
async fn parse_rpc_response(
|
||||
body: HttpBody,
|
||||
) -> Result<Health, Box<dyn Error + Send + Sync + 'static>> {
|
||||
use http_body_util::BodyExt;
|
||||
|
||||
let bytes = body.collect().await?.to_bytes();
|
||||
|
||||
let raw_rp = serde_json::from_slice::<RpcResponse<Health>>(&bytes)?;
|
||||
let rp = RpcResponseSuccess::<Health>::try_from(raw_rp)?;
|
||||
|
||||
Ok(rp.result)
|
||||
}
|
||||
|
||||
/// Whether the request should be treated as ordinary RPC call or be modified.
|
||||
enum InterceptRequest {
|
||||
/// Proxy `/health` to `system_health`.
|
||||
Health,
|
||||
/// Checks if node has at least one peer and is not doing major syncing.
|
||||
///
|
||||
/// Returns HTTP status code 200 on success otherwise HTTP status code 500 is returned.
|
||||
Readiness,
|
||||
/// Treat as a ordinary RPC call and don't modify the request or response.
|
||||
No,
|
||||
/// Deny health or readiness calls that is not HTTP GET request.
|
||||
///
|
||||
/// Returns HTTP status code 405.
|
||||
Deny,
|
||||
}
|
||||
|
||||
impl InterceptRequest {
|
||||
fn from_http(req: &HttpRequest) -> InterceptRequest {
|
||||
match req.uri().path() {
|
||||
"/health" =>
|
||||
if req.method() == http::Method::GET {
|
||||
InterceptRequest::Health
|
||||
} else {
|
||||
InterceptRequest::Deny
|
||||
},
|
||||
"/health/readiness" =>
|
||||
if req.method() == http::Method::GET {
|
||||
InterceptRequest::Readiness
|
||||
} else {
|
||||
InterceptRequest::Deny
|
||||
},
|
||||
// Forward all other requests to the RPC server.
|
||||
_ => InterceptRequest::No,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! RPC rate limit.
|
||||
|
||||
use governor::{
|
||||
clock::{DefaultClock, QuantaClock},
|
||||
middleware::NoOpMiddleware,
|
||||
state::{InMemoryState, NotKeyed},
|
||||
Quota,
|
||||
};
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
|
||||
type RateLimitInner = governor::RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>;
|
||||
|
||||
/// Rate limit.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RateLimit {
|
||||
pub(crate) inner: Arc<RateLimitInner>,
|
||||
pub(crate) clock: QuantaClock,
|
||||
}
|
||||
|
||||
impl RateLimit {
|
||||
/// Create a new `RateLimit` per minute.
|
||||
pub fn per_minute(n: NonZeroU32) -> Self {
|
||||
let clock = QuantaClock::default();
|
||||
Self {
|
||||
inner: Arc::new(RateLimitInner::direct_with_clock(Quota::per_minute(n), &clock)),
|
||||
clock,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user