diff --git a/Cargo.toml b/Cargo.toml index 8eeb72fca..0f5abc129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,11 @@ async-dup = "1.1.0" async-std = "1.6.2" easy-parallel = "3.1.0" +jsonrpc-core = "16.0.0" +http-types = "2.9.0" +async-h1 = "2.3.0" +async-native-tls = "0.3.3" + [[bin]] name = "zkvm" path = "src/bin/zkvm.rs" diff --git a/scripts/client.py b/scripts/client.py new file mode 100644 index 000000000..c735f24f3 --- /dev/null +++ b/scripts/client.py @@ -0,0 +1,23 @@ +import requests +import json + + +def main(): + url = "http://localhost:8000/" + + # Example echo method + payload = { + "method": "say_hello", + "params": ["echome!"], + "jsonrpc": "2.0", + "id": 0, + } + response = requests.post(url, json=payload).json() + + print(response) + assert response["result"] == "Hello World!" + assert response["jsonrpc"] + +if __name__ == "__main__": + main() + diff --git a/src/bin/dfi.rs b/src/bin/dfi.rs index 837ba1e85..58c622d6f 100644 --- a/src/bin/dfi.rs +++ b/src/bin/dfi.rs @@ -11,7 +11,91 @@ use std::sync::Arc; use sapvi::{ClientProtocol, Result, SeedProtocol, ServerProtocol}; +use std::net::TcpListener; + +use async_native_tls::{Identity, TlsAcceptor}; +use http_types::{Request, Response, StatusCode, Body, Method}; +use smol::{future, Async}; + +/// Serves a request and returns a response. +async fn serve(mut req: Request) -> http_types::Result { + println!("Serving {}", req.url()); + + let request = req.body_string().await?; + + let mut io = jsonrpc_core::IoHandler::new(); + io.add_sync_method("say_hello", |_| { + Ok(jsonrpc_core::Value::String("Hello World!".into())) + }); + + //let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; + //let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; + + //assert_eq!(io.handle_request_sync(request), Some(response.to_string())); + + let response = io.handle_request_sync(&request).ok_or(sapvi::Error::BadOperationType)?; + + let mut res = Response::new(StatusCode::Ok); + res.insert_header("Content-Type", "text/plain"); + res.set_body(response); + Ok(res) +} + +/// Listens for incoming connections and serves them. +async fn listen(executor: Arc>, listener: Async, tls: Option) -> Result<()> { + // Format the full host address. + let host = match &tls { + None => format!("http://{}", listener.get_ref().local_addr()?), + Some(_) => format!("https://{}", listener.get_ref().local_addr()?), + }; + println!("Listening on {}", host); + + loop { + // Accept the next connection. + let (stream, _) = listener.accept().await?; + + // Spawn a background task serving this connection. + let task = match &tls { + None => { + let stream = async_dup::Arc::new(stream); + executor.spawn(async move { + if let Err(err) = async_h1::accept(stream, serve).await { + println!("Connection error: {:#?}", err); + } + }) + } + Some(tls) => { + // In case of HTTPS, establish a secure TLS connection first. + match tls.accept(stream).await { + Ok(stream) => { + let stream = async_dup::Arc::new(async_dup::Mutex::new(stream)); + executor.spawn(async move { + if let Err(err) = async_h1::accept(stream, serve).await { + println!("Connection error: {:#?}", err); + } + }) + } + Err(err) => { + println!("Failed to establish secure TLS connection: {:#?}", err); + continue; + } + } + } + }; + + // Detach the task to let it run in the background. + task.detach(); + } +} + async fn start(executor: Arc>, options: ProgramOptions) -> Result<()> { + let http = listen(executor.clone(), Async::::bind(([127, 0, 0, 1], 8000))?, None); + http.await; + + return Ok(()); + + /* + let connections = Arc::new(Mutex::new(HashMap::new())); let stored_addrs = Arc::new(Mutex::new(Vec::new())); @@ -83,6 +167,7 @@ async fn start(executor: Arc>, options: ProgramOptions) -> Result<( //server_task.cancel().await; //Ok(()) + */ } struct ProgramOptions {