Skip to main content

karyon_jsonrpc/server/http/
mod.rs

1//! HTTP server: HTTP/1.1 + HTTP/2 over TCP, optional HTTP/3 over QUIC.
2
3mod h1h2;
4#[cfg(feature = "http3")]
5mod h3;
6
7use std::{net::SocketAddr, sync::Arc};
8
9use bytes::Bytes;
10use http_body_util::Full;
11use hyper::{Response, StatusCode};
12
13use karyon_core::async_runtime::net::TcpListener;
14
15#[cfg(feature = "http3")]
16use karyon_core::async_util::select;
17
18use karyon_net::Endpoint;
19
20pub(super) use crate::hyper_exec::HyperExecutor;
21
22use crate::{
23    error::{Error, Result},
24    server::Server,
25};
26
27/// Max HTTP request body size (1 MB).
28pub(super) const MAX_HTTP_BODY_SIZE: u64 = 1024 * 1024;
29
30// Pre-encoded JSON-RPC errors returned before a message can be parsed.
31pub(super) const ERR_METHOD_NOT_ALLOWED: &str = r#"{"jsonrpc":"2.0","error":{"code":-32600,"message":"Only POST method is accepted"},"id":null}"#;
32pub(super) const ERR_BODY_TOO_LARGE: &str =
33    r#"{"jsonrpc":"2.0","error":{"code":-32600,"message":"Request body too large"},"id":null}"#;
34pub(super) const ERR_READ_BODY: &str = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Failed to read request body"},"id":null}"#;
35
36/// HTTP server (TCP for h1/h2, optional QUIC for h3).
37pub(crate) struct HttpServer {
38    tcp_listener: TcpListener,
39    #[cfg(feature = "http3")]
40    quic_endpoint: Option<karyon_net::quic::QuicEndpoint>,
41}
42
43impl HttpServer {
44    /// Create HTTP/1.1 + HTTP/2 server.
45    pub(crate) async fn new(endpoint: &Endpoint) -> Result<Self> {
46        let addr = SocketAddr::try_from(endpoint.clone())?;
47        let tcp_listener = TcpListener::bind(addr).await?;
48        Ok(Self {
49            tcp_listener,
50            #[cfg(feature = "http3")]
51            quic_endpoint: None,
52        })
53    }
54
55    /// Create HTTP/1.1 + HTTP/2 + HTTP/3 server.
56    #[cfg(feature = "http3")]
57    pub(crate) async fn new_h3(
58        endpoint: &Endpoint,
59        quic_config: karyon_net::quic::ServerQuicConfig,
60    ) -> Result<Self> {
61        let addr = SocketAddr::try_from(endpoint.clone())?;
62        let tcp_listener = TcpListener::bind(addr).await?;
63        let actual_addr = tcp_listener.local_addr()?;
64        let quic_ep_addr = Endpoint::new_quic_addr(actual_addr);
65        let quic_endpoint =
66            karyon_net::quic::QuicEndpoint::listen(&quic_ep_addr, quic_config).await?;
67        Ok(Self {
68            tcp_listener,
69            quic_endpoint: Some(quic_endpoint),
70        })
71    }
72
73    pub(crate) fn local_endpoint(&self) -> Result<Endpoint> {
74        let addr = self.tcp_listener.local_addr()?;
75        format!("http://{addr}/")
76            .parse()
77            .map_err(|e: karyon_net::Error| Error::HttpError(e.to_string()))
78    }
79}
80
81/// Run the HTTP accept loop.
82pub(crate) async fn accept_loop(server: Arc<Server>, http_server: &HttpServer) -> Result<()> {
83    #[cfg(feature = "http3")]
84    if let Some(ref quic_ep) = http_server.quic_endpoint {
85        loop {
86            select(
87                h1h2::accept_tcp(&server, &http_server.tcp_listener),
88                h3::accept_h3(&server, quic_ep),
89            )
90            .await;
91        }
92    }
93
94    loop {
95        h1h2::accept_tcp(&server, &http_server.tcp_listener).await;
96    }
97}
98
99// -- Shared helpers --
100
101pub(super) fn json_response(status: StatusCode, body: &str) -> Response<Full<Bytes>> {
102    Response::builder()
103        .status(status)
104        .header("Content-Type", "application/json")
105        .body(Full::new(Bytes::from(body.to_string())))
106        .unwrap()
107}
108
109pub(super) fn json_response_bytes(status: StatusCode, body: Vec<u8>) -> Response<Full<Bytes>> {
110    Response::builder()
111        .status(status)
112        .header("Content-Type", "application/json")
113        .body(Full::new(Bytes::from(body)))
114        .unwrap()
115}