improved error handling #1
This commit is contained in:
+22
-13
@@ -4,8 +4,6 @@ mod set_hat;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{BufRead, BufReader},
|
|
||||||
net::TcpStream,
|
|
||||||
sync::{mpsc, Arc},
|
sync::{mpsc, Arc},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ impl Session {
|
|||||||
|
|
||||||
std::thread::spawn(move || match stream.read() {
|
std::thread::spawn(move || match stream.read() {
|
||||||
Some(session_token) => token_send.send(Ok(session_token)),
|
Some(session_token) => token_send.send(Ok(session_token)),
|
||||||
_ => token_send.send(Err(crate::response::Error::InvalidHandshake)),
|
_ => token_send.send(Err(crate::response::Error::InvalidHandshake("Failed to read session token".to_string()))),
|
||||||
});
|
});
|
||||||
|
|
||||||
match token_recv.recv_timeout(std::time::Duration::from_secs(20)) {
|
match token_recv.recv_timeout(std::time::Duration::from_secs(20)) {
|
||||||
@@ -48,17 +46,17 @@ impl Session {
|
|||||||
let response = minreq::get("https://api.minecraftservices.com/minecraft/profile")
|
let response = minreq::get("https://api.minecraftservices.com/minecraft/profile")
|
||||||
.with_header("Authorization", &format!("Bearer {session_token}"))
|
.with_header("Authorization", &format!("Bearer {session_token}"))
|
||||||
.send()
|
.send()
|
||||||
.map_err(|_| crate::response::Error::InvalidSession)?;
|
.map_err(|_| crate::response::Error::InvalidSession("Failed to validate session".to_string()))?;
|
||||||
|
|
||||||
// If the session is invalid, return an error
|
// If the session is invalid, return an error
|
||||||
if response.status_code != 200 {
|
if response.status_code != 200 {
|
||||||
return Err(crate::response::Error::InvalidSession);
|
return Err(crate::response::Error::InvalidSession(format!("Invalid session status code: {}", response.status_code)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the player data
|
// Parse the player data
|
||||||
let local_player: LocalPlayer = response
|
let local_player: LocalPlayer = response
|
||||||
.json()
|
.json()
|
||||||
.map_err(|_| crate::response::Error::InvalidSession)?;
|
.map_err(|_| crate::response::Error::InvalidSession("Failed to parse player data".to_string()))?;
|
||||||
|
|
||||||
println!("[MOJANG] {} successfully logged on", &local_player.name);
|
println!("[MOJANG] {} successfully logged on", &local_player.name);
|
||||||
|
|
||||||
@@ -74,8 +72,8 @@ impl Session {
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(mpsc::RecvTimeoutError::Timeout) => Err(crate::response::Error::Timeout),
|
Err(mpsc::RecvTimeoutError::Timeout) => Err(crate::response::Error::Timeout("Session handshake timed out".to_string())),
|
||||||
_ => Err(crate::response::Error::InvalidHandshake),
|
_ => Err(crate::response::Error::InvalidHandshake("Failed to receive handshake response".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,18 +89,29 @@ impl Session {
|
|||||||
|
|
||||||
"players" => {
|
"players" => {
|
||||||
let mut players: Vec<PlayerResponse> = Vec::new();
|
let mut players: Vec<PlayerResponse> = Vec::new();
|
||||||
for uuid in params.parse_param::<String>("uuids")?.split("$") {
|
let uuids = params.parse_param::<String>("uuids")?;
|
||||||
|
if uuids.is_empty() {
|
||||||
|
return Err(crate::response::Error::InvalidParameter {
|
||||||
|
param: "uuids".to_string(),
|
||||||
|
reason: "UUIDs list cannot be empty".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for uuid in uuids.split("$") {
|
||||||
|
if uuid.is_empty() {
|
||||||
|
return Err(crate::response::Error::InvalidParameter {
|
||||||
|
param: "uuids".to_string(),
|
||||||
|
reason: "UUID cannot be empty".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
match player::player(self, uuid.to_string())? {
|
match player::player(self, uuid.to_string())? {
|
||||||
Response::Player(p) => {
|
Response::Player(p) => players.push(p),
|
||||||
players.push(p);
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Response::Players(players))
|
Ok(Response::Players(players))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(crate::response::Error::InvalidRequest),
|
_ => Err(crate::response::Error::InvalidMethod(method.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-19
@@ -7,26 +7,32 @@ use super::{player, Session};
|
|||||||
pub fn set_cloak(session: &Session, cloak: String) -> Result {
|
pub fn set_cloak(session: &Session, cloak: String) -> Result {
|
||||||
match player::player(session, session.local_player.id.clone())? {
|
match player::player(session, session.local_player.id.clone())? {
|
||||||
Response::Player(crate::response::PlayerResponse::Player { cloaks, .. }) => {
|
Response::Player(crate::response::PlayerResponse::Player { cloaks, .. }) => {
|
||||||
if cloaks.contains(&cloak) || cloak.is_empty() {
|
if !cloaks.contains(&cloak) && !cloak.is_empty() {
|
||||||
session
|
return Err(Error::ValidationError(
|
||||||
.database
|
format!("Player does not own cloak: {}", cloak)
|
||||||
.players
|
));
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"uuid": session.local_player.id.clone(),
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"cloak": cloak,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.run()?;
|
|
||||||
Ok(Response::Success)
|
|
||||||
} else {
|
|
||||||
Err(Error::SomethingWentWrong)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session
|
||||||
|
.database
|
||||||
|
.players
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"uuid": session.local_player.id.clone(),
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"cloak": cloak.clone(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
.map_err(|e| Error::DatabaseError(
|
||||||
|
format!("Failed to update cloak: {}", e)
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(Response::Success)
|
||||||
}
|
}
|
||||||
_ => Err(Error::SomethingWentWrong),
|
_ => Err(Error::ValidationError("Invalid player data received".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-20
@@ -7,27 +7,32 @@ use super::{player, Session};
|
|||||||
pub fn set_hat(session: &Session, hat: String) -> Result {
|
pub fn set_hat(session: &Session, hat: String) -> Result {
|
||||||
match player::player(session, session.local_player.id.clone())? {
|
match player::player(session, session.local_player.id.clone())? {
|
||||||
Response::Player(crate::response::PlayerResponse::Player { hats, .. }) => {
|
Response::Player(crate::response::PlayerResponse::Player { hats, .. }) => {
|
||||||
if hats.contains(&hat) || hat.is_empty() {
|
if !hats.contains(&hat) && !hat.is_empty() {
|
||||||
session
|
return Err(Error::ValidationError(
|
||||||
.database
|
format!("Player does not own hat: {}", hat)
|
||||||
.players
|
));
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"uuid": session.local_player.id.clone(),
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"hat": hat,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.run()?;
|
|
||||||
|
|
||||||
Ok(Response::Success)
|
|
||||||
} else {
|
|
||||||
Err(Error::SomethingWentWrong)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session
|
||||||
|
.database
|
||||||
|
.players
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"uuid": session.local_player.id.clone(),
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"hat": hat.clone(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
.map_err(|e| Error::DatabaseError(
|
||||||
|
format!("Failed to update hat: {}", e)
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(Response::Success)
|
||||||
}
|
}
|
||||||
_ => Err(Error::SomethingWentWrong),
|
_ => Err(Error::ValidationError("Invalid player data received".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-10
@@ -7,23 +7,43 @@ pub trait ParamMap {
|
|||||||
impl ParamMap for HashMap<String, String> {
|
impl ParamMap for HashMap<String, String> {
|
||||||
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, crate::response::Error> {
|
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, crate::response::Error> {
|
||||||
match self.get(param) {
|
match self.get(param) {
|
||||||
Some(value) => T::from_str(value).map_err(|_| crate::response::Error::InvalidParameter),
|
Some(value) => T::from_str(value).map_err(|_| crate::response::Error::InvalidParameter {
|
||||||
None => Err(crate::response::Error::InvalidParameter),
|
param: param.to_string(),
|
||||||
|
reason: "Failed to parse parameter value".to_string(),
|
||||||
|
}),
|
||||||
|
None => Err(crate::response::Error::ParameterNotFound(param.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(s: &str) -> std::io::Result<(String, HashMap<String, String>)> {
|
pub fn parse(s: &str) -> Result<(String, HashMap<String, String>), crate::response::Error> {
|
||||||
let mut parts = s.split("@");
|
let mut parts = s.split("@");
|
||||||
|
|
||||||
let method = parts.next().unwrap().trim().to_lowercase();
|
let method = parts.next()
|
||||||
|
.ok_or_else(|| crate::response::Error::InvalidRequest("Empty request".to_string()))?
|
||||||
|
.trim()
|
||||||
|
.to_lowercase();
|
||||||
|
|
||||||
let params = parts
|
if method.is_empty() {
|
||||||
.map(|p| {
|
return Err(crate::response::Error::InvalidRequest("Method name cannot be empty".to_string()));
|
||||||
let parts = p.split("=").collect::<Vec<&str>>();
|
}
|
||||||
(parts[0].trim().to_string(), parts[1].trim().to_string())
|
|
||||||
})
|
let mut params = HashMap::new();
|
||||||
.collect::<HashMap<String, String>>();
|
for part in parts {
|
||||||
|
let key_value: Vec<&str> = part.split("=").collect();
|
||||||
|
match key_value.as_slice() {
|
||||||
|
[key, value] => {
|
||||||
|
let key = key.trim();
|
||||||
|
if key.is_empty() {
|
||||||
|
return Err(crate::response::Error::InvalidRequest("Parameter key cannot be empty".to_string()));
|
||||||
|
}
|
||||||
|
params.insert(key.to_string(), value.trim().to_string());
|
||||||
|
},
|
||||||
|
_ => return Err(crate::response::Error::InvalidRequest(
|
||||||
|
format!("Invalid parameter format in part: {}", part)
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok((method, params))
|
Ok((method, params))
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-19
@@ -62,29 +62,33 @@ impl std::fmt::Display for Response {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
InvalidRequest,
|
InvalidRequest(String),
|
||||||
InvalidMethod,
|
InvalidMethod(String),
|
||||||
InvalidParameter,
|
InvalidParameter { param: String, reason: String },
|
||||||
ParameterNotFound,
|
ParameterNotFound(String),
|
||||||
InvalidSession,
|
InvalidSession(String),
|
||||||
InvalidHandshake,
|
InvalidHandshake(String),
|
||||||
DatabaseError,
|
DatabaseError(String),
|
||||||
SomethingWentWrong,
|
NetworkError(String),
|
||||||
Timeout,
|
Timeout(String),
|
||||||
|
AuthenticationError(String),
|
||||||
|
ValidationError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::InvalidRequest => write!(f, "Invalid request"),
|
Error::InvalidRequest(msg) => write!(f, "Invalid request: {}", msg),
|
||||||
Error::InvalidMethod => write!(f, "Invalid method"),
|
Error::InvalidMethod(method) => write!(f, "Invalid method: {}", method),
|
||||||
Error::InvalidParameter => write!(f, "Invalid parameter"),
|
Error::InvalidParameter { param, reason } => write!(f, "Invalid parameter '{}': {}", param, reason),
|
||||||
Error::ParameterNotFound => write!(f, "Parameter not found"),
|
Error::ParameterNotFound(param) => write!(f, "Required parameter not found: {}", param),
|
||||||
Error::InvalidSession => write!(f, "Invalid session"),
|
Error::InvalidSession(details) => write!(f, "Invalid session: {}", details),
|
||||||
Error::InvalidHandshake => write!(f, "Invalid handshake"),
|
Error::InvalidHandshake(details) => write!(f, "Handshake failed: {}", details),
|
||||||
Error::DatabaseError => write!(f, "Database error"),
|
Error::DatabaseError(details) => write!(f, "Database error: {}", details),
|
||||||
Error::SomethingWentWrong => write!(f, "Something went wrong"),
|
Error::NetworkError(details) => write!(f, "Network error: {}", details),
|
||||||
Error::Timeout => write!(f, "Timeout"),
|
Error::Timeout(operation) => write!(f, "Operation timed out: {}", operation),
|
||||||
|
Error::AuthenticationError(details) => write!(f, "Authentication failed: {}", details),
|
||||||
|
Error::ValidationError(details) => write!(f, "Validation failed: {}", details),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +96,7 @@ impl std::fmt::Display for Error {
|
|||||||
impl From<mongodb::error::Error> for Error {
|
impl From<mongodb::error::Error> for Error {
|
||||||
fn from(e: mongodb::error::Error) -> Self {
|
fn from(e: mongodb::error::Error) -> Self {
|
||||||
println!("{e}");
|
println!("{e}");
|
||||||
Error::DatabaseError
|
Error::DatabaseError(e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user