Implement the http variant of the bank server.
During that process, many shortcomings with the socket server and the bank lib were fixed. I am aware a massive commit like this is not ideal.
This commit is contained in:
parent
c69654a924
commit
dac95b7dae
34 changed files with 1797 additions and 140 deletions
|
@ -7,4 +7,5 @@ edition = "2021"
|
|||
bank = { path = "../bank" }
|
||||
anyhow = "1.0.55"
|
||||
log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
thiserror = "1.0.30"
|
|
@ -12,7 +12,12 @@ use crate::protocol;
|
|||
pub struct CloseAccount;
|
||||
|
||||
impl Command for CloseAccount {
|
||||
fn execute(&self, bank: Arc<RwLock<Bank>>, data: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
data: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
debug!("account nr bytes {:?}", data);
|
||||
|
||||
let nr = String::from_utf8_lossy(data).to_string();
|
||||
|
@ -25,9 +30,9 @@ impl Command for CloseAccount {
|
|||
let mut acc = acc.write().unwrap();
|
||||
stream.write(&protocol::account_passivate(acc.passivate()))?
|
||||
}
|
||||
None => stream.write(&protocol::account_passivate(false))?
|
||||
None => stream.write(&protocol::account_passivate(false))?,
|
||||
};
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ use crate::protocol;
|
|||
pub struct CreateAccount;
|
||||
|
||||
impl Command for CreateAccount {
|
||||
fn execute(&self, bank: Arc<RwLock<Bank>>, data: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
data: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
debug!("owner nr bytes {:?}", data);
|
||||
|
||||
let owner = String::from_utf8_lossy(data);
|
||||
|
@ -25,4 +30,4 @@ impl Command for CreateAccount {
|
|||
let written = stream.write(&protocol::account_nr(&nr))?;
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ use crate::protocol;
|
|||
pub struct Deposit;
|
||||
|
||||
impl Command for Deposit {
|
||||
fn execute(&self, bank: Arc<RwLock<Bank>>, data: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
data: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
let value_bytes: [u8; 8] = <[u8; 8]>::try_from(data[..8].to_vec().as_slice())?;
|
||||
debug!("value bytes {:?}", value_bytes);
|
||||
|
||||
|
@ -30,9 +35,9 @@ impl Command for Deposit {
|
|||
let mut acc = acc.write().unwrap();
|
||||
stream.write(&protocol::deposit(acc.deposit(amount)))?
|
||||
}
|
||||
None => stream.write(&protocol::error(2))?
|
||||
None => stream.write(&protocol::error(2))?,
|
||||
};
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
35
socket-server/src/commands/error.rs
Normal file
35
socket-server/src/commands/error.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use bank::account::AccountError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub struct SocketAccountError(pub AccountError);
|
||||
|
||||
impl Display for SocketAccountError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SocketAccountError {
|
||||
type Target = AccountError;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocketAccountError> for u8 {
|
||||
fn from(e: SocketAccountError) -> Self {
|
||||
match e.deref() {
|
||||
AccountError::Overdraw => 11,
|
||||
AccountError::Inactive => 12,
|
||||
AccountError::InvalidAmount => 13,
|
||||
AccountError::NotFound => 14,
|
||||
AccountError::AccountNotZero => 15,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,9 +12,14 @@ use crate::protocol;
|
|||
pub struct Fail;
|
||||
|
||||
impl Command for Fail {
|
||||
fn execute(&self, _: Arc<RwLock<Bank>>, error_code: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
_: Arc<RwLock<Bank>>,
|
||||
error_code: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
error!("sending error code {}", error_code[0]);
|
||||
let written = stream.write(&protocol::error(error_code[0]))?;
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ use crate::protocol;
|
|||
pub struct GetAccount;
|
||||
|
||||
impl Command for GetAccount {
|
||||
fn execute(&self, bank: Arc<RwLock<Bank>>, data: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
data: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
debug!("account nr bytes {:?}", data);
|
||||
let nr = String::from_utf8_lossy(data).to_string();
|
||||
|
||||
|
@ -25,9 +30,9 @@ impl Command for GetAccount {
|
|||
let acc = acc.read().unwrap();
|
||||
stream.write(&protocol::account(&acc))?
|
||||
}
|
||||
None => stream.write(&protocol::error(2))?
|
||||
None => stream.write(&protocol::error(2))?,
|
||||
};
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,10 @@ impl Command for GetAccountNrs {
|
|||
info!("getting account numbers...");
|
||||
|
||||
let bank = bank.read().unwrap();
|
||||
let nrs: Vec<String> = bank.account_numbers()
|
||||
.into_iter()
|
||||
.collect();
|
||||
let nrs: Vec<String> = bank.account_numbers().into_iter().collect();
|
||||
|
||||
let written = stream.write(&protocol::account_nrs(&nrs))?;
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock};
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use bank::account::AccountError;
|
||||
use bank::bank::Bank;
|
||||
|
||||
use crate::commands::close_account::CloseAccount;
|
||||
|
@ -16,13 +15,14 @@ use crate::commands::get_account_nrs::GetAccountNrs;
|
|||
use crate::commands::pong::Pong;
|
||||
use crate::commands::withdraw::Withdraw;
|
||||
|
||||
mod pong;
|
||||
mod fail;
|
||||
mod close_account;
|
||||
mod create_account;
|
||||
mod deposit;
|
||||
pub mod error;
|
||||
mod fail;
|
||||
mod get_account;
|
||||
mod get_account_nrs;
|
||||
mod pong;
|
||||
mod withdraw;
|
||||
|
||||
pub trait Command: Sync + Send {
|
||||
|
@ -33,26 +33,6 @@ pub struct Commands {
|
|||
cmds: HashMap<u8, Box<dyn Command>>,
|
||||
}
|
||||
|
||||
struct WebAccountError(AccountError);
|
||||
|
||||
impl From<&WebAccountError> for u8 {
|
||||
fn from(error: &WebAccountError) -> Self {
|
||||
match error.0 {
|
||||
AccountError::Overdraw() => 11,
|
||||
AccountError::Inactive() => 12,
|
||||
AccountError::InvalidAmount() => 13,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_to_code(error: &AccountError) -> u8 {
|
||||
match error {
|
||||
AccountError::Overdraw() => 11,
|
||||
AccountError::Inactive() => 12,
|
||||
AccountError::InvalidAmount() => 13,
|
||||
}
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
pub fn new() -> Self {
|
||||
let mut cmds = HashMap::new();
|
||||
|
@ -64,15 +44,19 @@ impl Commands {
|
|||
cmds.insert(6_u8, Box::new(Deposit) as Box<dyn Command>);
|
||||
cmds.insert(7_u8, Box::new(Withdraw) as Box<dyn Command>);
|
||||
|
||||
Self {
|
||||
cmds
|
||||
}
|
||||
Self { cmds }
|
||||
}
|
||||
|
||||
pub fn run(&self, bank: Arc<RwLock<Bank>>, cmd: u8, data: &[u8], stream: &TcpStream) -> Result<usize> {
|
||||
pub fn run(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
cmd: u8,
|
||||
data: &[u8],
|
||||
stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
match self.cmds.get(&cmd) {
|
||||
None => Fail.execute(bank, &[1_u8], stream),
|
||||
Some(cmd) => cmd.execute(bank, data, stream)
|
||||
Some(cmd) => cmd.execute(bank, data, stream),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,4 +17,4 @@ impl Command for Pong {
|
|||
let written = stream.write(&[protocol::PONG])?;
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ use crate::protocol;
|
|||
pub struct Withdraw;
|
||||
|
||||
impl Command for Withdraw {
|
||||
fn execute(&self, bank: Arc<RwLock<Bank>>, data: &[u8], mut stream: &TcpStream) -> Result<usize> {
|
||||
fn execute(
|
||||
&self,
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
data: &[u8],
|
||||
mut stream: &TcpStream,
|
||||
) -> Result<usize> {
|
||||
let value_bytes: [u8; 8] = <[u8; 8]>::try_from(data[..8].to_vec().as_slice())?;
|
||||
debug!("value bytes {:?}", value_bytes);
|
||||
|
||||
|
@ -24,15 +29,14 @@ impl Command for Withdraw {
|
|||
info!("withdrawing {} from {}...", amount, nr);
|
||||
|
||||
let bank = bank.read().unwrap();
|
||||
|
||||
let written = match bank.accounts.get(&nr) {
|
||||
Some(acc) => {
|
||||
let mut acc = acc.write().unwrap();
|
||||
stream.write(&protocol::withdraw(acc.withdraw(amount)))?
|
||||
}
|
||||
None => stream.write(&protocol::error(2))?
|
||||
None => stream.write(&protocol::error(2))?,
|
||||
};
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
mod commands;
|
||||
mod protocol;
|
||||
mod server;
|
||||
mod threadpool;
|
||||
mod commands;
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
|
|
@ -2,18 +2,11 @@ use anyhow::Result;
|
|||
|
||||
use bank::account::{Account, AccountError};
|
||||
|
||||
use crate::commands::error_to_code;
|
||||
use crate::commands::error::SocketAccountError;
|
||||
|
||||
pub const START: [u8; 2] = [0xde, 0xad];
|
||||
pub const PONG: u8 = 0b0010_0000;
|
||||
|
||||
fn to_error_code(error: anyhow::Error, default: u8) -> u8 {
|
||||
match error.root_cause().downcast_ref::<AccountError>() {
|
||||
Some(e) => error_to_code(e),
|
||||
None => default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn account_nr(nr: &str) -> Vec<u8> {
|
||||
let mut response = vec![PONG];
|
||||
response.append(&mut nr.as_bytes().to_vec());
|
||||
|
@ -26,17 +19,17 @@ pub fn account_passivate(was_passivated: bool) -> Vec<u8> {
|
|||
vec![PONG | is_active_byte]
|
||||
}
|
||||
|
||||
pub fn deposit(result: Result<()>) -> Vec<u8> {
|
||||
pub fn deposit(result: Result<f64, AccountError>) -> Vec<u8> {
|
||||
match result {
|
||||
Err(e) => error(to_error_code(e, 10)).to_vec(),
|
||||
Ok(_) => vec![PONG]
|
||||
Err(e) => error(SocketAccountError(e).into()).to_vec(),
|
||||
Ok(_) => vec![PONG],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn withdraw(result: Result<()>) -> Vec<u8> {
|
||||
pub fn withdraw(result: Result<f64, AccountError>) -> Vec<u8> {
|
||||
match result {
|
||||
Err(e) => error(to_error_code(e, 10)).to_vec(),
|
||||
Ok(_) => vec![PONG]
|
||||
Err(e) => error(SocketAccountError(e).into()).to_vec(),
|
||||
Ok(_) => vec![PONG],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,4 +78,4 @@ pub fn account(account: &Account) -> Vec<u8> {
|
|||
|
||||
pub fn error(code: u8) -> [u8; 1] {
|
||||
[0b0100_0000 | code]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,11 @@ pub fn run(host: &str, threads: usize) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connection(bank: Arc<RwLock<Bank>>, cmds: &Commands, mut stream: TcpStream) -> Result<()> {
|
||||
fn handle_connection(
|
||||
bank: Arc<RwLock<Bank>>,
|
||||
cmds: &Commands,
|
||||
mut stream: TcpStream,
|
||||
) -> Result<()> {
|
||||
const BUF_SIZE: usize = 64;
|
||||
|
||||
let mut data: Vec<u8> = vec![];
|
||||
|
@ -58,4 +62,3 @@ fn handle_connection(bank: Arc<RwLock<Bank>>, cmds: &Commands, mut stream: TcpSt
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
|
||||
|
@ -33,8 +33,8 @@ impl ThreadPool {
|
|||
}
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
let job = Box::new(f);
|
||||
|
||||
|
@ -91,4 +91,4 @@ impl Worker {
|
|||
thread: Some(thread),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue