mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-23 16:43:24 +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::http::types::*;
|
||||
use anyhow::Result;
|
||||
use http::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
// 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": "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"}}
|
||||
|
||||
//
|
||||
// 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(
|
||||
our_name: String,
|
||||
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::types::*;
|
||||
use anyhow::Result;
|
||||
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
|
||||
use route_recognizer::Router;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
@ -17,16 +16,26 @@ use warp::http::{header::HeaderValue, StatusCode};
|
||||
use warp::ws::{WebSocket, Ws};
|
||||
use warp::{Filter, Reply};
|
||||
|
||||
mod server_fns;
|
||||
|
||||
// types and constants
|
||||
type HttpSender = tokio::sync::oneshot::Sender<HttpResponse>;
|
||||
type HttpResponseSenders = Arc<Mutex<HashMap<u64, (String, HttpSender)>>>;
|
||||
type PathBindings = Arc<RwLock<Router<BoundPath>>>;
|
||||
|
||||
// node -> ID -> random ID
|
||||
|
||||
/// http driver
|
||||
/// 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:
|
||||
///
|
||||
/// 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(
|
||||
our_name: String,
|
||||
our_port: u16,
|
||||
@ -37,7 +46,6 @@ pub async fn http_server(
|
||||
) -> Result<()> {
|
||||
let http_response_senders = 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
|
||||
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);
|
||||
|
||||
// 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 _ = tokio::join!(
|
||||
@ -90,7 +89,6 @@ pub async fn http_server(
|
||||
http_response_senders.clone(),
|
||||
path_bindings.clone(),
|
||||
websockets.clone(),
|
||||
ws_proxies.clone(),
|
||||
jwt_secret_bytes.clone(),
|
||||
send_to_loop.clone(),
|
||||
print_tx.clone(),
|
||||
@ -848,13 +846,3 @@ async fn handler(
|
||||
}
|
||||
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 futures::stream::SplitSink;
|
||||
use hmac::{Hmac, Mac};
|
||||
@ -230,6 +231,16 @@ pub fn deserialize_headers(hashmap: HashMap<String, String>) -> HeaderMap {
|
||||
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 {
|
||||
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 _ = responder.send(true);
|
||||
},
|
||||
t::CapMessage::Drop { on, cap, responder } => {
|
||||
t::CapMessage::_Drop { on, cap, responder } => {
|
||||
// remove cap from process map
|
||||
let Some(entry) = process_map.get_mut(&on) else {
|
||||
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 filesystem;
|
||||
mod http_client;
|
||||
mod http_server;
|
||||
mod http;
|
||||
mod kernel;
|
||||
mod keygen;
|
||||
mod net;
|
||||
@ -193,7 +192,7 @@ async fn main() {
|
||||
// username, networking key, and routing info.
|
||||
// if any do not match, we should prompt user to create a "transaction"
|
||||
// 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);
|
||||
let (kill_tx, kill_rx) = oneshot::channel::<bool>();
|
||||
|
||||
@ -317,7 +316,7 @@ async fn main() {
|
||||
fs_kill_recv,
|
||||
fs_kill_confirm_send,
|
||||
));
|
||||
tasks.spawn(http_server::http_server(
|
||||
tasks.spawn(http::server::http_server(
|
||||
our.name.clone(),
|
||||
http_server_port,
|
||||
decoded_keyfile.jwt_secret_bytes.clone(),
|
||||
@ -325,7 +324,7 @@ async fn main() {
|
||||
kernel_message_sender.clone(),
|
||||
print_sender.clone(),
|
||||
));
|
||||
tasks.spawn(http_client::http_client(
|
||||
tasks.spawn(http::client::http_client(
|
||||
our.name.clone(),
|
||||
kernel_message_sender.clone(),
|
||||
http_client_receiver,
|
||||
|
@ -17,7 +17,6 @@ use warp::{
|
||||
Filter, Rejection, Reply,
|
||||
};
|
||||
|
||||
use crate::http_server;
|
||||
use crate::keygen;
|
||||
use crate::types::*;
|
||||
|
||||
@ -29,7 +28,7 @@ pub fn generate_jwt(jwt_secret_bytes: &[u8], username: String) -> Option<String>
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let claims = JwtClaims {
|
||||
let claims = crate::http::types::JwtClaims {
|
||||
username: username.clone(),
|
||||
expiration: 0,
|
||||
};
|
||||
@ -304,7 +303,7 @@ async fn handle_info(
|
||||
};
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// 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
|
||||
// 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 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>>,
|
||||
}
|
||||
|
||||
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)]
|
||||
pub struct WrappedSendError {
|
||||
pub id: u64,
|
||||
@ -509,17 +527,6 @@ pub struct Printout {
|
||||
// -> kernel sets `Some(A) = Rsvp` for B's request to C
|
||||
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)]
|
||||
pub enum DebugCommand {
|
||||
Toggle,
|
||||
@ -545,7 +552,6 @@ pub enum KernelCommand {
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum CapMessage {
|
||||
Add {
|
||||
@ -553,7 +559,7 @@ pub enum CapMessage {
|
||||
cap: Capability,
|
||||
responder: tokio::sync::oneshot::Sender<bool>,
|
||||
},
|
||||
Drop {
|
||||
_Drop {
|
||||
// not used yet!
|
||||
on: ProcessId,
|
||||
cap: Capability,
|
||||
@ -598,14 +604,6 @@ pub struct ProcessContext {
|
||||
pub context: Option<Context>,
|
||||
}
|
||||
|
||||
//
|
||||
// runtime-module-specific types
|
||||
//
|
||||
|
||||
//
|
||||
// filesystem.rs types
|
||||
//
|
||||
|
||||
pub type PackageVersion = (u32, u32, u32);
|
||||
|
||||
/// 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