From 8e3af597f6a6ae86e5b8f0f38f32ae7e19d10565 Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Tue, 26 Oct 2021 15:57:33 +0800 Subject: [PATCH 1/2] provide basic hashmap API for HTTP responding; bump 0.0.4 --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 15 +++++- calcit.cirru | 103 ++++++++++++++++++++++++++------------- compact.cirru | 18 ++++--- src/lib.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 219 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb04d5d..0fb979e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "calcit_http" -version = "0.0.3" +version = "0.0.4" dependencies = [ "cirru_edn", "cirru_parser", diff --git a/Cargo.toml b/Cargo.toml index bf9b077..a744997 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcit_http" -version = "0.0.3" +version = "0.0.4" authors = ["jiyinyiyong "] edition = "2018" diff --git a/README.md b/README.md index bb7ceae..2eafb31 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,19 @@ APIs: ```cirru -http.core/serve-http? $ {} - :port 4000 +http.core/serve-http! + {} + :port 4000 + :host "|0.0.0.0" + fn (req) + on-request req + +defn on-request (req) + {} + :code 200 + :headers $ {} + :content-type |application/json + :body "|some content" ``` Install to `~/.config/calcit/modules/`, compile and provide `*.{dylib,so}` file with `./build.sh`. diff --git a/calcit.cirru b/calcit.cirru index 04810e4..231ae0c 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -73,6 +73,68 @@ |j $ {} (:type :leaf) (:by |u0) (:at 1634703953240) (:text |calcit-filename) :configs $ {} :defs $ {} + |mid-call $ {} (:type :expr) (:by |u0) (:at 1634927786817) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1634927788771) (:text |defn) + |j $ {} (:type :leaf) (:by |u0) (:at 1635228168857) (:text |mid-call) + |r $ {} (:type :expr) (:by |u0) (:at 1634927786817) + :data $ {} + |v $ {} (:type :expr) (:by |u0) (:at 1634927789650) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1634927790523) (:text |println) + |j $ {} (:type :leaf) (:by |u0) (:at 1635228176331) (:text "|\"Calling internal function") + |on-request $ {} (:type :expr) (:by |u0) (:at 1635234356153) + :data $ {} + |sT $ {} (:type :expr) (:by |u0) (:at 1635234439960) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234441166) (:text |println) + |j $ {} (:type :expr) (:by |u0) (:at 1635234441792) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234442744) (:text |:url) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234443443) (:text |req) + |T $ {} (:type :leaf) (:by |u0) (:at 1635234356153) (:text |defn) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234356153) (:text |on-request) + |r $ {} (:type :expr) (:by |u0) (:at 1635234356153) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234356153) (:text |req) + |s $ {} (:type :expr) (:by |u0) (:at 1635234372099) + :data $ {} + |L $ {} (:type :leaf) (:by |u0) (:at 1635234436278) (:text |;) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234372099) (:text |println) + |r $ {} (:type :leaf) (:by |u0) (:at 1635234372099) (:text "|\"Handling request:") + |v $ {} (:type :leaf) (:by |u0) (:at 1635234372099) (:text |req) + |t $ {} (:type :expr) (:by |u0) (:at 1635234365546) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234365546) (:text |;) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234365546) (:text |mid-call) + |v $ {} (:type :expr) (:by |u0) (:at 1635234357410) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |{}) + |j $ {} (:type :expr) (:by |u0) (:at 1635234357410) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |:status) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |:ok) + |r $ {} (:type :expr) (:by |u0) (:at 1635234357410) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |:code) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |200) + |t $ {} (:type :expr) (:by |u0) (:at 1635234527621) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234530563) (:text |:headers) + |j $ {} (:type :expr) (:by |u0) (:at 1635234530868) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234531808) (:text |{}) + |j $ {} (:type :expr) (:by |u0) (:at 1635234532157) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234534604) (:text |:content-type) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234808004) (:text "|\"application/json") + |v $ {} (:type :expr) (:by |u0) (:at 1635234357410) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234357410) (:text |:body) + |j $ {} (:type :expr) (:by |u0) (:at 1635234494733) + :data $ {} + |D $ {} (:type :leaf) (:by |u0) (:at 1635234502294) (:text |format-cirru-edn) + |L $ {} (:type :leaf) (:by |u0) (:at 1635234496887) (:text |req) |run-tests $ {} (:type :expr) (:by |u0) (:at 1633150008092) :data $ {} |T $ {} (:type :leaf) (:by |u0) (:at 1633150011172) (:text |defn) @@ -101,16 +163,6 @@ |v $ {} (:type :expr) (:by |u0) (:at 1633150002066) :data $ {} |T $ {} (:type :leaf) (:by |u0) (:at 1633150004371) (:text |run-tests) - |mid-f $ {} (:type :expr) (:by |u0) (:at 1634927786817) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634927788771) (:text |defn) - |j $ {} (:type :leaf) (:by |u0) (:at 1634927786817) (:text |mid-f) - |r $ {} (:type :expr) (:by |u0) (:at 1634927786817) - :data $ {} - |v $ {} (:type :expr) (:by |u0) (:at 1634927789650) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634927790523) (:text |println) - |j $ {} (:type :leaf) (:by |u0) (:at 1634927804262) (:text "|\"fff222") |demo-server! $ {} (:type :expr) (:by |u0) (:at 1634925851472) :data $ {} |T $ {} (:type :leaf) (:by |u0) (:at 1634925851472) (:text |defn) @@ -133,35 +185,20 @@ |j $ {} (:type :expr) (:by |u0) (:at 1634925876370) :data $ {} |T $ {} (:type :leaf) (:by |u0) (:at 1634925879038) (:text |req) - |n $ {} (:type :expr) (:by |u0) (:at 1634926576882) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634926579047) (:text |println) - |j $ {} (:type :leaf) (:by |u0) (:at 1634927753197) (:text "|\"got request2") - |r $ {} (:type :leaf) (:by |u0) (:at 1634926607010) (:text |req) - |p $ {} (:type :expr) (:by |u0) (:at 1634927780305) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634927786365) (:text |mid-f) - |r $ {} (:type :expr) (:by |u0) (:at 1634925879592) + |u $ {} (:type :expr) (:by |u0) (:at 1635234343570) :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634925880124) (:text |{}) - |j $ {} (:type :expr) (:by |u0) (:at 1634925880395) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634925884137) (:text |:status) - |j $ {} (:type :leaf) (:by |u0) (:at 1634925885728) (:text |:ok) - |r $ {} (:type :expr) (:by |u0) (:at 1634925886286) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634925887053) (:text |:code) - |j $ {} (:type :leaf) (:by |u0) (:at 1634925890257) (:text |200) - |v $ {} (:type :expr) (:by |u0) (:at 1634925893151) - :data $ {} - |T $ {} (:type :leaf) (:by |u0) (:at 1634925894468) (:text |:body) - |j $ {} (:type :leaf) (:by |u0) (:at 1634925900012) (:text "|\"TODO some Body") + |T $ {} (:type :leaf) (:by |u0) (:at 1635234354812) (:text |on-request) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234348198) (:text |req) |reload! $ {} (:type :expr) (:by |u0) (:at 1633149998862) :data $ {} |T $ {} (:type :leaf) (:by |u0) (:at 1633149998862) (:text |defn) |j $ {} (:type :leaf) (:by |u0) (:at 1633149998862) (:text |reload!) |r $ {} (:type :expr) (:by |u0) (:at 1633149998862) :data $ {} + |v $ {} (:type :expr) (:by |u0) (:at 1635234381868) + :data $ {} + |T $ {} (:type :leaf) (:by |u0) (:at 1635234382819) (:text |println) + |j $ {} (:type :leaf) (:by |u0) (:at 1635234384199) (:text "|\"Reload") :proc $ {} (:type :expr) (:by |u0) (:at 1633149625774) :data $ {} |http.util $ {} @@ -241,4 +278,4 @@ :data $ {} :configs $ {} (:port 6001) (:init-fn |http.test/main!) (:reload-fn |http.test/reload!) :modules $ [] - :version |0.0.3 + :version |0.0.4 diff --git a/compact.cirru b/compact.cirru index bb5fa66..cc3e3cf 100644 --- a/compact.cirru +++ b/compact.cirru @@ -2,7 +2,7 @@ {} (:package |http) :configs $ {} (:init-fn |http.test/main!) (:reload-fn |http.test/reload!) :modules $ [] - :version |0.0.3 + :version |0.0.4 :files $ {} |http.core $ {} :ns $ quote @@ -19,19 +19,25 @@ http.core :refer $ serve-http! http.$meta :refer $ calcit-dirname calcit-filename :defs $ {} + |mid-call $ quote + defn mid-call () $ println "\"Calling internal function" + |on-request $ quote + defn on-request (req) (; println "\"Handling request:" req) + println $ :url req + ; mid-call + {} (:status :ok) (:code 200) + :headers $ {} (:content-type "\"application/json") + :body $ format-cirru-edn req |run-tests $ quote defn run-tests () (println "\"%%%% test for lib") (println calcit-filename calcit-dirname) (println "\"No tests...") |main! $ quote defn main! () $ run-tests - |mid-f $ quote - defn mid-f () $ println "\"fff222" |demo-server! $ quote defn demo-server! () $ serve-http! {} $ :port 4000 - fn (req) (println "\"got request2" req) (mid-f) - {} (:status :ok) (:code 200) (:body "\"TODO some Body") + fn (req) (on-request req) |reload! $ quote - defn reload! $ + defn reload! () $ println "\"Reload" |http.util $ {} :ns $ quote ns http.util $ :require diff --git a/src/lib.rs b/src/lib.rs index 9cf925e..4e6b0ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,32 @@ use cirru_edn::Edn; use std::collections::HashMap; use std::sync::Arc; -use tiny_http::{Response, Server}; +use tiny_http::{Method, Response, Server}; + +struct HttpServerOptions { + port: u16, + host: String, +} + +struct ResponseSkeleton { + code: u8, + headers: HashMap, + body: String, +} #[no_mangle] pub fn serve_http( args: Vec, - handler: Arc Result>, + handler: Arc) -> Result>, ) -> Result { - println!("TODO args: {:?}", args); - let server = Server::http("0.0.0.0:8000").unwrap(); + if args.is_empty() { + return Err(format!("expected an option, got nothing: {:?}", args)); + } + let options = parse_options(&args[0])?; + let server = Server::http(&format!("{}:{}", options.host, options.port)).unwrap(); + println!("Server started at {}:{}", options.host, options.port); - for request in server.incoming_requests() { + for mut request in server.incoming_requests() { // println!( // "received request! method: {:?}, url: {:?}, headers: {:?}", // request.method(), @@ -22,18 +37,117 @@ pub fn serve_http( let mut m: HashMap = HashMap::new(); m.insert( Edn::Keyword(String::from("method")), - Edn::Str(request.method().to_string()), + Edn::Keyword(request.method().to_string()), ); m.insert( Edn::Keyword(String::from("url")), Edn::Str(request.url().to_string()), ); + + let mut headers: HashMap = HashMap::new(); + + for pair in request.headers() { + headers.insert( + Edn::Keyword(pair.field.to_string()), + Edn::Str(pair.value.to_string()), + ); + } + m.insert(Edn::Keyword(String::from("headers")), Edn::Map(headers)); + + if request.method() != &Method::Get { + let mut content = String::new(); + request.as_reader().read_to_string(&mut content).unwrap(); + m.insert( + Edn::Keyword(String::from("body")), + Edn::Str(content.to_string()), + ); + } + let info = Edn::Map(m); - let result = handler(info)?; + let result = handler(vec![info])?; + let res = parse_response(&result)?; + + let mut response = Response::from_string(res.body.to_string()).with_status_code(res.code); - let response = Response::from_string(result.to_string()); + for (field, value) in res.headers { + response.add_header( + format!("{}: {}", field, value) + .parse::() + .unwrap(), + ); + } request.respond(response).map_err(|x| x.to_string())?; } Ok(Edn::Nil) } + +fn parse_options(d: &Edn) -> Result { + match d { + Edn::Nil => Ok(HttpServerOptions { + port: 4000, + host: String::from("0.0.0.0"), + }), + Edn::Map(m) => { + let mut options = HttpServerOptions { + port: 4000, + host: String::from("0.0.0.0"), + }; + options.port = match m.get(&Edn::Keyword(String::from("port"))) { + Some(Edn::Number(port)) => *port as u16, + None => 4000, + a => return Err(format!("invalid config for port: {:?}", a)), + }; + options.host = match m.get(&Edn::Keyword(String::from("host"))) { + Some(Edn::Str(host)) => host.to_owned(), + None => String::from("0.0.0.0"), + a => return Err(format!("invalid config for host: {:?}", a)), + }; + Ok(options) + } + _ => Err(format!("invalid data for options: {}", d)), + } +} + +/// from user response +fn parse_response(info: &Edn) -> Result { + if let Edn::Map(m) = info { + let mut res = ResponseSkeleton { + code: 200, + headers: HashMap::new(), + body: String::from(""), + }; + res.code = match m.get(&Edn::Keyword(String::from("code"))) { + Some(Edn::Number(n)) => *n as u8, + None => 200, + a => return Err(format!("invalid code: {:?}", a)), + }; + res.body = match m.get(&Edn::Keyword(String::from("body"))) { + Some(Edn::Str(s)) => s.to_owned(), + Some(a) => a.to_string(), + None => String::from(""), + }; + res.headers = match m.get(&Edn::Keyword(String::from("headers"))) { + Some(Edn::Map(m)) => { + let mut hs: HashMap = HashMap::new(); + for (k, v) in m { + if let Edn::Keyword(s) = k { + if let Edn::Str(s2) = v { + hs.insert(s.to_owned(), s2.to_owned()); + } else { + hs.insert(s.to_owned(), v.to_string()); + } + } else { + return Err(format!("invalid head entry: {}", k)); + } + } + hs + } + Some(a) => return Err(format!("invalid data for headers: {}", a)), + None => HashMap::new(), + }; + Ok(res) + } else { + Err(format!("invalid response shape: {}", info)) + } +} From 129207d3845b8a9341ef34c190ce02e6b50430cf Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Wed, 27 Oct 2021 15:07:41 +0800 Subject: [PATCH 2/2] update about ABI version; tag 0.0.5 --- Cargo.lock | 2 +- Cargo.toml | 2 +- calcit.cirru | 2 +- compact.cirru | 2 +- src/lib.rs | 6 ++++++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fb979e..0e6f39b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "calcit_http" -version = "0.0.4" +version = "0.0.5" dependencies = [ "cirru_edn", "cirru_parser", diff --git a/Cargo.toml b/Cargo.toml index a744997..2e05134 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcit_http" -version = "0.0.4" +version = "0.0.5" authors = ["jiyinyiyong "] edition = "2018" diff --git a/calcit.cirru b/calcit.cirru index 231ae0c..29ae989 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -278,4 +278,4 @@ :data $ {} :configs $ {} (:port 6001) (:init-fn |http.test/main!) (:reload-fn |http.test/reload!) :modules $ [] - :version |0.0.4 + :version |0.0.5 diff --git a/compact.cirru b/compact.cirru index cc3e3cf..77096f3 100644 --- a/compact.cirru +++ b/compact.cirru @@ -2,7 +2,7 @@ {} (:package |http) :configs $ {} (:init-fn |http.test/main!) (:reload-fn |http.test/reload!) :modules $ [] - :version |0.0.4 + :version |0.0.5 :files $ {} |http.core $ {} :ns $ quote diff --git a/src/lib.rs b/src/lib.rs index 4e6b0ca..9fecfd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,16 @@ struct ResponseSkeleton { body: String, } +#[no_mangle] +pub fn abi_version() -> String { + String::from("0.0.1") +} + #[no_mangle] pub fn serve_http( args: Vec, handler: Arc) -> Result>, + _finish: Box, ) -> Result { if args.is_empty() { return Err(format!("expected an option, got nothing: {:?}", args));