Skip to content

Commit

Permalink
Merge pull request #131 from kinode-dao/develop
Browse files Browse the repository at this point in the history
update error types
  • Loading branch information
dr-frmr authored Dec 23, 2024
2 parents d791baa + 2ba06dd commit 2ed6d60
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 183 deletions.
67 changes: 33 additions & 34 deletions src/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ pub enum HttpClientAction {
},
}

/// HTTP Request type that can be shared over Wasm boundary to apps.
/// This is the one you send to the `http-client:distro:sys` service.
/// HTTP Request type contained in [`HttpClientAction::Http`].
///
/// BODY is stored in the lazy_load_blob, as bytes
///
/// TIMEOUT is stored in the message expect_response value
/// TIMEOUT is stored in the message's `expects_response` value
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OutgoingHttpRequest {
/// must parse to [`http::Method`]
Expand Down Expand Up @@ -63,32 +62,38 @@ pub enum HttpClientRequest {

/// [`crate::Response`] type received from the `http-client:distro:sys` service after
/// sending a successful [`HttpClientAction`] to it.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum HttpClientResponse {
Http(HttpResponse),
WebSocketAck,
}

#[derive(Error, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Error, Serialize, Deserialize)]
pub enum HttpClientError {
// HTTP errors
#[error("http-client: request is not valid HttpClientRequest: {req}.")]
BadRequest { req: String },
#[error("http-client: http method not supported: {method}.")]
#[error("request could not be deserialized to valid HttpClientRequest")]
MalformedRequest,
#[error("http method not supported: {method}")]
BadMethod { method: String },
#[error("http-client: url could not be parsed: {url}.")]
#[error("url could not be parsed: {url}")]
BadUrl { url: String },
#[error("http-client: http version not supported: {version}.")]
#[error("http version not supported: {version}")]
BadVersion { version: String },
#[error("http-client: failed to execute request {error}.")]
RequestFailed { error: String },
#[error("client failed to build request: {0}")]
BuildRequestFailed(String),
#[error("client failed to execute request: {0}")]
ExecuteRequestFailed(String),

// WebSocket errors
#[error("websocket_client: failed to open connection {url}.")]
#[error("could not open connection to {url}")]
WsOpenFailed { url: String },
#[error("websocket_client: failed to send message {req}.")]
WsPushFailed { req: String },
#[error("websocket_client: failed to close connection {channel_id}.")]
#[error("sent WebSocket push to unknown channel {channel_id}")]
WsPushUnknownChannel { channel_id: u32 },
#[error("WebSocket push failed because message had no blob attached")]
WsPushNoBlob,
#[error("WebSocket push failed because message type was Text, but blob was not a valid UTF-8 string")]
WsPushBadText,
#[error("failed to close connection {channel_id} because it was not open")]
WsCloseFailed { channel_id: u32 },
}

Expand Down Expand Up @@ -141,48 +146,42 @@ pub fn send_request_await_response(
url: url.to_string(),
headers: headers.unwrap_or_default(),
}))
.map_err(|e| HttpClientError::BadRequest {
req: format!("{e:?}"),
})?,
.map_err(|_| HttpClientError::MalformedRequest)?,
)
.blob_bytes(body)
.send_and_await_response(timeout)
.unwrap();
let Ok(Message::Response { body, .. }) = res else {
return Err(HttpClientError::RequestFailed {
error: "http-client timed out".to_string(),
});
return Err(HttpClientError::ExecuteRequestFailed(
"http-client timed out".to_string(),
));
};
let resp = match serde_json::from_slice::<
std::result::Result<HttpClientResponse, HttpClientError>,
>(&body)
{
Ok(Ok(HttpClientResponse::Http(resp))) => resp,
Ok(Ok(HttpClientResponse::WebSocketAck)) => {
return Err(HttpClientError::RequestFailed {
error: "http-client gave unexpected response".to_string(),
})
return Err(HttpClientError::ExecuteRequestFailed(
"http-client gave unexpected response".to_string(),
))
}
Ok(Err(e)) => return Err(e),
Err(e) => {
return Err(HttpClientError::RequestFailed {
error: format!("http-client gave invalid response: {e:?}"),
})
return Err(HttpClientError::ExecuteRequestFailed(format!(
"http-client gave invalid response: {e:?}"
)))
}
};
let mut http_response = http::Response::builder()
.status(http::StatusCode::from_u16(resp.status).unwrap_or_default());
let headers = http_response.headers_mut().unwrap();
for (key, value) in &resp.headers {
let Ok(key) = http::header::HeaderName::from_str(key) else {
return Err(HttpClientError::RequestFailed {
error: format!("http-client gave invalid header key: {key}"),
});
continue;
};
let Ok(value) = http::header::HeaderValue::from_str(value) else {
return Err(HttpClientError::RequestFailed {
error: format!("http-client gave invalid header value: {value}"),
});
continue;
};
headers.insert(key, value);
}
Expand Down
34 changes: 15 additions & 19 deletions src/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,14 +268,16 @@ impl HttpResponse {
/// Part of the [`crate::Response`] type issued by http-server
#[derive(Clone, Debug, Error, Serialize, Deserialize)]
pub enum HttpServerError {
#[error("request could not be parsed to HttpServerAction: {req}.")]
BadRequest { req: String },
#[error("action expected lazy_load_blob")]
#[error("request could not be deserialized to valid HttpServerRequest")]
MalformedRequest,
#[error("action expected blob")]
NoBlob,
#[error("path binding error: {error}")]
PathBindError { error: String },
#[error("WebSocket error: {error}")]
WebSocketPushError { error: String },
#[error("path binding error: invalid source process")]
InvalidSourceProcess,
#[error("WebSocket error: ping/pong message too long")]
WsPingPongTooLong,
#[error("WebSocket error: channel not found")]
WsChannelNotFound,
/// Not actually issued by `http-server:distro:sys`, just this library
#[error("timeout")]
Timeout,
Expand Down Expand Up @@ -683,9 +685,7 @@ impl HttpServer {
let entry = self
.http_paths
.get_mut(path)
.ok_or(HttpServerError::PathBindError {
error: "path not found".to_string(),
})?;
.ok_or(HttpServerError::MalformedRequest)?;
let res = KiRequest::to(("our", "http-server", "distro", "sys"))
.body(
serde_json::to_vec(&HttpServerAction::Bind {
Expand Down Expand Up @@ -722,9 +722,7 @@ impl HttpServer {
let entry = self
.ws_paths
.get_mut(path)
.ok_or(HttpServerError::PathBindError {
error: "path not found".to_string(),
})?;
.ok_or(HttpServerError::MalformedRequest)?;
let res = KiRequest::to(("our", "http-server", "distro", "sys"))
.body(if entry.secure_subdomain {
serde_json::to_vec(&HttpServerAction::WebSocketSecureBind {
Expand Down Expand Up @@ -826,7 +824,7 @@ impl HttpServer {
),
action: VfsAction::Read,
})
.map_err(|e| HttpServerError::BadRequest { req: e.to_string() })?,
.map_err(|_| HttpServerError::MalformedRequest)?,
)
.send_and_await_response(self.timeout)
.unwrap();
Expand Down Expand Up @@ -861,7 +859,7 @@ impl HttpServer {
path: file_path.to_string(),
action: VfsAction::Read,
})
.map_err(|e| HttpServerError::BadRequest { req: e.to_string() })?,
.map_err(|_| HttpServerError::MalformedRequest)?,
)
.send_and_await_response(self.timeout)
.unwrap();
Expand Down Expand Up @@ -911,9 +909,7 @@ impl HttpServer {
.send_and_await_response(self.timeout)
.unwrap()
else {
return Err(HttpServerError::PathBindError {
error: format!("no ui directory to serve: {initial_path}"),
});
return Err(HttpServerError::MalformedRequest);
};

let directory_body = serde_json::from_slice::<VfsResponse>(directory_response.body())
Expand Down Expand Up @@ -976,7 +972,7 @@ impl HttpServer {

pub fn parse_request(&self, body: &[u8]) -> Result<HttpServerRequest, HttpServerError> {
let request = serde_json::from_slice::<HttpServerRequest>(body)
.map_err(|e| HttpServerError::BadRequest { req: e.to_string() })?;
.map_err(|_| HttpServerError::MalformedRequest)?;
Ok(request)
}

Expand Down
27 changes: 7 additions & 20 deletions src/vfs/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ impl Directory {
let message = vfs_request(&self.path, VfsAction::ReadDir)
.send_and_await_response(self.timeout)
.unwrap()
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: self.path.clone(),
})?;
.map_err(|e| VfsError::SendError(e.kind))?;

match parse_response(message.body())? {
VfsResponse::ReadDir(entries) => Ok(entries),
Expand All @@ -39,17 +36,13 @@ pub fn open_dir(path: &str, create: bool, timeout: Option<u64>) -> Result<Direct
let message = vfs_request(path, VfsAction::Metadata)
.send_and_await_response(timeout)
.unwrap()
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: path.to_string(),
})?;
.map_err(|e| VfsError::SendError(e.kind))?;
match parse_response(message.body())? {
VfsResponse::Metadata(m) => {
if m.file_type != FileType::Directory {
return Err(VfsError::IOError {
error: "Entry at path not a directory".to_string(),
path: path.to_string(),
});
return Err(VfsError::IOError(
"entry at path is not a directory".to_string(),
));
}
}
VfsResponse::Err(e) => return Err(e),
Expand All @@ -70,10 +63,7 @@ pub fn open_dir(path: &str, create: bool, timeout: Option<u64>) -> Result<Direct
let message = vfs_request(path, VfsAction::CreateDirAll)
.send_and_await_response(timeout)
.unwrap()
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: path.to_string(),
})?;
.map_err(|e| VfsError::SendError(e.kind))?;

match parse_response(message.body())? {
VfsResponse::Ok => Ok(Directory {
Expand All @@ -95,10 +85,7 @@ pub fn remove_dir(path: &str, timeout: Option<u64>) -> Result<(), VfsError> {
let message = vfs_request(path, VfsAction::RemoveDir)
.send_and_await_response(timeout)
.unwrap()
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: path.to_string(),
})?;
.map_err(|e| VfsError::SendError(e.kind))?;

match parse_response(message.body())? {
VfsResponse::Ok => Ok(()),
Expand Down
Loading

0 comments on commit 2ed6d60

Please sign in to comment.