karyon_jsonrpc/server/http/
h1h2.rs1use std::{net::SocketAddr, sync::Arc};
4
5use bytes::Bytes;
6use http_body_util::{BodyExt, Full, LengthLimitError, Limited};
7use hyper::{
8 body::{Body, Incoming},
9 service::service_fn,
10 Method, Request, Response, StatusCode,
11};
12use hyper_util::server::conn::auto::Builder as HttpConnBuilder;
13use log::{debug, error};
14
15#[cfg(feature = "tokio")]
16use hyper_util::rt::TokioTimer as HyperTimer;
17#[cfg(feature = "smol")]
18use smol_hyper::rt::SmolTimer as HyperTimer;
19
20use karyon_core::{
21 async_runtime::net::{TcpListener, TcpStream},
22 async_util::Backoff,
23};
24
25use crate::{
26 codec::{decode_body, JsonRpcCodec},
27 error::Result,
28 message,
29 server::{
30 http::{
31 encode_response, json_response, json_response_bytes, parse_error_response,
32 HyperExecutor, ERR_BODY_TOO_LARGE, ERR_ENCODE_RESPONSE, ERR_METHOD_NOT_ALLOWED,
33 ERR_READ_BODY, MAX_HTTP_BODY_SIZE,
34 },
35 Server,
36 },
37};
38
39pub(super) async fn accept_tcp<C: JsonRpcCodec>(
40 server: &Arc<Server>,
41 listener: &TcpListener,
42 codec: &C,
43 backoff: &Backoff,
44) {
45 match listener.accept().await {
46 Ok((stream, peer_addr)) => {
47 backoff.reset();
48 server
49 .task_group
50 .spawn(serve_task(server.clone(), stream, peer_addr, codec.clone()));
51 }
52 Err(err) => {
53 error!("Accept TCP connection: {err}");
54 backoff.sleep().await;
55 }
56 }
57}
58
59async fn serve_task<C: JsonRpcCodec>(
60 server: Arc<Server>,
61 stream: TcpStream,
62 peer_addr: SocketAddr,
63 codec: C,
64) -> Result<()> {
65 if let Err(err) = serve_conn(server, stream, peer_addr, codec).await {
66 error!("HTTP/1-2 from {peer_addr}: {err}");
67 }
68 Ok(())
69}
70
71async fn serve_conn<C: JsonRpcCodec>(
72 server: Arc<Server>,
73 stream: TcpStream,
74 peer_addr: SocketAddr,
75 codec: C,
76) -> Result<()> {
77 debug!("New HTTP/1-2 connection from {peer_addr}");
78
79 let task_group = server.task_group.clone();
80 let service = service_fn(move |req: Request<Incoming>| {
81 let server = server.clone();
82 let codec = codec.clone();
83 async move { handle_hyper_request(server, req, codec).await }
84 });
85
86 #[cfg(feature = "smol")]
87 let io = smol_hyper::rt::FuturesIo::new(stream);
88 #[cfg(feature = "tokio")]
89 let io = hyper_util::rt::TokioIo::new(stream);
90
91 let mut builder = HttpConnBuilder::new(HyperExecutor::new(task_group));
92 builder.http1().timer(HyperTimer::new());
96 let conn = builder.serve_connection(io, service);
97
98 if let Err(err) = conn.await {
99 debug!("HTTP/1-2 from {peer_addr} closed: {err}");
100 }
101
102 Ok(())
103}
104
105async fn handle_hyper_request<C: JsonRpcCodec>(
106 server: Arc<Server>,
107 req: Request<Incoming>,
108 codec: C,
109) -> std::result::Result<Response<Full<Bytes>>, hyper::Error> {
110 if req.method() != Method::POST {
111 return Ok(json_response(
112 StatusCode::METHOD_NOT_ALLOWED,
113 ERR_METHOD_NOT_ALLOWED,
114 ));
115 }
116
117 if let Some(len) = req.body().size_hint().upper() {
119 if len > MAX_HTTP_BODY_SIZE {
120 return Ok(json_response(
121 StatusCode::PAYLOAD_TOO_LARGE,
122 ERR_BODY_TOO_LARGE,
123 ));
124 }
125 }
126
127 let limited = Limited::new(req.into_body(), MAX_HTTP_BODY_SIZE as usize);
129 let body = match limited.collect().await {
130 Ok(collected) => collected.to_bytes(),
131 Err(err) => {
132 if err.downcast_ref::<LengthLimitError>().is_some() {
133 return Ok(json_response(
134 StatusCode::PAYLOAD_TOO_LARGE,
135 ERR_BODY_TOO_LARGE,
136 ));
137 }
138 return Ok(json_response(StatusCode::BAD_REQUEST, ERR_READ_BODY));
139 }
140 };
141
142 let response = match decode_body(&codec, &body) {
143 Ok(msg) => server.handle_request(None, msg).await,
144 Err(_) => parse_error_response(),
145 };
146 debug!("--> {response}");
147
148 let content_type = &server.config.http_config.content_type;
149 Ok(encode_hyper_response(&codec, content_type, &response))
150}
151
152fn encode_hyper_response<C: JsonRpcCodec>(
153 codec: &C,
154 content_type: &str,
155 resp: &message::Response,
156) -> Response<Full<Bytes>> {
157 match encode_response(codec, resp) {
158 Ok(body) => json_response_bytes(StatusCode::OK, content_type, body),
159 Err(err) => {
160 error!("Encode HTTP response: {err}");
161 json_response(StatusCode::INTERNAL_SERVER_ERROR, ERR_ENCODE_RESPONSE)
162 }
163 }
164}