depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit 936d49c62955e5163604003aa6be4be80e05f660
parent f86929453485b03617e253d5ab4c30fb811635df
Author: Antoine A <>
Date:   Wed, 24 Nov 2021 16:20:07 +0100

Check method and endpoint

Diffstat:
Mscript/test_bank.sh | 11++++++++++-
Mwire-gateway/src/api_common.rs | 32++++++++++++--------------------
Mwire-gateway/src/error_codes.rs | 1+
Mwire-gateway/src/main.rs | 70++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
4 files changed, 75 insertions(+), 39 deletions(-)

diff --git a/script/test_bank.sh b/script/test_bank.sh @@ -49,7 +49,16 @@ echo -n "Requesting exchange's outgoing transaction list..." taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -o | grep TESTKUDOS:2 > /dev/null echo " OK" - echo "All tests passed" + +# ---- Endpoint & Method Error ----- # + +echo -n "Unknown endpoint..." +test `curl -w %{http_code} -s -o /dev/null ${BANK_ENDPOINT}test` -eq 404 && echo " OK" || echo " Failed" + +echo -n "Method not allowed..." +test `curl -w %{http_code} -s -o /dev/null ${BANK_ENDPOINT}transfer` -eq 405 && echo " OK" || echo " Failed" + + exit 0 diff --git a/wire-gateway/src/api_common.rs b/wire-gateway/src/api_common.rs @@ -10,18 +10,18 @@ use serde_json::Value; /// <https://docs.taler.net/core/api-common.html#tsref-type-ErrorDetail> #[derive(Debug, Clone, serde::Serialize)] -struct ErrorDetail { - code: i64, - hint: Option<String>, - detail: Option<String>, - parameter: Option<String>, - path: Option<String>, - offset: Option<String>, - index: Option<String>, - object: Option<String>, - currency: Option<String>, - type_expected: Option<String>, - type_actual: Option<String>, +pub struct ErrorDetail { + pub code: i64, + pub hint: Option<String>, + pub detail: Option<String>, + pub parameter: Option<String>, + pub path: Option<String>, + pub offset: Option<String>, + pub index: Option<String>, + pub object: Option<String>, + pub currency: Option<String>, + pub type_expected: Option<String>, + pub type_actual: Option<String>, } /// <https://docs.taler.net/core/api-common.html#tsref-type-Timestamp> @@ -123,14 +123,6 @@ pub enum ParseSafeUint64Error { Format(#[from] ParseIntError), } -impl FromStr for SafeUint64 { - type Err = ParseSafeUint64Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(SafeUint64::try_from(s.parse::<u64>()?)?) - } -} - /// <https://docs.taler.net/core/api-common.html#tsref-type-Amount> #[derive( Debug, Clone, PartialEq, Eq, serde_with::DeserializeFromStr, serde_with::SerializeDisplay, diff --git a/wire-gateway/src/error_codes.rs b/wire-gateway/src/error_codes.rs @@ -25,6 +25,7 @@ #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] #[allow(dead_code)] +#[repr(u32)] pub enum ErrorCode { /** Special code to indicate success (no error). diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs @@ -1,6 +1,7 @@ use api_common::{Amount, SafeUint64, ShortHashCode, Timestamp}; use api_wire::{OutgoingBankTransaction, OutgoingHistory}; use async_compression::tokio::bufread::ZlibDecoder; +use error_codes::ErrorCode; use hyper::{ header, http::request::Parts, @@ -9,9 +10,12 @@ use hyper::{ }; use tokio::{io::AsyncReadExt, sync::Mutex}; -use crate::api_wire::{ - AddIncomingRequest, AddIncomingResponse, HistoryParams, IncomingBankTransaction, - IncomingHistory, TransferRequest, TransferResponse, +use crate::{ + api_common::ErrorDetail, + api_wire::{ + AddIncomingRequest, AddIncomingResponse, HistoryParams, IncomingBankTransaction, + IncomingHistory, TransferRequest, TransferResponse, + }, }; mod error_codes; @@ -27,7 +31,28 @@ async fn main() { let addr = ([0, 0, 0, 0], 8080).into(); let make_service = make_service_fn(move |_| async move { Ok::<_, Error>(service_fn(move |req| async move { - let response = router(req, state).await.unwrap_or_else(|e| e); + let response = match router(req, state).await { + Ok(resp) => resp, + Err((status, err)) => { + json_response( + status, + &ErrorDetail { + code: err as i64, + hint: None, + detail: None, + parameter: None, + path: None, + offset: None, + index: None, + object: None, + currency: None, + type_expected: None, + type_actual: None, + }, + ) + .await + } + }; Ok::<Response<Body>, Error>(response) })) }); @@ -65,11 +90,6 @@ struct ServerState { pub mod api_common; pub mod api_wire; -struct ServerError { - status: StatusCode, - detail: error_codes::ErrorCode, -} - async fn parse_json<J: serde::de::DeserializeOwned>(parts: &Parts, body: Body) -> J { let bytes = hyper::body::to_bytes(body).await.unwrap(); let mut buf = Vec::new(); @@ -103,13 +123,27 @@ async fn json_response<J: serde::Serialize>(status: StatusCode, json: &J) -> Res .unwrap() } +type ServerErr = (StatusCode, ErrorCode); + +fn assert_method(parts: &Parts, method: Method) -> Result<(), ServerErr> { + if parts.method == method { + Ok(()) + } else { + Err(( + StatusCode::METHOD_NOT_ALLOWED, + ErrorCode::GENERIC_METHOD_INVALID, + )) + } +} + async fn router( req: Request<Body>, state: &'static ServerState, -) -> Result<Response<Body>, Response<Body>> { +) -> Result<Response<Body>, (StatusCode, ErrorCode)> { let (parts, body) = req.into_parts(); let response = match parts.uri.path() { - "/transfer" if parts.method == Method::POST => { + "/transfer" => { + assert_method(&parts, Method::POST)?; let request: TransferRequest = parse_json(&parts, body).await; let mut guard = state.outgoing.lock().await; let row_id = SafeUint64::try_from(guard.len() as u64 + 1).unwrap(); @@ -123,7 +157,8 @@ async fn router( }); json_response(StatusCode::OK, &TransferResponse { timestamp, row_id }).await } - "/history/incoming" if parts.method == Method::GET => { + "/history/incoming" => { + assert_method(&parts, Method::GET)?; let params: HistoryParams = serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); let guard = state.incoming.lock().await; @@ -146,7 +181,8 @@ async fn router( ) .await } - "/history/outgoing" if parts.method == Method::GET => { + "/history/outgoing" => { + assert_method(&parts, Method::GET)?; let params: HistoryParams = serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); let guard = state.outgoing.lock().await; @@ -170,7 +206,8 @@ async fn router( ) .await } - "/admin/add-incoming" if parts.method == Method::POST => { + "/admin/add-incoming" => { + assert_method(&parts, Method::POST)?; let request: AddIncomingRequest = parse_json(&parts, body).await; let mut guard = state.incoming.lock().await; let row_id = SafeUint64::try_from(guard.len() as u64 + 1).unwrap(); @@ -184,10 +221,7 @@ async fn router( }); json_response(StatusCode::OK, &AddIncomingResponse { timestamp, row_id }).await } - _ => Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap(), + _ => return Err((StatusCode::NOT_FOUND, ErrorCode::GENERIC_ENDPOINT_UNKNOWN)), }; return Ok(response); }