commit 936d49c62955e5163604003aa6be4be80e05f660
parent f86929453485b03617e253d5ab4c30fb811635df
Author: Antoine A <>
Date: Wed, 24 Nov 2021 16:20:07 +0100
Check method and endpoint
Diffstat:
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);
}