diff --git a/http-server/Cargo.toml b/http-server/Cargo.toml index 040edcf415..e9cb208bb0 100644 --- a/http-server/Cargo.toml +++ b/http-server/Cargo.toml @@ -18,7 +18,8 @@ jsonrpsee-core = { path = "../core", version = "0.13.1", features = ["server", " globset = "0.4" lazy_static = "1.4" tracing = "0.1" -serde_json = "1" +serde_json = { version = "1.0", features = ["raw_value"] } +serde = "1" tokio = { version = "1.16", features = ["rt-multi-thread", "macros"] } unicase = "2.6.0" diff --git a/http-server/src/server.rs b/http-server/src/server.rs index e276b64706..7f96b301b3 100644 --- a/http-server/src/server.rs +++ b/http-server/src/server.rs @@ -170,11 +170,20 @@ impl Builder { } /// Enable health endpoint. - /// Allows you to expose one of the methods under GET / The method will be invoked with no parameters. Error returned from the method will be converted to status 500 response. - /// Expects a tuple with (, ). - pub fn health_api(mut self, path: impl Into, method: impl Into) -> Self { - self.health_api = Some(HealthApi { path: path.into(), method: method.into() }); - self + /// Allows you to expose one of the methods under GET / The method will be invoked with no parameters. + /// Error returned from the method will be converted to status 500 response. + /// Expects a tuple with (, ). + /// + /// Fails if the path is missing `/`. + pub fn health_api(mut self, path: impl Into, method: impl Into) -> Result { + let path = path.into(); + + if !path.starts_with("/") { + return Err(Error::Custom(format!("Health endpoint path must start with `/` to work, got: {}", path))); + } + + self.health_api = Some(HealthApi { path: path, method: method.into() }); + Ok(self) } /// Finalizes the configuration of the server with customized TCP settings on the socket and on hyper. @@ -754,7 +763,17 @@ async fn process_health_request( middleware.on_response(request_start); match data { - Some(resp) if success => Ok(response::ok_response(resp)), + Some(data) if success => { + #[derive(serde::Deserialize)] + struct RpcPayload<'a> { + #[serde(borrow)] + result: &'a serde_json::value::RawValue, + } + + let payload: RpcPayload = serde_json::from_str(&data) + .expect("valid JSON-RPC response must have a result field and be valid JSON; qed"); + Ok(response::ok_response(payload.result.to_string())) + } _ => Ok(response::internal_error()), } } diff --git a/tests/tests/helpers.rs b/tests/tests/helpers.rs index 86760db67f..ee17ad66d8 100644 --- a/tests/tests/helpers.rs +++ b/tests/tests/helpers.rs @@ -225,6 +225,7 @@ pub async fn http_server_with_access_control(acl: AccessControl) -> (SocketAddr, let server = HttpServerBuilder::default() .set_access_control(acl) .health_api("/health", "system_health") + .unwrap() .build("127.0.0.1:0") .await .unwrap(); @@ -233,7 +234,7 @@ pub async fn http_server_with_access_control(acl: AccessControl) -> (SocketAddr, module.register_method("say_hello", |_, _| Ok("hello")).unwrap(); module.register_method("notif", |_, _| Ok("")).unwrap(); - module.register_method("system_health", |_, _| Ok("im ok")).unwrap(); + module.register_method("system_health", |_, _| Ok(serde_json::json!({ "health": true }))).unwrap(); let handle = server.start(module).unwrap(); (addr, handle) diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index c9dce9704f..f62faddcb4 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -767,5 +767,5 @@ async fn http_health_api_works() { let bytes = hyper::body::to_bytes(res.into_body()).await.unwrap(); let out = String::from_utf8(bytes.to_vec()).unwrap(); - assert_eq!(out, "{\"jsonrpc\":\"2.0\",\"result\":\"im ok\",\"id\":0}"); + assert_eq!(out.as_str(), "{\"health\":true}"); }