From 2d45df33b6a1d6da4ef9269d6fe5a54914f7b3d2 Mon Sep 17 00:00:00 2001 From: kostekIV <27210860+kostekIV@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:12:31 +0100 Subject: [PATCH 1/2] Add ready endpoint (#1) --- config.yml | 5 +- rpc_configs/substrate.yml | 4 + src/extensions/server/mod.rs | 5 +- src/extensions/server/proxy_get_request.rs | 2 +- src/extensions/server/ready_get_request.rs | 145 +++++++++++++++++++++ 5 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 src/extensions/server/ready_get_request.rs diff --git a/config.yml b/config.yml index c284bf2..c86b47e 100644 --- a/config.yml +++ b/config.yml @@ -1,8 +1,7 @@ extensions: client: endpoints: - - wss://acala-rpc.dwellir.com - - wss://acala-rpc-0.aca-api.network + - ws://localhost:9944 event_bus: substrate_api: stale_timeout_seconds: 180 # rotate endpoint if no new blocks for 3 minutes @@ -14,7 +13,7 @@ extensions: merge_subscription: keep_alive_seconds: 60 server: - port: 9944 + port: 9934 listen_address: '0.0.0.0' max_connections: 2000 http_methods: diff --git a/rpc_configs/substrate.yml b/rpc_configs/substrate.yml index c5d5b5c..5fe5bbe 100644 --- a/rpc_configs/substrate.yml +++ b/rpc_configs/substrate.yml @@ -178,6 +178,10 @@ methods: - name: accountId ty: AccountId + - method: alephNode_ready + cache: + size: 0 + subscriptions: - subscribe: author_submitAndWatchExtrinsic unsubscribe: author_unwatchExtrinsic diff --git a/src/extensions/server/mod.rs b/src/extensions/server/mod.rs index f052f0a..672fa33 100644 --- a/src/extensions/server/mod.rs +++ b/src/extensions/server/mod.rs @@ -19,7 +19,9 @@ use super::{Extension, ExtensionRegistry}; use crate::extensions::rate_limit::{MethodWeights, RateLimitBuilder, XFF}; mod proxy_get_request; +mod ready_get_request; use proxy_get_request::{ProxyGetRequestLayer, ProxyGetRequestMethod}; +use ready_get_request::ReadyProxyLayer; pub struct SubwayServerBuilder { pub config: ServerConfig, @@ -124,7 +126,8 @@ impl SubwayServerBuilder { .collect(), ) .expect("Invalid health config"), - ); + ) + .layer(ReadyProxyLayer); let rpc_module = rpc_module.clone(); let stop_handle = stop_handle.clone(); diff --git a/src/extensions/server/proxy_get_request.rs b/src/extensions/server/proxy_get_request.rs index 638fb97..9631d37 100644 --- a/src/extensions/server/proxy_get_request.rs +++ b/src/extensions/server/proxy_get_request.rs @@ -172,7 +172,7 @@ where } } -mod response { +pub mod response { use jsonrpsee::types::{error::ErrorCode, ErrorObjectOwned, Id, Response, ResponsePayload}; const JSON: &str = "application/json; charset=utf-8"; diff --git a/src/extensions/server/ready_get_request.rs b/src/extensions/server/ready_get_request.rs new file mode 100644 index 0000000..d6290d5 --- /dev/null +++ b/src/extensions/server/ready_get_request.rs @@ -0,0 +1,145 @@ +use std::{ + error::Error, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use crate::extensions::server::proxy_get_request::response::internal_error; +use futures::TryFutureExt; +use hyper::{ + header::{ACCEPT, CONTENT_TYPE}, + http::HeaderValue, + Body, Method, Request, Response, Uri, +}; +use jsonrpsee::types::{Id, RequestSer}; +use tower::{Layer, Service}; + +type BoxedError = Box; +type PinnedFuture = Pin, BoxedError>> + Send + 'static>>; + +/// Layer that catches requests to '/ready' endpoint redirects them to rpc call `alephNode_ready` +/// and translate the response to plain get response 200 or 503. +#[derive(Debug, Clone)] +pub struct ReadyProxyLayer; + +impl Layer for ReadyProxyLayer { + type Service = ReadyRequest; + + fn layer(&self, inner: S) -> Self::Service { + ReadyRequest::new(inner) + } +} + +fn ok_response() -> hyper::Response { + hyper::Response::builder() + .status(hyper::StatusCode::OK) + .body(Body::empty()) + .expect("Unable to parse response body for type conversion") +} + +fn bad_response() -> hyper::Response { + hyper::Response::builder() + .status(hyper::StatusCode::BAD_GATEWAY) + .body(Body::empty()) + .expect("Unable to parse response body for type conversion") +} + +#[derive(Debug, Clone)] +pub struct ReadyRequest { + inner: S, +} + +impl ReadyRequest +where + S: Service, Response = Response>, + S::Response: 'static, + S::Error: Into + 'static, + S::Future: Send + 'static, +{ + #[inline] + fn poll_ready_inner(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx).map_err(Into::into) + } + + fn prepare_request(req: &mut Request) { + const RPC_METHOD_NAME: &str = "alephNode_ready"; + + *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(CONTENT_TYPE, HeaderValue::from_static("application/json")); + req.headers_mut() + .insert(ACCEPT, HeaderValue::from_static("application/json")); + + // Adjust the body to reflect the method call. + let body = Body::from( + serde_json::to_string(&RequestSer::borrowed(&Id::Number(0), &RPC_METHOD_NAME, None)) + .expect("Valid request; qed"), + ); + *req.body_mut() = body; + } + + pub fn call_proxy(&mut self, mut req: Request) -> PinnedFuture { + const METHOD_NAME: &str = "/ready"; + + if req.uri().path() != METHOD_NAME { + return Box::pin(self.inner.call(req).map_err(Into::into)); + } + + Self::prepare_request(&mut req); + + let fut = self.inner.call(req); + + let res_fut = async move { + let res = fut.await.map_err(|err| err.into())?; + + let body = res.into_body(); + let bytes = hyper::body::to_bytes(body).await?; + #[derive(serde::Deserialize, Debug)] + struct RpcPayload { + result: bool, + } + + let response = match serde_json::from_slice::(&bytes) { + Ok(RpcPayload { result }) if result => ok_response(), + Ok(_) => bad_response(), + _ => internal_error(), + }; + + Ok(response) + }; + + Box::pin(res_fut) + } +} + +impl ReadyRequest { + pub fn new(inner: S) -> Self { + Self { inner } + } +} + +impl Service> for ReadyRequest +where + S: Service, Response = Response>, + S::Response: 'static, + S::Error: Into + 'static, + S::Future: Send + 'static, +{ + type Response = Response; + type Error = BoxedError; + type Future = PinnedFuture; + + #[inline] + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready_inner(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + self.call_proxy(req) + } +} From c0ee78aa1f7dc085b51b9da969688b58d15a1796 Mon Sep 17 00:00:00 2001 From: kostekIV <27210860+kostekIV@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:24:29 +0100 Subject: [PATCH 2/2] adjust ci (#2) --- .github/workflows/docker.yml | 43 ------------------------------------ .github/workflows/test.yml | 4 ++-- 2 files changed, 2 insertions(+), 45 deletions(-) delete mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index c61a73d..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: docker - -on: - workflow_dispatch: - inputs: - branch: - description: Branch to build docker image (optional). - required: false - type: string - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.inputs.branch || github.ref }} - submodules: recursive - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: acala/${{ github.event.repository.name }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=ref,event=tag - type=sha - type=sha,format=long - type=raw,value=latest,enable={{is_default_branch}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ae7ade..985b744 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,11 +2,11 @@ name: Test on: push: - branches: [ "master" ] + branches: [ "proxologist" ] paths-ignore: - '**/README.md' pull_request: - branches: [ "master" ] + branches: [ "proxologist" ] env: CARGO_TERM_COLOR: always