mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-24 00:53:37 +03:00
move everything into http module
This commit is contained in:
parent
18df6332b4
commit
b39531166e
@ -1,51 +1,15 @@
|
|||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
use crate::http::types::*;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use http::header::{HeaderMap, HeaderName, HeaderValue};
|
use http::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
// Test http_client with these commands in the terminal
|
// Test http_client with these commands in the terminal
|
||||||
// !message our http_client {"method": "GET", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {}}
|
// !message our http_client {"method": "GET", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {}}
|
||||||
// !message our http_client {"method": "POST", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {"Content-Type": "application/json"}}
|
// !message our http_client {"method": "POST", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {"Content-Type": "application/json"}}
|
||||||
// !message our http_client {"method": "PUT", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {"Content-Type": "application/json"}}
|
// !message our http_client {"method": "PUT", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {"Content-Type": "application/json"}}
|
||||||
|
|
||||||
//
|
|
||||||
// http_client.rs types
|
|
||||||
//
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct HttpRequest {
|
|
||||||
pub method: String, // must parse to http::Method
|
|
||||||
pub version: Option<String>, // must parse to http::Version
|
|
||||||
pub url: String, // must parse to url::Url
|
|
||||||
pub headers: HashMap<String, String>,
|
|
||||||
// BODY is stored in the payload, as bytes
|
|
||||||
// TIMEOUT is stored in the message expect_response
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct HttpResponse {
|
|
||||||
pub status: u16,
|
|
||||||
pub headers: HashMap<String, String>,
|
|
||||||
// BODY is stored in the payload, as bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum HttpClientError {
|
|
||||||
#[error("http_client: request could not be parsed to HttpRequest: {}.", req)]
|
|
||||||
BadRequest { req: String },
|
|
||||||
#[error("http_client: http method not supported: {}", method)]
|
|
||||||
BadMethod { method: String },
|
|
||||||
#[error("http_client: url could not be parsed: {}", url)]
|
|
||||||
BadUrl { url: String },
|
|
||||||
#[error("http_client: http version not supported: {}", version)]
|
|
||||||
BadVersion { version: String },
|
|
||||||
#[error("http_client: failed to execute request {}", error)]
|
|
||||||
RequestFailed { error: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn http_client(
|
pub async fn http_client(
|
||||||
our_name: String,
|
our_name: String,
|
||||||
send_to_loop: MessageSender,
|
send_to_loop: MessageSender,
|
4
src/http/mod.rs
Normal file
4
src/http/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod client;
|
||||||
|
pub mod server;
|
||||||
|
pub mod types;
|
||||||
|
pub mod utils;
|
@ -1,11 +1,10 @@
|
|||||||
use crate::http_server::server_fns::*;
|
use crate::http::utils::*;
|
||||||
|
use crate::http::types::*;
|
||||||
use crate::register;
|
use crate::register;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
|
||||||
use route_recognizer::Router;
|
use route_recognizer::Router;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@ -17,16 +16,26 @@ use warp::http::{header::HeaderValue, StatusCode};
|
|||||||
use warp::ws::{WebSocket, Ws};
|
use warp::ws::{WebSocket, Ws};
|
||||||
use warp::{Filter, Reply};
|
use warp::{Filter, Reply};
|
||||||
|
|
||||||
mod server_fns;
|
|
||||||
|
|
||||||
// types and constants
|
// types and constants
|
||||||
type HttpSender = tokio::sync::oneshot::Sender<HttpResponse>;
|
type HttpSender = tokio::sync::oneshot::Sender<HttpResponse>;
|
||||||
type HttpResponseSenders = Arc<Mutex<HashMap<u64, (String, HttpSender)>>>;
|
type HttpResponseSenders = Arc<Mutex<HashMap<u64, (String, HttpSender)>>>;
|
||||||
type PathBindings = Arc<RwLock<Router<BoundPath>>>;
|
type PathBindings = Arc<RwLock<Router<BoundPath>>>;
|
||||||
|
|
||||||
// node -> ID -> random ID
|
/// HTTP server: a runtime module that handles HTTP requests at a given port.
|
||||||
|
/// The server accepts bindings-requests from apps. These can be used in two ways:
|
||||||
/// http driver
|
///
|
||||||
|
/// 1. The app can bind to a path and receive all subsequent requests in the form
|
||||||
|
/// of an [`HttpRequest`] to that path.
|
||||||
|
/// They will be responsible for generating HTTP responses in the form of an
|
||||||
|
/// [`HttpResponse`] to those requests.
|
||||||
|
///
|
||||||
|
/// 2. The app can bind static content to a path. The server will handle all subsequent
|
||||||
|
/// requests, serving that static content. It will only respond to `GET` requests.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// In addition to binding on paths, the HTTP server can receive incoming WebSocket connections
|
||||||
|
/// and pass them to a targeted app. The server will handle encrypting and decrypting messages
|
||||||
|
/// over these connections.
|
||||||
pub async fn http_server(
|
pub async fn http_server(
|
||||||
our_name: String,
|
our_name: String,
|
||||||
our_port: u16,
|
our_port: u16,
|
||||||
@ -37,7 +46,6 @@ pub async fn http_server(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let http_response_senders = Arc::new(Mutex::new(HashMap::new()));
|
let http_response_senders = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let websockets: WebSockets = Arc::new(Mutex::new(HashMap::new()));
|
let websockets: WebSockets = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let ws_proxies: WebSocketProxies = Arc::new(Mutex::new(HashMap::new())); // channel_id -> node
|
|
||||||
|
|
||||||
// Add RPC path
|
// Add RPC path
|
||||||
let mut bindings_map: Router<BoundPath> = Router::new();
|
let mut bindings_map: Router<BoundPath> = Router::new();
|
||||||
@ -49,15 +57,6 @@ pub async fn http_server(
|
|||||||
};
|
};
|
||||||
bindings_map.add("/rpc:sys:uqbar/message", rpc_bound_path);
|
bindings_map.add("/rpc:sys:uqbar/message", rpc_bound_path);
|
||||||
|
|
||||||
// Add encryptor binding
|
|
||||||
let encryptor_bound_path = BoundPath {
|
|
||||||
app: ProcessId::from_str("encryptor:sys:uqbar").unwrap(),
|
|
||||||
authenticated: false,
|
|
||||||
local_only: true,
|
|
||||||
original_path: "/encryptor:sys:uqbar".to_string(),
|
|
||||||
};
|
|
||||||
bindings_map.add("/encryptor:sys:uqbar", encryptor_bound_path);
|
|
||||||
|
|
||||||
let path_bindings: PathBindings = Arc::new(RwLock::new(bindings_map));
|
let path_bindings: PathBindings = Arc::new(RwLock::new(bindings_map));
|
||||||
|
|
||||||
let _ = tokio::join!(
|
let _ = tokio::join!(
|
||||||
@ -90,7 +89,6 @@ pub async fn http_server(
|
|||||||
http_response_senders.clone(),
|
http_response_senders.clone(),
|
||||||
path_bindings.clone(),
|
path_bindings.clone(),
|
||||||
websockets.clone(),
|
websockets.clone(),
|
||||||
ws_proxies.clone(),
|
|
||||||
jwt_secret_bytes.clone(),
|
jwt_secret_bytes.clone(),
|
||||||
send_to_loop.clone(),
|
send_to_loop.clone(),
|
||||||
print_tx.clone(),
|
print_tx.clone(),
|
||||||
@ -848,13 +846,3 @@ async fn handler(
|
|||||||
}
|
}
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_open_port(start_at: u16) -> Option<u16> {
|
|
||||||
for port in start_at..=u16::MAX {
|
|
||||||
let bind_addr = format!("0.0.0.0:{}", port);
|
|
||||||
if is_port_available(&bind_addr).await {
|
|
||||||
return Some(port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
121
src/http/types.rs
Normal file
121
src/http/types.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use crate::types::Address;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// HTTP Request type that can be shared over WASM boundary to apps.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct HttpRequest {
|
||||||
|
pub method: String, // must parse to http::Method
|
||||||
|
pub version: Option<String>, // must parse to http::Version
|
||||||
|
pub url: String, // must parse to url::Url
|
||||||
|
pub headers: HashMap<String, String>,
|
||||||
|
// BODY is stored in the payload, as bytes
|
||||||
|
// TIMEOUT is stored in the message expect_response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HTTP Response type that can be shared over WASM boundary to apps.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct HttpResponse {
|
||||||
|
pub status: u16,
|
||||||
|
pub headers: HashMap<String, String>,
|
||||||
|
// BODY is stored in the payload, as bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum HttpClientError {
|
||||||
|
#[error("http_client: request could not be parsed to HttpRequest: {}.", req)]
|
||||||
|
BadRequest { req: String },
|
||||||
|
#[error("http_client: http method not supported: {}", method)]
|
||||||
|
BadMethod { method: String },
|
||||||
|
#[error("http_client: url could not be parsed: {}", url)]
|
||||||
|
BadUrl { url: String },
|
||||||
|
#[error("http_client: http version not supported: {}", version)]
|
||||||
|
BadVersion { version: String },
|
||||||
|
#[error("http_client: failed to execute request {}", error)]
|
||||||
|
RequestFailed { error: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum HttpServerError {
|
||||||
|
#[error("http_server: json is None")]
|
||||||
|
NoJson,
|
||||||
|
#[error("http_server: response not ok")]
|
||||||
|
ResponseError,
|
||||||
|
#[error("http_server: bytes are None")]
|
||||||
|
NoBytes,
|
||||||
|
#[error(
|
||||||
|
"http_server: JSON payload could not be parsed to HttpClientRequest: {error}. Got {:?}.",
|
||||||
|
json
|
||||||
|
)]
|
||||||
|
BadJson { json: String, error: String },
|
||||||
|
#[error("http_server: path binding error: {:?}", error)]
|
||||||
|
PathBind { error: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct JwtClaims {
|
||||||
|
pub username: String,
|
||||||
|
pub expiration: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WebSocketServerTarget {
|
||||||
|
pub node: String,
|
||||||
|
pub id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WebSocketPush {
|
||||||
|
pub target: WebSocketServerTarget,
|
||||||
|
pub is_text: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ServerAction {
|
||||||
|
pub action: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum HttpServerAction {
|
||||||
|
BindPath {
|
||||||
|
path: String,
|
||||||
|
authenticated: bool,
|
||||||
|
local_only: bool,
|
||||||
|
},
|
||||||
|
WebSocketPush(WebSocketPush),
|
||||||
|
ServerAction(ServerAction),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WsRegister {
|
||||||
|
pub ws_auth_token: String,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub channel_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WsMessage {
|
||||||
|
pub ws_auth_token: String,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub channel_id: String,
|
||||||
|
pub target: Address,
|
||||||
|
pub json: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct EncryptedWsMessage {
|
||||||
|
pub ws_auth_token: String,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub channel_id: String,
|
||||||
|
pub target: Address,
|
||||||
|
pub encrypted: String, // Encrypted JSON as hex with the 32-byte authentication tag appended
|
||||||
|
pub nonce: String, // Hex of the 12-byte nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum WebSocketClientMessage {
|
||||||
|
WsRegister(WsRegister),
|
||||||
|
WsMessage(WsMessage),
|
||||||
|
EncryptedWsMessage(EncryptedWsMessage),
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::http::types::*;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
use futures::stream::SplitSink;
|
use futures::stream::SplitSink;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
@ -230,6 +231,16 @@ pub fn deserialize_headers(hashmap: HashMap<String, String>) -> HeaderMap {
|
|||||||
header_map
|
header_map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_open_port(start_at: u16) -> Option<u16> {
|
||||||
|
for port in start_at..=u16::MAX {
|
||||||
|
let bind_addr = format!("0.0.0.0:{}", port);
|
||||||
|
if is_port_available(&bind_addr).await {
|
||||||
|
return Some(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn is_port_available(bind_addr: &str) -> bool {
|
pub async fn is_port_available(bind_addr: &str) -> bool {
|
||||||
TcpListener::bind(bind_addr).await.is_ok()
|
TcpListener::bind(bind_addr).await.is_ok()
|
||||||
}
|
}
|
@ -2169,7 +2169,7 @@ async fn make_event_loop(
|
|||||||
let _ = persist_state(&our_name, &send_to_loop, &process_map).await;
|
let _ = persist_state(&our_name, &send_to_loop, &process_map).await;
|
||||||
let _ = responder.send(true);
|
let _ = responder.send(true);
|
||||||
},
|
},
|
||||||
t::CapMessage::Drop { on, cap, responder } => {
|
t::CapMessage::_Drop { on, cap, responder } => {
|
||||||
// remove cap from process map
|
// remove cap from process map
|
||||||
let Some(entry) = process_map.get_mut(&on) else {
|
let Some(entry) = process_map.get_mut(&on) else {
|
||||||
let _ = responder.send(false);
|
let _ = responder.send(false);
|
||||||
|
@ -1,422 +0,0 @@
|
|||||||
use super::bindings::component::uq_process::types as wit;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
//
|
|
||||||
// process-facing kernel types, used for process
|
|
||||||
// management and message-passing
|
|
||||||
// matches types in uqbar.wit
|
|
||||||
//
|
|
||||||
|
|
||||||
pub type Context = Vec<u8>;
|
|
||||||
pub type NodeId = String; // QNS domain name
|
|
||||||
|
|
||||||
/// process ID is a formatted unique identifier that contains
|
|
||||||
/// the publishing node's ID, the package name, and finally the process name.
|
|
||||||
/// the process name can be a random number, or a name chosen by the user.
|
|
||||||
/// the formatting is as follows:
|
|
||||||
/// `[process name]:[package name]:[node ID]`
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct ProcessId {
|
|
||||||
process_name: String,
|
|
||||||
package_name: String,
|
|
||||||
publisher_node: NodeId,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl ProcessId {
|
|
||||||
/// generates a random u64 number if process_name is not declared
|
|
||||||
pub fn new(process_name: &str, package_name: &str, publisher_node: &str) -> Self {
|
|
||||||
ProcessId {
|
|
||||||
process_name: process_name.into(),
|
|
||||||
package_name: package_name.into(),
|
|
||||||
publisher_node: publisher_node.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_str(input: &str) -> Result<Self, ProcessIdParseError> {
|
|
||||||
// split string on colons into 3 segments
|
|
||||||
let mut segments = input.split(':');
|
|
||||||
let process_name = segments
|
|
||||||
.next()
|
|
||||||
.ok_or(ProcessIdParseError::MissingField)?
|
|
||||||
.to_string();
|
|
||||||
let package_name = segments
|
|
||||||
.next()
|
|
||||||
.ok_or(ProcessIdParseError::MissingField)?
|
|
||||||
.to_string();
|
|
||||||
let publisher_node = segments
|
|
||||||
.next()
|
|
||||||
.ok_or(ProcessIdParseError::MissingField)?
|
|
||||||
.to_string();
|
|
||||||
if segments.next().is_some() {
|
|
||||||
return Err(ProcessIdParseError::TooManyColons);
|
|
||||||
}
|
|
||||||
Ok(ProcessId {
|
|
||||||
process_name,
|
|
||||||
package_name,
|
|
||||||
publisher_node,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
[
|
|
||||||
self.process_name.as_str(),
|
|
||||||
self.package_name.as_str(),
|
|
||||||
self.publisher_node.as_str(),
|
|
||||||
]
|
|
||||||
.join(":")
|
|
||||||
}
|
|
||||||
pub fn process(&self) -> &str {
|
|
||||||
&self.process_name
|
|
||||||
}
|
|
||||||
pub fn package(&self) -> &str {
|
|
||||||
&self.package_name
|
|
||||||
}
|
|
||||||
pub fn publisher_node(&self) -> &str {
|
|
||||||
&self.publisher_node
|
|
||||||
}
|
|
||||||
pub fn en_wit(&self) -> wit::ProcessId {
|
|
||||||
wit::ProcessId {
|
|
||||||
process_name: self.process_name.clone(),
|
|
||||||
package_name: self.package_name.clone(),
|
|
||||||
publisher_node: self.publisher_node.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn de_wit(wit: wit::ProcessId) -> ProcessId {
|
|
||||||
ProcessId {
|
|
||||||
process_name: wit.process_name,
|
|
||||||
package_name: wit.package_name,
|
|
||||||
publisher_node: wit.publisher_node,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ProcessIdParseError {
|
|
||||||
TooManyColons,
|
|
||||||
MissingField,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Address {
|
|
||||||
pub node: NodeId,
|
|
||||||
pub process: ProcessId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Address {
|
|
||||||
pub fn en_wit(&self) -> wit::Address {
|
|
||||||
wit::Address {
|
|
||||||
node: self.node.clone(),
|
|
||||||
process: self.process.en_wit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn de_wit(wit: wit::Address) -> Address {
|
|
||||||
Address {
|
|
||||||
node: wit.node,
|
|
||||||
process: ProcessId {
|
|
||||||
process_name: wit.process.process_name,
|
|
||||||
package_name: wit.process.package_name,
|
|
||||||
publisher_node: wit.process.publisher_node,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Payload {
|
|
||||||
pub mime: Option<String>, // MIME type
|
|
||||||
pub bytes: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Request {
|
|
||||||
pub inherit: bool,
|
|
||||||
pub expects_response: Option<u64>, // number of seconds until timeout
|
|
||||||
pub ipc: Vec<u8>,
|
|
||||||
pub metadata: Option<String>, // JSON-string
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Response {
|
|
||||||
pub inherit: bool,
|
|
||||||
pub ipc: Vec<u8>,
|
|
||||||
pub metadata: Option<String>, // JSON-string
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Message {
|
|
||||||
Request(Request),
|
|
||||||
Response((Response, Option<Context>)),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Capability {
|
|
||||||
pub issuer: Address,
|
|
||||||
pub params: String, // JSON-string
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct SignedCapability {
|
|
||||||
pub issuer: Address,
|
|
||||||
pub params: String, // JSON-string
|
|
||||||
pub signature: Vec<u8>, // signed by the kernel, so we can verify that the kernel issued it
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct SendError {
|
|
||||||
pub kind: SendErrorKind,
|
|
||||||
pub target: Address,
|
|
||||||
pub message: Message,
|
|
||||||
pub payload: Option<Payload>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum SendErrorKind {
|
|
||||||
Offline,
|
|
||||||
Timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum OnPanic {
|
|
||||||
None,
|
|
||||||
Restart,
|
|
||||||
Requests(Vec<(Address, Request, Option<Payload>)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OnPanic {
|
|
||||||
pub fn is_restart(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
OnPanic::None => false,
|
|
||||||
OnPanic::Restart => true,
|
|
||||||
OnPanic::Requests(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum KernelCommand {
|
|
||||||
StartProcess {
|
|
||||||
id: ProcessId,
|
|
||||||
wasm_bytes_handle: u128,
|
|
||||||
on_panic: OnPanic,
|
|
||||||
initial_capabilities: HashSet<SignedCapability>,
|
|
||||||
public: bool,
|
|
||||||
},
|
|
||||||
KillProcess(ProcessId), // this is extrajudicial killing: we might lose messages!
|
|
||||||
// kernel only
|
|
||||||
RebootProcess {
|
|
||||||
process_id: ProcessId,
|
|
||||||
persisted: PersistedProcess,
|
|
||||||
},
|
|
||||||
Shutdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum KernelResponse {
|
|
||||||
StartedProcess,
|
|
||||||
StartProcessError,
|
|
||||||
KilledProcess(ProcessId),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct PersistedProcess {
|
|
||||||
pub wasm_bytes_handle: u128,
|
|
||||||
// pub drive: String,
|
|
||||||
// pub full_path: String,
|
|
||||||
pub on_panic: OnPanic,
|
|
||||||
pub capabilities: HashSet<Capability>,
|
|
||||||
pub public: bool, // marks if a process allows messages from any process
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct VfsRequest {
|
|
||||||
pub drive: String,
|
|
||||||
pub action: VfsAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum VfsAction {
|
|
||||||
New,
|
|
||||||
Add {
|
|
||||||
full_path: String,
|
|
||||||
entry_type: AddEntryType,
|
|
||||||
},
|
|
||||||
Rename {
|
|
||||||
full_path: String,
|
|
||||||
new_full_path: String,
|
|
||||||
},
|
|
||||||
Delete(String),
|
|
||||||
WriteOffset {
|
|
||||||
full_path: String,
|
|
||||||
offset: u64,
|
|
||||||
},
|
|
||||||
SetSize {
|
|
||||||
full_path: String,
|
|
||||||
size: u64,
|
|
||||||
},
|
|
||||||
GetPath(u128),
|
|
||||||
GetHash(String),
|
|
||||||
GetEntry(String),
|
|
||||||
GetFileChunk {
|
|
||||||
full_path: String,
|
|
||||||
offset: u64,
|
|
||||||
length: u64,
|
|
||||||
},
|
|
||||||
GetEntryLength(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum AddEntryType {
|
|
||||||
Dir,
|
|
||||||
NewFile, // add a new file to fs and add name in vfs
|
|
||||||
ExistingFile { hash: u128 }, // link an existing file in fs to a new name in vfs
|
|
||||||
ZipArchive,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum GetEntryType {
|
|
||||||
Dir,
|
|
||||||
File,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum VfsResponse {
|
|
||||||
Ok,
|
|
||||||
Err(VfsError),
|
|
||||||
GetPath(Option<String>),
|
|
||||||
GetHash(Option<u128>),
|
|
||||||
GetEntry {
|
|
||||||
// file bytes in payload, if entry was a file
|
|
||||||
is_file: bool,
|
|
||||||
children: Vec<String>,
|
|
||||||
},
|
|
||||||
GetFileChunk, // chunk in payload
|
|
||||||
GetEntryLength(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum VfsError {
|
|
||||||
BadDriveName,
|
|
||||||
BadDescriptor,
|
|
||||||
NoCap,
|
|
||||||
EntryNotFound,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl VfsError {
|
|
||||||
pub fn kind(&self) -> &str {
|
|
||||||
match *self {
|
|
||||||
VfsError::BadDriveName => "BadDriveName",
|
|
||||||
VfsError::BadDescriptor => "BadDescriptor",
|
|
||||||
VfsError::NoCap => "NoCap",
|
|
||||||
VfsError::EntryNotFound => "EntryNotFound",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package types
|
|
||||||
//
|
|
||||||
|
|
||||||
pub type PackageVersion = (u32, u32, u32);
|
|
||||||
|
|
||||||
/// the type that gets deserialized from `metadata.json` in a package
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct PackageMetadata {
|
|
||||||
pub package: String,
|
|
||||||
pub publisher: String,
|
|
||||||
pub version: PackageVersion,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub website: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the type that gets deserialized from each entry in the array in `manifest.json`
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct PackageManifestEntry {
|
|
||||||
pub process_name: String,
|
|
||||||
pub process_wasm_path: String,
|
|
||||||
pub on_panic: OnPanic,
|
|
||||||
pub request_networking: bool,
|
|
||||||
pub request_messaging: Vec<String>,
|
|
||||||
pub public: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// conversions between wit types and kernel types (annoying!)
|
|
||||||
//
|
|
||||||
|
|
||||||
pub fn de_wit_request(wit: wit::Request) -> Request {
|
|
||||||
Request {
|
|
||||||
inherit: wit.inherit,
|
|
||||||
expects_response: wit.expects_response,
|
|
||||||
ipc: wit.ipc,
|
|
||||||
metadata: wit.metadata,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn en_wit_request(request: Request) -> wit::Request {
|
|
||||||
wit::Request {
|
|
||||||
inherit: request.inherit,
|
|
||||||
expects_response: request.expects_response,
|
|
||||||
ipc: request.ipc,
|
|
||||||
metadata: request.metadata,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn de_wit_response(wit: wit::Response) -> Response {
|
|
||||||
Response {
|
|
||||||
inherit: wit.inherit,
|
|
||||||
ipc: wit.ipc,
|
|
||||||
metadata: wit.metadata,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn en_wit_response(response: Response) -> wit::Response {
|
|
||||||
wit::Response {
|
|
||||||
inherit: response.inherit,
|
|
||||||
ipc: response.ipc,
|
|
||||||
metadata: response.metadata,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn de_wit_payload(wit: Option<wit::Payload>) -> Option<Payload> {
|
|
||||||
match wit {
|
|
||||||
None => None,
|
|
||||||
Some(wit) => Some(Payload {
|
|
||||||
mime: wit.mime,
|
|
||||||
bytes: wit.bytes,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn en_wit_payload(load: Option<Payload>) -> Option<wit::Payload> {
|
|
||||||
match load {
|
|
||||||
None => None,
|
|
||||||
Some(load) => Some(wit::Payload {
|
|
||||||
mime: load.mime,
|
|
||||||
bytes: load.bytes,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> SignedCapability {
|
|
||||||
SignedCapability {
|
|
||||||
issuer: Address {
|
|
||||||
node: wit.issuer.node,
|
|
||||||
process: ProcessId {
|
|
||||||
process_name: wit.issuer.process.process_name,
|
|
||||||
package_name: wit.issuer.process.package_name,
|
|
||||||
publisher_node: wit.issuer.process.publisher_node,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
params: wit.params,
|
|
||||||
signature: wit.signature,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn en_wit_signed_capability(cap: SignedCapability) -> wit::SignedCapability {
|
|
||||||
wit::SignedCapability {
|
|
||||||
issuer: cap.issuer.en_wit(),
|
|
||||||
params: cap.params,
|
|
||||||
signature: cap.signature,
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,8 +10,7 @@ use tokio::{fs, time::timeout};
|
|||||||
|
|
||||||
mod eth_rpc;
|
mod eth_rpc;
|
||||||
mod filesystem;
|
mod filesystem;
|
||||||
mod http_client;
|
mod http;
|
||||||
mod http_server;
|
|
||||||
mod kernel;
|
mod kernel;
|
||||||
mod keygen;
|
mod keygen;
|
||||||
mod net;
|
mod net;
|
||||||
@ -193,7 +192,7 @@ async fn main() {
|
|||||||
// username, networking key, and routing info.
|
// username, networking key, and routing info.
|
||||||
// if any do not match, we should prompt user to create a "transaction"
|
// if any do not match, we should prompt user to create a "transaction"
|
||||||
// that updates their PKI info on-chain.
|
// that updates their PKI info on-chain.
|
||||||
let http_server_port = http_server::find_open_port(8080).await.unwrap();
|
let http_server_port = http::utils::find_open_port(8080).await.unwrap();
|
||||||
println!("login or register at http://localhost:{}", http_server_port);
|
println!("login or register at http://localhost:{}", http_server_port);
|
||||||
let (kill_tx, kill_rx) = oneshot::channel::<bool>();
|
let (kill_tx, kill_rx) = oneshot::channel::<bool>();
|
||||||
|
|
||||||
@ -317,7 +316,7 @@ async fn main() {
|
|||||||
fs_kill_recv,
|
fs_kill_recv,
|
||||||
fs_kill_confirm_send,
|
fs_kill_confirm_send,
|
||||||
));
|
));
|
||||||
tasks.spawn(http_server::http_server(
|
tasks.spawn(http::server::http_server(
|
||||||
our.name.clone(),
|
our.name.clone(),
|
||||||
http_server_port,
|
http_server_port,
|
||||||
decoded_keyfile.jwt_secret_bytes.clone(),
|
decoded_keyfile.jwt_secret_bytes.clone(),
|
||||||
@ -325,7 +324,7 @@ async fn main() {
|
|||||||
kernel_message_sender.clone(),
|
kernel_message_sender.clone(),
|
||||||
print_sender.clone(),
|
print_sender.clone(),
|
||||||
));
|
));
|
||||||
tasks.spawn(http_client::http_client(
|
tasks.spawn(http::client::http_client(
|
||||||
our.name.clone(),
|
our.name.clone(),
|
||||||
kernel_message_sender.clone(),
|
kernel_message_sender.clone(),
|
||||||
http_client_receiver,
|
http_client_receiver,
|
||||||
|
@ -17,7 +17,6 @@ use warp::{
|
|||||||
Filter, Rejection, Reply,
|
Filter, Rejection, Reply,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::http_server;
|
|
||||||
use crate::keygen;
|
use crate::keygen;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ pub fn generate_jwt(jwt_secret_bytes: &[u8], username: String) -> Option<String>
|
|||||||
Err(_) => return None,
|
Err(_) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let claims = JwtClaims {
|
let claims = crate::http::types::JwtClaims {
|
||||||
username: username.clone(),
|
username: username.clone(),
|
||||||
expiration: 0,
|
expiration: 0,
|
||||||
};
|
};
|
||||||
@ -304,7 +303,7 @@ async fn handle_info(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: if IP is localhost, don't allow registration as direct
|
// TODO: if IP is localhost, don't allow registration as direct
|
||||||
let ws_port = http_server::find_open_port(9000).await.unwrap();
|
let ws_port = crate::http::utils::find_open_port(9000).await.unwrap();
|
||||||
|
|
||||||
let our = Identity {
|
let our = Identity {
|
||||||
networking_key: format!("0x{}", public_key),
|
networking_key: format!("0x{}", public_key),
|
||||||
|
176
src/types.rs
176
src/types.rs
@ -18,10 +18,10 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// types shared between kernel and processes. frustratingly, this is an exact copy
|
// types shared between kernel and processes. frustratingly, this is an exact copy
|
||||||
// of the types in process_lib/src/kernel_types.rs
|
// of the types in process_lib
|
||||||
// this is because even though the types are identical, they will not match when
|
// this is because even though the types are identical, they will not match when
|
||||||
// used in the kernel context which generates bindings differently than the process
|
// used in the kernel context which generates bindings differently than the process
|
||||||
// standard library. make sure to keep this synced with kernel_types.rs
|
// standard library. make sure to keep this synced with process_lib.
|
||||||
//
|
//
|
||||||
pub type Context = Vec<u8>;
|
pub type Context = Vec<u8>;
|
||||||
pub type NodeId = String; // QNS domain name
|
pub type NodeId = String; // QNS domain name
|
||||||
@ -382,7 +382,7 @@ pub fn de_wit_on_panic(wit: wit::OnPanic) -> OnPanic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// END SYNC WITH kernel_types.rs
|
// END SYNC WITH process_lib
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -488,6 +488,24 @@ pub struct KernelMessage {
|
|||||||
pub signed_capabilities: Option<Vec<SignedCapability>>,
|
pub signed_capabilities: Option<Vec<SignedCapability>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for KernelMessage {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{{\n id: {},\n source: {},\n target: {},\n rsvp: {},\n message: {},\n payload: {}\n}}",
|
||||||
|
self.id,
|
||||||
|
self.source,
|
||||||
|
self.target,
|
||||||
|
match &self.rsvp {
|
||||||
|
Some(rsvp) => rsvp.to_string(),
|
||||||
|
None => "None".to_string()
|
||||||
|
},
|
||||||
|
self.message,
|
||||||
|
self.payload.is_some(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct WrappedSendError {
|
pub struct WrappedSendError {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
@ -509,17 +527,6 @@ pub struct Printout {
|
|||||||
// -> kernel sets `Some(A) = Rsvp` for B's request to C
|
// -> kernel sets `Some(A) = Rsvp` for B's request to C
|
||||||
pub type Rsvp = Option<Address>;
|
pub type Rsvp = Option<Address>;
|
||||||
|
|
||||||
//
|
|
||||||
// boot/startup specific types???
|
|
||||||
//
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct BootOutboundRequest {
|
|
||||||
pub target_process: ProcessId,
|
|
||||||
pub json: Option<String>,
|
|
||||||
pub bytes: Option<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum DebugCommand {
|
pub enum DebugCommand {
|
||||||
Toggle,
|
Toggle,
|
||||||
@ -545,7 +552,6 @@ pub enum KernelCommand {
|
|||||||
Shutdown,
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CapMessage {
|
pub enum CapMessage {
|
||||||
Add {
|
Add {
|
||||||
@ -553,7 +559,7 @@ pub enum CapMessage {
|
|||||||
cap: Capability,
|
cap: Capability,
|
||||||
responder: tokio::sync::oneshot::Sender<bool>,
|
responder: tokio::sync::oneshot::Sender<bool>,
|
||||||
},
|
},
|
||||||
Drop {
|
_Drop {
|
||||||
// not used yet!
|
// not used yet!
|
||||||
on: ProcessId,
|
on: ProcessId,
|
||||||
cap: Capability,
|
cap: Capability,
|
||||||
@ -598,14 +604,6 @@ pub struct ProcessContext {
|
|||||||
pub context: Option<Context>,
|
pub context: Option<Context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// runtime-module-specific types
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// filesystem.rs types
|
|
||||||
//
|
|
||||||
|
|
||||||
pub type PackageVersion = (u32, u32, u32);
|
pub type PackageVersion = (u32, u32, u32);
|
||||||
|
|
||||||
/// the type that gets deserialized from `metadata.json` in a package
|
/// the type that gets deserialized from `metadata.json` in a package
|
||||||
@ -832,133 +830,3 @@ impl VfsError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// custom kernel displays
|
|
||||||
//
|
|
||||||
|
|
||||||
impl std::fmt::Display for KernelMessage {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{{\n id: {},\n source: {},\n target: {},\n rsvp: {},\n message: {},\n payload: {}\n}}",
|
|
||||||
self.id,
|
|
||||||
self.source,
|
|
||||||
self.target,
|
|
||||||
match &self.rsvp {
|
|
||||||
Some(rsvp) => rsvp.to_string(),
|
|
||||||
None => "None".to_string()
|
|
||||||
},
|
|
||||||
self.message,
|
|
||||||
self.payload.is_some(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// http_server.rs types
|
|
||||||
//
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct HttpResponse {
|
|
||||||
pub status: u16,
|
|
||||||
pub headers: HashMap<String, String>,
|
|
||||||
pub body: Option<Vec<u8>>, // TODO does this use a lot of memory?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum HttpServerError {
|
|
||||||
#[error("http_server: json is None")]
|
|
||||||
NoJson,
|
|
||||||
#[error("http_server: response not ok")]
|
|
||||||
ResponseError,
|
|
||||||
#[error("http_server: bytes are None")]
|
|
||||||
NoBytes,
|
|
||||||
#[error(
|
|
||||||
"http_server: JSON payload could not be parsed to HttpClientRequest: {error}. Got {:?}.",
|
|
||||||
json
|
|
||||||
)]
|
|
||||||
BadJson { json: String, error: String },
|
|
||||||
#[error("http_server: path binding error: {:?}", error)]
|
|
||||||
PathBind { error: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl HttpServerError {
|
|
||||||
pub fn kind(&self) -> &str {
|
|
||||||
match *self {
|
|
||||||
HttpServerError::NoJson { .. } => "NoJson",
|
|
||||||
HttpServerError::NoBytes { .. } => "NoBytes",
|
|
||||||
HttpServerError::BadJson { .. } => "BadJson",
|
|
||||||
HttpServerError::ResponseError { .. } => "ResponseError",
|
|
||||||
HttpServerError::PathBind { .. } => "PathBind",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct JwtClaims {
|
|
||||||
pub username: String,
|
|
||||||
pub expiration: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct WebSocketServerTarget {
|
|
||||||
pub node: String,
|
|
||||||
pub id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct WebSocketPush {
|
|
||||||
pub target: WebSocketServerTarget,
|
|
||||||
pub is_text: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ServerAction {
|
|
||||||
pub action: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum HttpServerMessage {
|
|
||||||
BindPath {
|
|
||||||
path: String,
|
|
||||||
authenticated: bool,
|
|
||||||
local_only: bool,
|
|
||||||
},
|
|
||||||
WebSocketPush(WebSocketPush),
|
|
||||||
ServerAction(ServerAction),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct WsRegister {
|
|
||||||
pub ws_auth_token: String,
|
|
||||||
pub auth_token: String,
|
|
||||||
pub channel_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct WsMessage {
|
|
||||||
pub ws_auth_token: String,
|
|
||||||
pub auth_token: String,
|
|
||||||
pub channel_id: String,
|
|
||||||
pub target: Address,
|
|
||||||
pub json: Option<serde_json::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct EncryptedWsMessage {
|
|
||||||
pub ws_auth_token: String,
|
|
||||||
pub auth_token: String,
|
|
||||||
pub channel_id: String,
|
|
||||||
pub target: Address,
|
|
||||||
pub encrypted: String, // Encrypted JSON as hex with the 32-byte authentication tag appended
|
|
||||||
pub nonce: String, // Hex of the 12-byte nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum WebSocketClientMessage {
|
|
||||||
WsRegister(WsRegister),
|
|
||||||
WsMessage(WsMessage),
|
|
||||||
EncryptedWsMessage(EncryptedWsMessage),
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user