Skip to main content

servers/
grpc.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod builder;
16mod cancellation;
17pub mod context_auth;
18mod database;
19pub mod flight;
20pub mod frontend_grpc_handler;
21pub mod greptime_handler;
22pub mod memory_limit;
23pub mod prom_query_gateway;
24pub mod region_server;
25
26use std::any::Any;
27use std::net::{IpAddr, SocketAddr};
28use std::time::Duration;
29
30use api::v1::health_check_server::{HealthCheck, HealthCheckServer};
31use api::v1::{HealthCheckRequest, HealthCheckResponse};
32use async_trait::async_trait;
33use common_base::readable_size::ReadableSize;
34use common_grpc::channel_manager::{
35    DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE, DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
36};
37use common_telemetry::{error, info, warn};
38use futures::FutureExt;
39use http::{HeaderName, Method};
40use otel_arrow_rust::proto::opentelemetry::arrow::v1::arrow_metrics_service_server::ArrowMetricsServiceServer;
41use serde::{Deserialize, Serialize};
42use snafu::{OptionExt, ResultExt, ensure};
43use tokio::net::TcpListener;
44use tokio::sync::Mutex;
45use tokio::sync::oneshot::{self, Receiver, Sender};
46use tonic::service::Routes;
47use tonic::service::interceptor::InterceptedService;
48use tonic::transport::ServerTlsConfig;
49use tonic::transport::server::TcpIncoming;
50use tonic::{Request, Response, Status};
51use tonic_reflection::server::v1::{ServerReflection, ServerReflectionServer};
52use tower_http::cors::{AllowHeaders, CorsLayer};
53
54use crate::error::{AlreadyStartedSnafu, InternalSnafu, Result, StartGrpcSnafu, TcpBindSnafu};
55use crate::grpc::memory_limit::MemoryLimiterExtensionService;
56use crate::http::cors_allow_origin;
57use crate::install_default_crypto_provider;
58use crate::metrics::MetricsMiddlewareLayer;
59use crate::otel_arrow::{HeaderInterceptor, OtelArrowServiceHandler};
60use crate::query_handler::OpenTelemetryProtocolHandlerRef;
61use crate::server::Server;
62use crate::tls::TlsOption;
63
64type TonicResult<T> = std::result::Result<T, Status>;
65
66#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
67#[serde(default)]
68pub struct GrpcOptions {
69    /// The address to bind the gRPC server.
70    pub bind_addr: String,
71    /// The address to advertise to clients.
72    pub server_addr: String,
73    /// Max gRPC receiving(decoding) message size
74    pub max_recv_message_size: ReadableSize,
75    /// Max gRPC sending(encoding) message size
76    pub max_send_message_size: ReadableSize,
77    /// Compression mode in Arrow Flight service.
78    pub flight_compression: FlightCompression,
79    pub runtime_size: usize,
80    #[serde(default = "Default::default")]
81    pub tls: TlsOption,
82    /// Maximum time that a channel may exist.
83    /// Useful when the server wants to control the reconnection of its clients.
84    /// Default to `None`, means infinite.
85    #[serde(with = "humantime_serde")]
86    pub max_connection_age: Option<Duration>,
87    /// The HTTP/2 keep-alive interval.
88    #[serde(with = "humantime_serde")]
89    pub http2_keep_alive_interval: Duration,
90    /// The HTTP/2 keep-alive timeout.
91    #[serde(with = "humantime_serde")]
92    pub http2_keep_alive_timeout: Duration,
93    /// Whether to enable CORS, required by gRPC-Web clients in browsers.
94    /// Only the frontend's public gRPC server honors it.
95    pub enable_cors: bool,
96    /// Origins allowed by CORS. Empty allows any origin.
97    pub cors_allowed_origins: Vec<String>,
98}
99
100impl GrpcOptions {
101    /// Detect the server address.
102    #[cfg(not(target_os = "android"))]
103    pub fn detect_server_addr(&mut self) {
104        if self.server_addr.is_empty() {
105            match local_ip_address::local_ip() {
106                Ok(ip) => {
107                    let port = port_from_bind_addr(&self.bind_addr);
108                    let detected_addr = format_server_addr(ip, port);
109                    info!("Using detected: {} as server address", detected_addr);
110                    self.server_addr = detected_addr;
111                }
112                Err(e) => {
113                    error!("Failed to detect local ip address: {}", e);
114                }
115            }
116        }
117    }
118
119    #[cfg(target_os = "android")]
120    pub fn detect_server_addr(&mut self) {
121        if self.server_addr.is_empty() {
122            common_telemetry::debug!("detect local IP is not supported on Android");
123        }
124    }
125
126    /// Create a [GrpcServerConfig] from self's options.
127    pub fn as_config(&self) -> GrpcServerConfig {
128        GrpcServerConfig {
129            max_recv_message_size: self.max_recv_message_size.as_bytes() as usize,
130            max_send_message_size: self.max_send_message_size.as_bytes() as usize,
131            tls: self.tls.clone(),
132            max_connection_age: self.max_connection_age,
133        }
134    }
135}
136
137const DEFAULT_GRPC_ADDR_PORT: u16 = 4001;
138
139fn port_from_bind_addr(bind_addr: &str) -> u16 {
140    bind_addr
141        .rsplit_once(':')
142        .and_then(|(_, port)| port.parse().ok())
143        .unwrap_or(DEFAULT_GRPC_ADDR_PORT)
144}
145
146fn format_server_addr(ip: IpAddr, port: u16) -> String {
147    SocketAddr::new(ip, port).to_string()
148}
149
150const DEFAULT_INTERNAL_GRPC_ADDR_PORT: &str = "4010";
151
152impl Default for GrpcOptions {
153    fn default() -> Self {
154        Self {
155            bind_addr: format!("127.0.0.1:{}", DEFAULT_GRPC_ADDR_PORT),
156            // If hostname is not set, the server will use the local ip address as the hostname.
157            server_addr: String::new(),
158            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE,
159            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
160            flight_compression: FlightCompression::ArrowIpc,
161            runtime_size: 8,
162            tls: TlsOption::default(),
163            max_connection_age: None,
164            http2_keep_alive_interval: Duration::from_secs(10),
165            http2_keep_alive_timeout: Duration::from_secs(3),
166            enable_cors: false,
167            cors_allowed_origins: Vec::new(),
168        }
169    }
170}
171
172impl GrpcOptions {
173    /// Default options for internal gRPC server.
174    /// The internal gRPC server is used for communication between different nodes in cluster.
175    /// It is not exposed to the outside world.
176    pub fn internal_default() -> Self {
177        Self {
178            bind_addr: format!("127.0.0.1:{}", DEFAULT_INTERNAL_GRPC_ADDR_PORT),
179            // If hostname is not set, the server will use the local ip address as the hostname.
180            server_addr: format!("127.0.0.1:{}", DEFAULT_INTERNAL_GRPC_ADDR_PORT),
181            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE,
182            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
183            flight_compression: FlightCompression::ArrowIpc,
184            runtime_size: 8,
185            tls: TlsOption::default(),
186            max_connection_age: None,
187            http2_keep_alive_interval: Duration::from_secs(10),
188            http2_keep_alive_timeout: Duration::from_secs(3),
189            enable_cors: false,
190            cors_allowed_origins: Vec::new(),
191        }
192    }
193
194    pub fn with_bind_addr(mut self, bind_addr: &str) -> Self {
195        self.bind_addr = bind_addr.to_string();
196        self
197    }
198
199    pub fn with_server_addr(mut self, server_addr: &str) -> Self {
200        self.server_addr = server_addr.to_string();
201        self
202    }
203}
204
205#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
206#[serde(rename_all = "snake_case")]
207pub enum FlightCompression {
208    /// Disable all compression in Arrow Flight service.
209    #[default]
210    None,
211    /// Enable only transport layer compression (zstd).
212    Transport,
213    /// Enable only payload compression (lz4)
214    ArrowIpc,
215    /// Enable all compression.
216    All,
217}
218
219impl FlightCompression {
220    pub fn transport_compression(&self) -> bool {
221        self == &FlightCompression::Transport || self == &FlightCompression::All
222    }
223
224    pub fn arrow_compression(&self) -> bool {
225        self == &FlightCompression::ArrowIpc || self == &FlightCompression::All
226    }
227}
228
229/// The wrapped OTLP Arrow service type used by [`GrpcServer`].
230type OtelArrowService = MemoryLimiterExtensionService<
231    InterceptedService<
232        ArrowMetricsServiceServer<OtelArrowServiceHandler<OpenTelemetryProtocolHandlerRef>>,
233        HeaderInterceptor,
234    >,
235>;
236
237pub struct GrpcServer {
238    // states
239    shutdown_tx: Mutex<Option<Sender<()>>>,
240    /// gRPC serving state receiver. Only present if the gRPC server is started.
241    /// Used to wait for the server to stop, performing the old blocking fashion.
242    serve_state: Mutex<Option<Receiver<Result<()>>>>,
243    // handlers
244    routes: Mutex<Option<Routes>>,
245    // tls config
246    tls_config: Option<ServerTlsConfig>,
247    // Otel arrow service
248    otel_arrow_service: Mutex<Option<OtelArrowService>>,
249    bind_addr: Option<SocketAddr>,
250    name: Option<String>,
251    config: GrpcServerConfig,
252    /// `None` disables CORS.
253    cors_allowed_origins: Option<Vec<String>>,
254}
255
256/// Grpc Server configuration
257#[derive(Debug, Clone)]
258pub struct GrpcServerConfig {
259    // Max gRPC receiving(decoding) message size
260    pub max_recv_message_size: usize,
261    // Max gRPC sending(encoding) message size
262    pub max_send_message_size: usize,
263    pub tls: TlsOption,
264    /// Maximum time that a channel may exist.
265    /// Useful when the server wants to control the reconnection of its clients.
266    /// Default to `None`, means infinite.
267    pub max_connection_age: Option<Duration>,
268}
269
270impl Default for GrpcServerConfig {
271    fn default() -> Self {
272        Self {
273            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE.as_bytes() as usize,
274            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE.as_bytes() as usize,
275            tls: TlsOption::default(),
276            max_connection_age: None,
277        }
278    }
279}
280
281impl GrpcServer {
282    fn cors_layer(&self) -> Result<Option<CorsLayer>> {
283        let Some(allowed_origins) = &self.cors_allowed_origins else {
284            return Ok(None);
285        };
286        let layer = CorsLayer::new()
287            .allow_methods([Method::POST])
288            .allow_origin(cors_allow_origin(allowed_origins)?)
289            .allow_headers(AllowHeaders::any())
290            // Trailers-only responses (most errors) carry `grpc-status` in HTTP
291            // headers, which browsers hide unless exposed.
292            .expose_headers([
293                HeaderName::from_static("grpc-status"),
294                HeaderName::from_static("grpc-message"),
295                HeaderName::from_static("grpc-status-details-bin"),
296            ]);
297        Ok(Some(layer))
298    }
299
300    pub fn create_healthcheck_service(&self) -> HealthCheckServer<impl HealthCheck> {
301        HealthCheckServer::new(HealthCheckHandler)
302    }
303
304    pub fn create_reflection_service(&self) -> ServerReflectionServer<impl ServerReflection> {
305        tonic_reflection::server::Builder::configure()
306            .register_encoded_file_descriptor_set(api::v1::GREPTIME_GRPC_DESC)
307            .with_service_name("greptime.v1.GreptimeDatabase")
308            .with_service_name("greptime.v1.HealthCheck")
309            .with_service_name("greptime.v1.RegionServer")
310            .build_v1()
311            .inspect_err(|e| {
312                common_telemetry::error!(e; "Failed to build gRPC reflection server");
313            })
314            .unwrap()
315    }
316
317    pub async fn wait_for_serve(&self) -> Result<()> {
318        let mut serve_state = self.serve_state.lock().await;
319        let rx = serve_state.take().context(InternalSnafu {
320            err_msg: "gRPC serving state is unknown, maybe the server is not started, \
321                      or we have already waited for the serve result before.",
322        })?;
323        let Ok(result) = rx.await else {
324            warn!("Background gRPC serving task is quited before we can receive the serve result.");
325            return Ok(());
326        };
327        if let Err(e) = result {
328            error!(e; "GRPC serve error");
329        }
330        Ok(())
331    }
332}
333
334pub struct HealthCheckHandler;
335
336#[async_trait]
337impl HealthCheck for HealthCheckHandler {
338    async fn health_check(
339        &self,
340        _req: Request<HealthCheckRequest>,
341    ) -> TonicResult<Response<HealthCheckResponse>> {
342        Ok(Response::new(HealthCheckResponse {}))
343    }
344}
345
346pub const GRPC_SERVER: &str = "GRPC_SERVER";
347
348#[async_trait]
349impl Server for GrpcServer {
350    async fn shutdown(&self) -> Result<()> {
351        let mut shutdown_tx = self.shutdown_tx.lock().await;
352        if let Some(tx) = shutdown_tx.take()
353            && tx.send(()).is_err()
354        {
355            info!("Receiver dropped, the grpc server has already exited");
356        }
357        info!("Shutdown grpc server");
358
359        Ok(())
360    }
361
362    async fn start(&mut self, addr: SocketAddr) -> Result<()> {
363        let routes = {
364            let mut routes = self.routes.lock().await;
365            let Some(routes) = routes.take() else {
366                return AlreadyStartedSnafu {
367                    server: self.name(),
368                }
369                .fail();
370            };
371            routes
372        };
373
374        let (tx, rx) = oneshot::channel();
375        let (incoming, addr) = {
376            let mut shutdown_tx = self.shutdown_tx.lock().await;
377            ensure!(
378                shutdown_tx.is_none(),
379                AlreadyStartedSnafu { server: "gRPC" }
380            );
381
382            let listener = TcpListener::bind(addr)
383                .await
384                .context(TcpBindSnafu { addr })?;
385            let addr = listener.local_addr().context(TcpBindSnafu { addr })?;
386            let incoming = TcpIncoming::from(listener).with_nodelay(Some(true));
387            info!("gRPC server(name={}) is bound to {}", self.name(), addr);
388
389            *shutdown_tx = Some(tx);
390
391            (incoming, addr)
392        };
393
394        let cors_layer = self.cors_layer()?;
395
396        // `Server::layer` prepends, so the first layer added is outermost. CORS must
397        // sit outside `GrpcWebLayer`, which rejects the `OPTIONS` preflight with a 400.
398        let middleware_layer = tower::ServiceBuilder::new()
399            .layer(MetricsMiddlewareLayer)
400            .option_layer(cors_layer)
401            .into_inner();
402
403        let mut builder = tonic::transport::Server::builder()
404            .accept_http1(true)
405            .layer(middleware_layer)
406            .layer(tonic_web::GrpcWebLayer::new());
407
408        if let Some(tls_config) = self.tls_config.clone() {
409            // tonic builds the underlying rustls server config here, which requires a
410            // process-level crypto provider to be installed first.
411            if let Err(err) = install_default_crypto_provider() {
412                warn!("Failed to install default rustls crypto provider: {err}");
413            }
414            builder = builder.tls_config(tls_config).context(StartGrpcSnafu)?;
415        }
416
417        if let Some(max_connection_age) = self.config.max_connection_age {
418            builder = builder.max_connection_age(max_connection_age);
419        }
420
421        let mut builder = builder
422            .add_routes(routes)
423            .add_service(self.create_healthcheck_service())
424            .add_service(self.create_reflection_service());
425
426        if let Some(otel_arrow_service) = self.otel_arrow_service.lock().await.take() {
427            builder = builder.add_service(otel_arrow_service);
428        }
429
430        let (serve_state_tx, serve_state_rx) = oneshot::channel();
431        let mut serve_state = self.serve_state.lock().await;
432        *serve_state = Some(serve_state_rx);
433
434        let _handle = common_runtime::spawn_global(async move {
435            let result = builder
436                .serve_with_incoming_shutdown(incoming, rx.map(drop))
437                .await
438                .context(StartGrpcSnafu);
439            serve_state_tx.send(result)
440        });
441
442        self.bind_addr = Some(addr);
443        Ok(())
444    }
445
446    fn name(&self) -> &str {
447        if let Some(name) = &self.name {
448            name
449        } else {
450            GRPC_SERVER
451        }
452    }
453
454    fn bind_addr(&self) -> Option<SocketAddr> {
455        self.bind_addr
456    }
457
458    fn as_any(&self) -> &dyn Any {
459        self
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
466
467    use common_runtime::Runtime;
468    use common_runtime::runtime::{BuilderBuild, RuntimeTrait};
469    use http::header::{
470        ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN,
471        ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_REQUEST_HEADERS,
472        ACCESS_CONTROL_REQUEST_METHOD, CONTENT_TYPE, ORIGIN,
473    };
474    use http::{HeaderName, Method, StatusCode};
475
476    use super::{
477        DEFAULT_GRPC_ADDR_PORT, GrpcServer, GrpcServerConfig, format_server_addr,
478        port_from_bind_addr,
479    };
480    use crate::grpc::builder::GrpcServerBuilder;
481    use crate::server::Server;
482
483    #[test]
484    fn test_port_from_bind_addr() {
485        assert_eq!(3002, port_from_bind_addr("127.0.0.1:3002"));
486        assert_eq!(3002, port_from_bind_addr("[::]:3002"));
487        assert_eq!(
488            3002,
489            port_from_bind_addr("greptimedb-metasrv.default.svc.cluster.local:3002")
490        );
491        assert_eq!(
492            DEFAULT_GRPC_ADDR_PORT,
493            port_from_bind_addr("invalid-bind-addr")
494        );
495    }
496
497    #[test]
498    fn test_format_server_addr() {
499        assert_eq!(
500            "127.0.0.1:3002",
501            format_server_addr(IpAddr::V4(Ipv4Addr::LOCALHOST), 3002)
502        );
503        assert_eq!(
504            "[::1]:3002",
505            format_server_addr(IpAddr::V6(Ipv6Addr::LOCALHOST), 3002)
506        );
507    }
508
509    /// The returned server owns the shutdown sender and must outlive the requests.
510    async fn start_test_grpc_server(
511        cors_allowed_origins: Option<Vec<String>>,
512    ) -> (GrpcServer, SocketAddr) {
513        let runtime = Runtime::builder().build().unwrap();
514        let mut builder = GrpcServerBuilder::new(GrpcServerConfig::default(), runtime);
515        if let Some(origins) = cors_allowed_origins {
516            builder = builder.with_cors(origins);
517        }
518        let mut server = builder.build();
519        server
520            .start(SocketAddr::from((Ipv4Addr::LOCALHOST, 0)))
521            .await
522            .unwrap();
523        let addr = server.bind_addr().unwrap();
524        (server, addr)
525    }
526
527    const TEST_PATH: &str = "greptime.v1.HealthCheck/Check";
528
529    async fn send_preflight(addr: SocketAddr, origin: &str) -> reqwest::Response {
530        reqwest::Client::new()
531            .request(Method::OPTIONS, format!("http://{addr}/{TEST_PATH}"))
532            .header(ORIGIN, origin)
533            .header(ACCESS_CONTROL_REQUEST_METHOD, "POST")
534            .header(ACCESS_CONTROL_REQUEST_HEADERS, "content-type,x-grpc-web")
535            .send()
536            .await
537            .unwrap()
538    }
539
540    /// The path is not served, so the response is a trailers-only `Unimplemented`.
541    async fn send_grpc_web_call(addr: SocketAddr, origin: &str) -> reqwest::Response {
542        reqwest::Client::new()
543            .post(format!("http://{addr}/{TEST_PATH}"))
544            .header(ORIGIN, origin)
545            .header(CONTENT_TYPE, "application/grpc-web+proto")
546            .header("x-grpc-web", "1")
547            // Empty gRPC-Web frame: 1-byte compression flag + 4-byte length.
548            .body(vec![0u8; 5])
549            .send()
550            .await
551            .unwrap()
552    }
553
554    fn header(response: &reqwest::Response, name: HeaderName) -> Option<&str> {
555        response.headers().get(name).map(|v| v.to_str().unwrap())
556    }
557
558    #[tokio::test]
559    async fn test_grpc_web_cors_preflight() {
560        let (_server, addr) = start_test_grpc_server(Some(Vec::new())).await;
561
562        let response = send_preflight(addr, "https://example.com").await;
563
564        assert_eq!(response.status(), StatusCode::OK);
565        assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_ORIGIN), Some("*"));
566        assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_HEADERS), Some("*"));
567        // Lists the method of the actual call, not `OPTIONS`.
568        assert_eq!(
569            header(&response, ACCESS_CONTROL_ALLOW_METHODS),
570            Some("POST")
571        );
572    }
573
574    #[tokio::test]
575    async fn test_grpc_web_cors_expose_headers() {
576        let (_server, addr) = start_test_grpc_server(Some(Vec::new())).await;
577
578        let response = send_grpc_web_call(addr, "https://example.com").await;
579
580        assert_eq!(response.status(), StatusCode::OK);
581        assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_ORIGIN), Some("*"));
582        // Premise: the response is trailers-only.
583        assert_eq!(
584            header(&response, HeaderName::from_static("grpc-status")),
585            Some("12")
586        );
587        let expose_headers = header(&response, ACCESS_CONTROL_EXPOSE_HEADERS).unwrap();
588        for name in ["grpc-status", "grpc-message", "grpc-status-details-bin"] {
589            assert!(expose_headers.contains(name), "{expose_headers}");
590        }
591    }
592
593    #[tokio::test]
594    async fn test_grpc_web_cors_custom_origins() {
595        let (_server, addr) =
596            start_test_grpc_server(Some(vec!["https://example.com".to_string()])).await;
597
598        let response = send_preflight(addr, "https://example.com").await;
599        assert_eq!(response.status(), StatusCode::OK);
600        assert_eq!(
601            header(&response, ACCESS_CONTROL_ALLOW_ORIGIN),
602            Some("https://example.com")
603        );
604
605        // A disallowed origin still gets a 200, just without allow-origin; the
606        // browser does the blocking.
607        let response = send_preflight(addr, "https://notallowed.com").await;
608        assert_eq!(response.status(), StatusCode::OK);
609        assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_ORIGIN), None);
610    }
611
612    #[tokio::test]
613    async fn test_grpc_web_cors_disabled() {
614        let (_server, addr) = start_test_grpc_server(None).await;
615
616        let response = send_preflight(addr, "https://example.com").await;
617
618        // Without CORS, `GrpcWebLayer` rejects the preflight.
619        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
620        assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_ORIGIN), None);
621    }
622}