1pub 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 pub bind_addr: String,
71 pub server_addr: String,
73 pub max_recv_message_size: ReadableSize,
75 pub max_send_message_size: ReadableSize,
77 pub flight_compression: FlightCompression,
79 pub runtime_size: usize,
80 #[serde(default = "Default::default")]
81 pub tls: TlsOption,
82 #[serde(with = "humantime_serde")]
86 pub max_connection_age: Option<Duration>,
87 #[serde(with = "humantime_serde")]
89 pub http2_keep_alive_interval: Duration,
90 #[serde(with = "humantime_serde")]
92 pub http2_keep_alive_timeout: Duration,
93 pub enable_cors: bool,
96 pub cors_allowed_origins: Vec<String>,
98}
99
100impl GrpcOptions {
101 #[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 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 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 pub fn internal_default() -> Self {
177 Self {
178 bind_addr: format!("127.0.0.1:{}", DEFAULT_INTERNAL_GRPC_ADDR_PORT),
179 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 #[default]
210 None,
211 Transport,
213 ArrowIpc,
215 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
229type OtelArrowService = MemoryLimiterExtensionService<
231 InterceptedService<
232 ArrowMetricsServiceServer<OtelArrowServiceHandler<OpenTelemetryProtocolHandlerRef>>,
233 HeaderInterceptor,
234 >,
235>;
236
237pub struct GrpcServer {
238 shutdown_tx: Mutex<Option<Sender<()>>>,
240 serve_state: Mutex<Option<Receiver<Result<()>>>>,
243 routes: Mutex<Option<Routes>>,
245 tls_config: Option<ServerTlsConfig>,
247 otel_arrow_service: Mutex<Option<OtelArrowService>>,
249 bind_addr: Option<SocketAddr>,
250 name: Option<String>,
251 config: GrpcServerConfig,
252 cors_allowed_origins: Option<Vec<String>>,
254}
255
256#[derive(Debug, Clone)]
258pub struct GrpcServerConfig {
259 pub max_recv_message_size: usize,
261 pub max_send_message_size: usize,
263 pub tls: TlsOption,
264 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 .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 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 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 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 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 .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 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 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 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 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
620 assert_eq!(header(&response, ACCESS_CONTROL_ALLOW_ORIGIN), None);
621 }
622}