diff --git a/Cargo.toml b/Cargo.toml index 9a0afd0..501606e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,11 @@ members = [ "itconfig", "itconfig_tests", "examples/diesel", -# "examples/rocket", + "examples/rocket", + "examples/hyper", +] + +default-members = [ + "itconfig", + "itconfig_tests", ] diff --git a/examples/hyper/Cargo.toml b/examples/hyper/Cargo.toml new file mode 100644 index 0000000..1b2e212 --- /dev/null +++ b/examples/hyper/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "itconfig-hyper-example" +version = "0.1.0" +authors = ["Dmitriy Pleshevskiy "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itconfig = { path = "../../itconfig" } +hyper = "0.13.1" +serde_json = "1.0" +tokio = { version = "0.2", features = ["macros"] } +bytes = "0.5" +futures-util = { version = "0.3", default-features = false } +pretty_env_logger = "0.3" \ No newline at end of file diff --git a/examples/hyper/src/main.rs b/examples/hyper/src/main.rs new file mode 100644 index 0000000..21112fe --- /dev/null +++ b/examples/hyper/src/main.rs @@ -0,0 +1,140 @@ +#![deny(warnings)] + +#[macro_use] +extern crate itconfig; + +use bytes::buf::BufExt; +use futures_util::{stream, StreamExt}; +use hyper::client::HttpConnector; +use hyper::service::{make_service_fn, service_fn}; +use hyper::{header, Body, Client, Method, Request, Response, Server, StatusCode}; + + +config! { + HYPER { + HOST < ( + ADDR => "127.0.0.1", + ":", + PORT => 8000, + ), + + JSON_API_URL < ( + "http://", + HYPER_HOST, + "/json_api", + ), + } +} + + +type GenericError = Box; +type HyperResult = std::result::Result; + + +static INDEX: &[u8] = b"test.html"; +static INTERNAL_SERVER_ERROR: &[u8] = b"Internal Server Error"; +static NOTFOUND: &[u8] = b"Not Found"; +static POST_DATA: &str = r#"{"original": "data"}"#; + + +async fn client_request_response(client: &Client) -> HyperResult> { + let req = Request::builder() + .method(Method::POST) + .uri(cfg::HYPER::JSON_API_URL()) + .header(header::CONTENT_TYPE, "application/json") + .body(POST_DATA.into()) + .unwrap(); + + let web_res = client.request(req).await?; + // Compare the JSON we sent (before) with what we received (after): + let before = stream::once(async { + Ok(format!( + "POST request body: {}
Response: ", + POST_DATA, + ) + .into()) + }); + let after = web_res.into_body(); + let body = Body::wrap_stream(before.chain(after)); + + Ok(Response::new(body)) +} + +async fn api_post_response(req: Request) -> HyperResult> { + // Aggregate the body... + let whole_body = hyper::body::aggregate(req).await?; + // Decode as JSON... + let mut data: serde_json::Value = serde_json::from_reader(whole_body.reader())?; + // Change the JSON... + data["test"] = serde_json::Value::from("test_value"); + // And respond with the new JSON. + let json = serde_json::to_string(&data)?; + let response = Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, "application/json") + .body(Body::from(json))?; + Ok(response) +} + +async fn api_get_response() -> HyperResult> { + let data = vec!["foo", "bar"]; + let res = match serde_json::to_string(&data) { + Ok(json) => Response::builder() + .header(header::CONTENT_TYPE, "application/json") + .body(Body::from(json)) + .unwrap(), + Err(_) => Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(INTERNAL_SERVER_ERROR.into()) + .unwrap(), + }; + Ok(res) +} + +async fn response_examples( + req: Request, + client: Client, +) -> HyperResult> { + match (req.method(), req.uri().path()) { + (&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())), + (&Method::GET, "/test.html") => client_request_response(&client).await, + (&Method::POST, "/json_api") => api_post_response(req).await, + (&Method::GET, "/json_api") => api_get_response().await, + _ => { + // Return 404 not found response. + Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(NOTFOUND.into()) + .unwrap()) + } + } +} + +#[tokio::main] +async fn main() -> HyperResult<()> { + pretty_env_logger::init(); + + let addr = cfg::HYPER::HOST().parse().unwrap(); + + // Share a `Client` with all `Service`s + let client = Client::new(); + + let new_service = make_service_fn(move |_| { + // Move a clone of `client` into the `service_fn`. + let client = client.clone(); + async { + Ok::<_, GenericError>(service_fn(move |req| { + // Clone again to ensure that client outlives this closure. + response_examples(req, client.to_owned()) + })) + } + }); + + let server = Server::bind(&addr).serve(new_service); + + println!("Listening on http://{}", addr); + + server.await?; + + Ok(()) +}