Skip to main content

servers/
http.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
15use std::collections::HashMap;
16use std::convert::Infallible;
17use std::fmt::Display;
18use std::net::SocketAddr;
19use std::sync::{Arc, Mutex as StdMutex};
20use std::time::Duration;
21
22use async_trait::async_trait;
23use auth::UserProviderRef;
24use axum::extract::{DefaultBodyLimit, Request};
25use axum::http::StatusCode as HttpStatusCode;
26use axum::middleware::Next;
27use axum::response::{IntoResponse, Response};
28use axum::routing::Route;
29use axum::serve::ListenerExt;
30use axum::{Router, middleware, routing};
31use common_base::readable_size::ReadableSize;
32use common_recordbatch::RecordBatch;
33use common_telemetry::{error, info};
34use common_time::Timestamp;
35use common_time::timestamp::TimeUnit;
36use datatypes::data_type::DataType;
37use datatypes::schema::SchemaRef;
38use event::{LogState, LogValidatorRef};
39use futures::FutureExt;
40use http::{HeaderValue, Method};
41use serde::{Deserialize, Serialize};
42use serde_json::Value;
43use snafu::{ResultExt, ensure};
44use tokio::sync::Mutex;
45use tokio::sync::oneshot::{self, Sender};
46use tonic::codegen::Service;
47use tower::{Layer, ServiceBuilder};
48use tower_http::compression::CompressionLayer;
49use tower_http::cors::{AllowOrigin, Any, CorsLayer};
50use tower_http::decompression::RequestDecompressionLayer;
51use tower_http::trace::TraceLayer;
52
53use self::authorize::AuthState;
54use self::result::table_result::TableResponse;
55use crate::elasticsearch;
56use crate::error::{
57    AddressBindSnafu, AlreadyStartedSnafu, Error, InternalIoSnafu, InvalidHeaderValueSnafu, Result,
58};
59use crate::http::influxdb::{influxdb_health, influxdb_ping, influxdb_write_v1, influxdb_write_v2};
60use crate::http::otlp::OtlpState;
61use crate::http::prom_store::PromStoreState;
62use crate::http::prometheus::{
63    build_info_query, format_query, instant_query, label_values_query, labels_query,
64    metadata_query, parse_query, range_query, series_query,
65};
66use crate::http::result::arrow_result::ArrowResponse;
67use crate::http::result::csv_result::CsvResponse;
68use crate::http::result::error_result::ErrorResponse;
69use crate::http::result::greptime_result_v1::GreptimedbV1Response;
70use crate::http::result::influxdb_result_v1::InfluxdbV1Response;
71use crate::http::result::json_result::JsonResponse;
72use crate::http::result::null_result::NullResponse;
73use crate::interceptor::LogIngestInterceptorRef;
74use crate::metrics::http_metrics_layer;
75use crate::metrics_handler::MetricsHandler;
76use crate::pending_rows_batcher::PendingRowsBatcher;
77use crate::prometheus_handler::PrometheusHandlerRef;
78use crate::query_handler::sql::ServerSqlQueryHandlerRef;
79use crate::query_handler::{
80    DashboardHandlerRef, InfluxdbLineProtocolHandlerRef, JaegerQueryHandlerRef, LogQueryHandlerRef,
81    OpenTelemetryProtocolHandlerRef, OpentsdbProtocolHandlerRef, PipelineHandlerRef,
82    PromStoreProtocolHandlerRef,
83};
84use crate::request_memory_limiter::ServerMemoryLimiter;
85use crate::server::Server;
86
87pub mod authorize;
88#[cfg(feature = "dashboard")]
89mod dashboard;
90pub mod dyn_log;
91pub mod dyn_trace;
92pub mod event;
93pub mod extractor;
94pub mod handler;
95pub mod header;
96pub mod influxdb;
97pub mod jaeger;
98pub mod logs;
99pub mod loki;
100pub mod mem_prof;
101mod memory_limit;
102pub mod opentsdb;
103pub mod otlp;
104pub mod pprof;
105pub mod prom_store;
106pub mod prometheus;
107pub mod result;
108pub mod splunk;
109mod timeout;
110pub mod utils;
111mod workload_scheduler;
112
113use result::HttpOutputWriter;
114pub(crate) use timeout::DynamicTimeoutLayer;
115
116mod client_ip;
117use crate::prom_remote_write::validation::PromValidationMode;
118mod hints;
119mod read_preference;
120#[cfg(any(test, feature = "testing"))]
121pub mod test_helpers;
122
123pub const HTTP_API_VERSION: &str = "v1";
124pub const HTTP_API_PREFIX: &str = "/v1/";
125pub const HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH: &str = "/v1";
126
127/// Provides an extra router to merge into an HTTP server.
128pub trait ExtraHttpRouterProvider: Send + Sync {
129    /// Returns the extra HTTP router.
130    fn router(&self) -> Router;
131}
132
133pub type ExtraHttpRouterProviderRef = Arc<dyn ExtraHttpRouterProvider>;
134
135/// Collection of extra HTTP router providers.
136#[derive(Clone, Default)]
137pub struct ExtraHttpRouterProviders {
138    providers: Vec<ExtraHttpRouterProviderRef>,
139}
140
141impl ExtraHttpRouterProviders {
142    /// Creates an empty provider collection.
143    pub fn new() -> Self {
144        Self::default()
145    }
146
147    /// Adds an extra HTTP router provider.
148    pub fn add(&mut self, provider: ExtraHttpRouterProviderRef) {
149        self.providers.push(provider);
150    }
151
152    /// Returns all registered providers.
153    pub fn iter(&self) -> impl Iterator<Item = &dyn ExtraHttpRouterProvider> {
154        self.providers.iter().map(|x| x.as_ref())
155    }
156}
157
158/// Default http body limit (64M).
159const DEFAULT_BODY_LIMIT: ReadableSize = ReadableSize::mb(64);
160/// Default address port for the public HTTP API server.
161const DEFAULT_HTTP_API_ADDR_PORT: u16 = 4006;
162
163/// Authorization header
164pub const AUTHORIZATION_HEADER: &str = "x-greptime-auth";
165
166// TODO(fys): This is a temporary workaround, it will be improved later
167// these APIs will bypass authentication.
168pub static PUBLIC_API_PREFIX: [&str; 4] = [
169    "/v1/influxdb/ping",
170    "/v1/influxdb/health",
171    "/v1/health",
172    "/v1/splunk/services/collector/health",
173];
174
175/// Listener mode for an [`HttpServer`].
176///
177/// Both modes share the *same* complete route registry; the [`HttpServerKind::Api`]
178/// listener additionally installs an outer guard that only exposes the `/v1` APIs
179/// and the dashboard, returning `404` for every other path.
180#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
181pub(crate) enum HttpServerKind {
182    /// Serves the complete historical HTTP surface (both `v1` APIs and internal
183    /// interfaces such as health, status, metrics, config and debug).
184    #[default]
185    Full,
186    /// Serves only the `v1` interfaces plus the dashboard.
187    Api,
188}
189
190#[derive(Default)]
191pub struct HttpServer {
192    router: StdMutex<Router>,
193    shutdown_tx: Mutex<Option<Sender<()>>>,
194    user_provider: Option<UserProviderRef>,
195    memory_limiter: ServerMemoryLimiter,
196
197    // server configs
198    options: HttpOptions,
199    bind_addr: Option<SocketAddr>,
200    /// What this server instance exposes. See [`HttpServerKind`].
201    kind: HttpServerKind,
202}
203
204/// Returns `true` when `path` equals `root` or is nested directly under it
205/// (`root/...`). Uses an exact slash boundary so `/v1` does **not** match `/v10`
206/// or `/v1-internal`.
207pub(crate) fn is_namespace(path: &str, root: &str) -> bool {
208    path == root
209        || path
210            .strip_prefix(root)
211            .is_some_and(|suffix| suffix.starts_with('/'))
212}
213
214/// Paths visible on the dedicated API listener: the `/v1` APIs and the dashboard.
215pub fn is_api_listener_path(path: &str) -> bool {
216    is_namespace(path, HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH) || is_namespace(path, "/dashboard")
217}
218
219/// Outer guard for the API listener. Rejects any path outside the API surface
220/// with `404 Not Found` before it can reach a handler (and before auth side
221/// effects). Reachability is kept separate from authentication.
222async fn enforce_api_surface(req: Request, next: Next) -> Response {
223    if !is_api_listener_path(req.uri().path()) {
224        return HttpStatusCode::NOT_FOUND.into_response();
225    }
226    next.run(req).await
227}
228
229impl HttpServer {
230    /// A short human-readable label for this server instance, used in logs.
231    fn kind(&self) -> &'static str {
232        match self.kind {
233            HttpServerKind::Api => "HTTP API",
234            HttpServerKind::Full => "HTTP",
235        }
236    }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(default)]
241pub struct HttpOptions {
242    pub addr: String,
243
244    #[serde(with = "humantime_serde")]
245    pub timeout: Duration,
246
247    #[serde(skip)]
248    pub disable_dashboard: bool,
249
250    pub body_limit: ReadableSize,
251
252    pub cors_allowed_origins: Vec<String>,
253
254    pub enable_cors: bool,
255
256    /// Whether to start the dedicated public HTTP **API** server, which serves
257    /// only the `v1` interfaces plus the dashboard. It shares every other
258    /// `[http]` option with the main server and only differs by its bound
259    /// address (see `api_server_addr`).
260    pub enable_api_server: bool,
261    /// The address to bind the dedicated HTTP API server, in the same form as
262    /// `addr` (e.g. `127.0.0.1:4006`).
263    pub api_server_addr: String,
264}
265
266impl Default for HttpOptions {
267    fn default() -> Self {
268        Self {
269            addr: "127.0.0.1:4000".to_string(),
270            timeout: Duration::from_secs(0),
271            disable_dashboard: false,
272            body_limit: DEFAULT_BODY_LIMIT,
273            cors_allowed_origins: Vec::new(),
274            enable_cors: true,
275            enable_api_server: false,
276            api_server_addr: format!("127.0.0.1:{}", DEFAULT_HTTP_API_ADDR_PORT),
277        }
278    }
279}
280
281#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
282pub struct ColumnSchema {
283    name: String,
284    data_type: String,
285}
286
287impl ColumnSchema {
288    pub fn new(name: String, data_type: String) -> ColumnSchema {
289        ColumnSchema { name, data_type }
290    }
291}
292
293#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
294pub struct OutputSchema {
295    column_schemas: Vec<ColumnSchema>,
296}
297
298impl OutputSchema {
299    pub fn new(columns: Vec<ColumnSchema>) -> OutputSchema {
300        OutputSchema {
301            column_schemas: columns,
302        }
303    }
304}
305
306impl From<SchemaRef> for OutputSchema {
307    fn from(schema: SchemaRef) -> OutputSchema {
308        OutputSchema {
309            column_schemas: schema
310                .column_schemas()
311                .iter()
312                .map(|cs| ColumnSchema {
313                    name: cs.name.clone(),
314                    data_type: cs.data_type.name(),
315                })
316                .collect(),
317        }
318    }
319}
320
321#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
322pub struct HttpRecordsOutput {
323    schema: OutputSchema,
324    rows: Vec<Vec<Value>>,
325    // total_rows is equal to rows.len() in most cases,
326    // the Dashboard query result may be truncated, so we need to return the total_rows.
327    #[serde(default)]
328    total_rows: usize,
329
330    // plan level execution metrics
331    #[serde(skip_serializing_if = "HashMap::is_empty")]
332    #[serde(default)]
333    metrics: HashMap<String, Value>,
334}
335
336impl HttpRecordsOutput {
337    pub fn num_rows(&self) -> usize {
338        self.rows.len()
339    }
340
341    pub fn num_cols(&self) -> usize {
342        self.schema.column_schemas.len()
343    }
344
345    pub fn schema(&self) -> &OutputSchema {
346        &self.schema
347    }
348
349    pub fn rows(&self) -> &Vec<Vec<Value>> {
350        &self.rows
351    }
352}
353
354impl HttpRecordsOutput {
355    pub fn try_new(
356        schema: SchemaRef,
357        recordbatches: Vec<RecordBatch>,
358    ) -> std::result::Result<HttpRecordsOutput, Error> {
359        if recordbatches.is_empty() {
360            Ok(HttpRecordsOutput {
361                schema: OutputSchema::from(schema),
362                rows: vec![],
363                total_rows: 0,
364                metrics: Default::default(),
365            })
366        } else {
367            let num_rows = recordbatches.iter().map(|r| r.num_rows()).sum::<usize>();
368            let mut rows = Vec::with_capacity(num_rows);
369
370            for recordbatch in recordbatches {
371                let mut writer = HttpOutputWriter::new(schema.num_columns(), None);
372                writer.write(recordbatch, &mut rows)?;
373            }
374
375            Ok(HttpRecordsOutput {
376                schema: OutputSchema::from(schema),
377                total_rows: rows.len(),
378                rows,
379                metrics: Default::default(),
380            })
381        }
382    }
383}
384
385#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
386#[serde(rename_all = "lowercase")]
387pub enum GreptimeQueryOutput {
388    AffectedRows(usize),
389    Records(HttpRecordsOutput),
390}
391
392/// It allows the results of SQL queries to be presented in different formats.
393#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
394pub enum ResponseFormat {
395    Arrow,
396    // (with_names, with_types)
397    Csv(bool, bool),
398    Table,
399    #[default]
400    GreptimedbV1,
401    InfluxdbV1,
402    Json,
403    Null,
404}
405
406impl ResponseFormat {
407    pub fn parse(s: &str) -> Option<Self> {
408        match s {
409            "arrow" => Some(ResponseFormat::Arrow),
410            "csv" => Some(ResponseFormat::Csv(false, false)),
411            "csvwithnames" => Some(ResponseFormat::Csv(true, false)),
412            "csvwithnamesandtypes" => Some(ResponseFormat::Csv(true, true)),
413            "table" => Some(ResponseFormat::Table),
414            "greptimedb_v1" => Some(ResponseFormat::GreptimedbV1),
415            "influxdb_v1" => Some(ResponseFormat::InfluxdbV1),
416            "json" => Some(ResponseFormat::Json),
417            "null" => Some(ResponseFormat::Null),
418            _ => None,
419        }
420    }
421
422    pub fn as_str(&self) -> &'static str {
423        match self {
424            ResponseFormat::Arrow => "arrow",
425            ResponseFormat::Csv(_, _) => "csv",
426            ResponseFormat::Table => "table",
427            ResponseFormat::GreptimedbV1 => "greptimedb_v1",
428            ResponseFormat::InfluxdbV1 => "influxdb_v1",
429            ResponseFormat::Json => "json",
430            ResponseFormat::Null => "null",
431        }
432    }
433}
434
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436pub enum Epoch {
437    Nanosecond,
438    Microsecond,
439    Millisecond,
440    Second,
441}
442
443impl Epoch {
444    pub fn parse(s: &str) -> Option<Epoch> {
445        // Both u and µ indicate microseconds.
446        // epoch = [ns,u,µ,ms,s],
447        // For details, see the Influxdb documents.
448        // https://docs.influxdata.com/influxdb/v1/tools/api/#query-string-parameters-1
449        match s {
450            "ns" => Some(Epoch::Nanosecond),
451            "u" | "µ" => Some(Epoch::Microsecond),
452            "ms" => Some(Epoch::Millisecond),
453            "s" => Some(Epoch::Second),
454            _ => None, // just returns None for other cases
455        }
456    }
457
458    pub fn convert_timestamp(&self, ts: Timestamp) -> Option<Timestamp> {
459        match self {
460            Epoch::Nanosecond => ts.convert_to(TimeUnit::Nanosecond),
461            Epoch::Microsecond => ts.convert_to(TimeUnit::Microsecond),
462            Epoch::Millisecond => ts.convert_to(TimeUnit::Millisecond),
463            Epoch::Second => ts.convert_to(TimeUnit::Second),
464        }
465    }
466}
467
468impl Display for Epoch {
469    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470        match self {
471            Epoch::Nanosecond => write!(f, "Epoch::Nanosecond"),
472            Epoch::Microsecond => write!(f, "Epoch::Microsecond"),
473            Epoch::Millisecond => write!(f, "Epoch::Millisecond"),
474            Epoch::Second => write!(f, "Epoch::Second"),
475        }
476    }
477}
478
479#[derive(Serialize, Deserialize, Debug)]
480pub enum HttpResponse {
481    Arrow(ArrowResponse),
482    Csv(CsvResponse),
483    Table(TableResponse),
484    Error(ErrorResponse),
485    GreptimedbV1(GreptimedbV1Response),
486    InfluxdbV1(InfluxdbV1Response),
487    Json(JsonResponse),
488    Null(NullResponse),
489}
490
491impl HttpResponse {
492    pub fn with_execution_time(self, execution_time: u64) -> Self {
493        match self {
494            HttpResponse::Arrow(resp) => resp.with_execution_time(execution_time).into(),
495            HttpResponse::Csv(resp) => resp.with_execution_time(execution_time).into(),
496            HttpResponse::Table(resp) => resp.with_execution_time(execution_time).into(),
497            HttpResponse::GreptimedbV1(resp) => resp.with_execution_time(execution_time).into(),
498            HttpResponse::InfluxdbV1(resp) => resp.with_execution_time(execution_time).into(),
499            HttpResponse::Json(resp) => resp.with_execution_time(execution_time).into(),
500            HttpResponse::Null(resp) => resp.with_execution_time(execution_time).into(),
501            HttpResponse::Error(resp) => resp.with_execution_time(execution_time).into(),
502        }
503    }
504
505    pub fn with_limit(self, limit: usize) -> Self {
506        match self {
507            HttpResponse::Csv(resp) => resp.with_limit(limit).into(),
508            HttpResponse::Table(resp) => resp.with_limit(limit).into(),
509            HttpResponse::GreptimedbV1(resp) => resp.with_limit(limit).into(),
510            HttpResponse::Json(resp) => resp.with_limit(limit).into(),
511            _ => self,
512        }
513    }
514}
515
516pub fn process_with_limit(
517    mut outputs: Vec<GreptimeQueryOutput>,
518    limit: usize,
519) -> Vec<GreptimeQueryOutput> {
520    outputs
521        .drain(..)
522        .map(|data| match data {
523            GreptimeQueryOutput::Records(mut records) => {
524                if records.rows.len() > limit {
525                    records.rows.truncate(limit);
526                    records.total_rows = limit;
527                }
528                GreptimeQueryOutput::Records(records)
529            }
530            _ => data,
531        })
532        .collect()
533}
534
535impl IntoResponse for HttpResponse {
536    fn into_response(self) -> Response {
537        match self {
538            HttpResponse::Arrow(resp) => resp.into_response(),
539            HttpResponse::Csv(resp) => resp.into_response(),
540            HttpResponse::Table(resp) => resp.into_response(),
541            HttpResponse::GreptimedbV1(resp) => resp.into_response(),
542            HttpResponse::InfluxdbV1(resp) => resp.into_response(),
543            HttpResponse::Json(resp) => resp.into_response(),
544            HttpResponse::Null(resp) => resp.into_response(),
545            HttpResponse::Error(resp) => resp.into_response(),
546        }
547    }
548}
549
550impl From<ArrowResponse> for HttpResponse {
551    fn from(value: ArrowResponse) -> Self {
552        HttpResponse::Arrow(value)
553    }
554}
555
556impl From<CsvResponse> for HttpResponse {
557    fn from(value: CsvResponse) -> Self {
558        HttpResponse::Csv(value)
559    }
560}
561
562impl From<TableResponse> for HttpResponse {
563    fn from(value: TableResponse) -> Self {
564        HttpResponse::Table(value)
565    }
566}
567
568impl From<ErrorResponse> for HttpResponse {
569    fn from(value: ErrorResponse) -> Self {
570        HttpResponse::Error(value)
571    }
572}
573
574impl From<GreptimedbV1Response> for HttpResponse {
575    fn from(value: GreptimedbV1Response) -> Self {
576        HttpResponse::GreptimedbV1(value)
577    }
578}
579
580impl From<InfluxdbV1Response> for HttpResponse {
581    fn from(value: InfluxdbV1Response) -> Self {
582        HttpResponse::InfluxdbV1(value)
583    }
584}
585
586impl From<JsonResponse> for HttpResponse {
587    fn from(value: JsonResponse) -> Self {
588        HttpResponse::Json(value)
589    }
590}
591
592impl From<NullResponse> for HttpResponse {
593    fn from(value: NullResponse) -> Self {
594        HttpResponse::Null(value)
595    }
596}
597
598#[derive(Clone)]
599pub struct ApiState {
600    pub sql_handler: ServerSqlQueryHandlerRef,
601}
602
603#[derive(Clone)]
604pub struct GreptimeOptionsConfigState {
605    pub greptime_config_options: String,
606}
607
608#[derive(Clone)]
609pub struct DashboardState {
610    pub handler: DashboardHandlerRef,
611}
612
613pub struct HttpServerBuilder {
614    options: HttpOptions,
615    user_provider: Option<UserProviderRef>,
616    router: Router,
617    memory_limiter: ServerMemoryLimiter,
618}
619
620impl HttpServerBuilder {
621    pub fn new(options: HttpOptions) -> Self {
622        Self {
623            options,
624            user_provider: None,
625            router: Router::new(),
626            memory_limiter: ServerMemoryLimiter::default(),
627        }
628    }
629
630    /// Set a global memory limiter for all server protocols.
631    pub fn with_memory_limiter(mut self, limiter: ServerMemoryLimiter) -> Self {
632        self.memory_limiter = limiter;
633        self
634    }
635
636    pub fn with_sql_handler(self, sql_handler: ServerSqlQueryHandlerRef) -> Self {
637        let sql_router = HttpServer::route_sql(ApiState { sql_handler });
638
639        Self {
640            router: self
641                .router
642                .nest(&format!("/{HTTP_API_VERSION}"), sql_router),
643            ..self
644        }
645    }
646
647    pub fn with_logs_handler(self, logs_handler: LogQueryHandlerRef) -> Self {
648        let logs_router = HttpServer::route_logs(logs_handler);
649
650        Self {
651            router: self
652                .router
653                .nest(&format!("/{HTTP_API_VERSION}"), logs_router),
654            ..self
655        }
656    }
657
658    pub fn with_opentsdb_handler(self, handler: OpentsdbProtocolHandlerRef) -> Self {
659        Self {
660            router: self.router.nest(
661                &format!("/{HTTP_API_VERSION}/opentsdb"),
662                HttpServer::route_opentsdb(handler),
663            ),
664            ..self
665        }
666    }
667
668    pub fn with_influxdb_handler(self, handler: InfluxdbLineProtocolHandlerRef) -> Self {
669        Self {
670            router: self.router.nest(
671                &format!("/{HTTP_API_VERSION}/influxdb"),
672                HttpServer::route_influxdb(handler),
673            ),
674            ..self
675        }
676    }
677
678    pub fn with_prom_handler(
679        self,
680        handler: PromStoreProtocolHandlerRef,
681        pipeline_handler: Option<PipelineHandlerRef>,
682        prom_store_with_metric_engine: bool,
683        prom_validation_mode: PromValidationMode,
684        experimental_enable_prometheus_native_histogram: bool,
685        pending_rows_batcher: Option<Arc<PendingRowsBatcher>>,
686    ) -> Self {
687        let state = PromStoreState {
688            prom_store_handler: handler,
689            pipeline_handler,
690            prom_store_with_metric_engine,
691            prom_validation_mode,
692            experimental_enable_prometheus_native_histogram,
693            pending_rows_batcher,
694        };
695
696        Self {
697            router: self.router.nest(
698                &format!("/{HTTP_API_VERSION}/prometheus"),
699                HttpServer::route_prom(state),
700            ),
701            ..self
702        }
703    }
704
705    pub fn with_prometheus_handler(self, handler: PrometheusHandlerRef) -> Self {
706        Self {
707            router: self.router.nest(
708                &format!("/{HTTP_API_VERSION}/prometheus/api/v1"),
709                HttpServer::route_prometheus(handler),
710            ),
711            ..self
712        }
713    }
714
715    pub fn with_otlp_handler(
716        self,
717        handler: OpenTelemetryProtocolHandlerRef,
718        with_metric_engine: bool,
719        experimental_enable_exponential_histogram: bool,
720    ) -> Self {
721        Self {
722            router: self.router.nest(
723                &format!("/{HTTP_API_VERSION}/otlp"),
724                HttpServer::route_otlp(
725                    handler,
726                    with_metric_engine,
727                    experimental_enable_exponential_histogram,
728                ),
729            ),
730            ..self
731        }
732    }
733
734    pub fn with_user_provider(self, user_provider: UserProviderRef) -> Self {
735        Self {
736            user_provider: Some(user_provider),
737            ..self
738        }
739    }
740
741    pub fn with_metrics_handler(self, handler: MetricsHandler) -> Self {
742        Self {
743            router: self.router.merge(HttpServer::route_metrics(handler)),
744            ..self
745        }
746    }
747
748    pub fn with_log_ingest_handler(
749        self,
750        handler: PipelineHandlerRef,
751        validator: Option<LogValidatorRef>,
752        ingest_interceptor: Option<LogIngestInterceptorRef<Error>>,
753    ) -> Self {
754        let log_state = LogState {
755            log_handler: handler,
756            log_validator: validator,
757            ingest_interceptor,
758        };
759
760        let router = self.router.nest(
761            &format!("/{HTTP_API_VERSION}"),
762            HttpServer::route_pipelines(log_state.clone()),
763        );
764        // deprecated since v0.11.0. Use `/logs` and `/pipelines` instead.
765        let router = router.nest(
766            &format!("/{HTTP_API_VERSION}/events"),
767            #[allow(deprecated)]
768            HttpServer::route_log_deprecated(log_state.clone()),
769        );
770
771        let router = router.nest(
772            &format!("/{HTTP_API_VERSION}/loki"),
773            HttpServer::route_loki(log_state.clone()),
774        );
775
776        let router = router.nest(
777            &format!("/{HTTP_API_VERSION}/elasticsearch"),
778            HttpServer::route_elasticsearch(log_state.clone()),
779        );
780
781        let router = router.nest(
782            &format!("/{HTTP_API_VERSION}/elasticsearch/"),
783            Router::new()
784                .route("/", routing::get(elasticsearch::handle_get_version))
785                .with_state(log_state.clone()),
786        );
787
788        let router = router.nest(
789            &format!("/{HTTP_API_VERSION}/splunk"),
790            HttpServer::route_splunk(log_state),
791        );
792
793        Self { router, ..self }
794    }
795
796    pub fn with_greptime_config_options(self, opts: String) -> Self {
797        let config_router = HttpServer::route_config(GreptimeOptionsConfigState {
798            greptime_config_options: opts,
799        });
800
801        Self {
802            router: self.router.merge(config_router),
803            ..self
804        }
805    }
806
807    pub fn with_jaeger_handler(self, handler: JaegerQueryHandlerRef) -> Self {
808        Self {
809            router: self.router.nest(
810                &format!("/{HTTP_API_VERSION}/jaeger"),
811                HttpServer::route_jaeger(handler),
812            ),
813            ..self
814        }
815    }
816
817    pub fn with_dashboard_handler(self, handler: DashboardHandlerRef) -> Self {
818        Self {
819            router: self.router.nest(
820                &format!("/{HTTP_API_VERSION}/dashboards"),
821                HttpServer::route_dashboard(handler),
822            ),
823            ..self
824        }
825    }
826
827    pub fn with_extra_router(self, router: Router) -> Self {
828        Self {
829            router: self.router.merge(router),
830            ..self
831        }
832    }
833
834    pub fn add_layer<L>(self, layer: L) -> Self
835    where
836        L: Layer<Route> + Clone + Send + Sync + 'static,
837        L::Service: Service<Request> + Clone + Send + Sync + 'static,
838        <L::Service as Service<Request>>::Response: IntoResponse + 'static,
839        <L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
840        <L::Service as Service<Request>>::Future: Send + 'static,
841    {
842        Self {
843            router: self.router.layer(layer),
844            ..self
845        }
846    }
847
848    pub fn build(self) -> HttpServer {
849        HttpServer {
850            options: self.options,
851            user_provider: self.user_provider,
852            shutdown_tx: Mutex::new(None),
853            router: StdMutex::new(self.router),
854            bind_addr: None,
855            memory_limiter: self.memory_limiter,
856            kind: HttpServerKind::Full,
857        }
858    }
859
860    /// Builds the full HTTP server (serves the `v1` interfaces **and** the
861    /// internal interfaces such as health, status, metrics, config and debug).
862    /// When [`HttpOptions::enable_api_server`] is `true`, it also builds a
863    /// dedicated HTTP **API** server that serves only the `v1` interfaces plus
864    /// the dashboard.
865    ///
866    /// Both servers are built from the **same** complete route registry (so every
867    /// built-in, plugin and downstream route registered via `with_extra_router()`
868    /// is present on both). The API server additionally installs an outer guard
869    /// that exposes only the `/v1` and `/dashboard` namespaces, returning `404`
870    /// for everything else. This means downstream projects (e.g. enterprise) do
871    /// not need to classify their routes as public/internal: non-`/v1`
872    /// operational routes are hidden from the API listener automatically.
873    ///
874    /// The first returned server is bound to `self.options.addr` (e.g. port
875    /// `4000`) and keeps the historical single-server behavior. The optional
876    /// second server is bound to `api_server_addr` (e.g. `127.0.0.1:4006`). The
877    /// API server inherits **every** option from `[http]` except the bound address.
878    pub fn build_servers(self) -> (HttpServer, Option<HttpServer>) {
879        let api_enabled = self.options.enable_api_server;
880        let api_addr = self.options.api_server_addr.clone();
881
882        let internal = HttpServer {
883            options: self.options,
884            user_provider: self.user_provider.clone(),
885            shutdown_tx: Mutex::new(None),
886            router: StdMutex::new(self.router.clone()),
887            bind_addr: None,
888            memory_limiter: self.memory_limiter.clone(),
889            kind: HttpServerKind::Full,
890        };
891
892        let api = if api_enabled {
893            // The API server shares every option with the full server except the
894            // bound address.
895            let api_options = HttpOptions {
896                addr: api_addr,
897                ..internal.options.clone()
898            };
899            Some(HttpServer {
900                options: api_options,
901                user_provider: self.user_provider.clone(),
902                shutdown_tx: Mutex::new(None),
903                router: StdMutex::new(self.router),
904                bind_addr: None,
905                memory_limiter: self.memory_limiter,
906                kind: HttpServerKind::Api,
907            })
908        } else {
909            None
910        };
911
912        (internal, api)
913    }
914}
915
916impl HttpServer {
917    /// Builds the complete route registry shared by every HTTP server: all the
918    /// `v1` protocol routes, the root/health/status/metrics/config interfaces,
919    /// the dashboard and (via `build()`) the debug routes.
920    ///
921    /// Both [`HttpServerKind::Full`] and [`HttpServerKind::Api`] listeners are
922    /// built from this same router. The API listener hides the non-`v1` paths
923    /// later, in [`HttpServer::build`], via an outer guard. Keeping one router
924    /// means downstream routes added through `with_extra_router()` are present
925    /// on both listeners automatically, and the API guard decides exposure.
926    pub fn make_app(&self) -> Router {
927        let mut router = self.router.lock().unwrap().clone();
928
929        router = router
930            .route(
931                &format!("/{HTTP_API_VERSION}/health"),
932                routing::get(handler::health).post(handler::health),
933            )
934            .route("/", routing::get(handler::index))
935            .route(
936                "/health",
937                routing::get(handler::health).post(handler::health),
938            )
939            .route(
940                "/ready",
941                routing::get(handler::health).post(handler::health),
942            )
943            .route("/status", routing::get(handler::status));
944
945        // The dashboard runs on top of the `v1` APIs, so it is exposed on every
946        // HTTP server (the API listener's namespace guard allows `/dashboard`).
947        #[cfg(feature = "dashboard")]
948        {
949            if !self.options.disable_dashboard {
950                info!("Enable dashboard service at '/dashboard'");
951                // redirect /dashboard to /dashboard/
952                router = router.route(
953                    "/dashboard",
954                    routing::get(|uri: axum::http::uri::Uri| async move {
955                        let path = uri.path();
956                        let query = uri.query().map(|q| format!("?{}", q)).unwrap_or_default();
957
958                        let new_uri = format!("{}/{}", path, query);
959                        axum::response::Redirect::permanent(&new_uri)
960                    }),
961                );
962
963                // "/dashboard" and "/dashboard/" are two different paths in Axum.
964                // We cannot nest "/dashboard/", because we already mapping "/dashboard/{*x}" while nesting "/dashboard".
965                // So we explicitly route "/dashboard/" here.
966                router = router
967                    .route(
968                        "/dashboard/",
969                        routing::get(dashboard::static_handler).post(dashboard::static_handler),
970                    )
971                    .route(
972                        "/dashboard/{*x}",
973                        routing::get(dashboard::static_handler).post(dashboard::static_handler),
974                    );
975            }
976        }
977
978        // Add a layer to collect HTTP metrics for axum.
979        router = router.route_layer(middleware::from_fn(http_metrics_layer));
980
981        router
982    }
983
984    /// Attaches middlewares and debug routes to the router.
985    /// Callers should call this method after [HttpServer::make_app()].
986    pub fn build(&self, router: Router) -> Result<Router> {
987        let timeout_layer = if self.options.timeout != Duration::default() {
988            Some(
989                ServiceBuilder::new().layer(
990                    DynamicTimeoutLayer::new(self.options.timeout)
991                        .with_status_code_fn(Self::request_timeout_status_code),
992                ),
993            )
994        } else {
995            info!("HTTP server timeout is disabled");
996            None
997        };
998        let body_limit_layer = if self.options.body_limit != ReadableSize(0) {
999            Some(
1000                ServiceBuilder::new()
1001                    .layer(DefaultBodyLimit::max(self.options.body_limit.0 as usize)),
1002            )
1003        } else {
1004            info!("HTTP server body limit is disabled");
1005            None
1006        };
1007        let cors_layer = if self.options.enable_cors {
1008            Some(
1009                CorsLayer::new()
1010                    .allow_methods([
1011                        Method::GET,
1012                        Method::POST,
1013                        Method::PUT,
1014                        Method::DELETE,
1015                        Method::HEAD,
1016                    ])
1017                    .allow_origin(if self.options.cors_allowed_origins.is_empty() {
1018                        AllowOrigin::from(Any)
1019                    } else {
1020                        AllowOrigin::from(
1021                            self.options
1022                                .cors_allowed_origins
1023                                .iter()
1024                                .map(|s| {
1025                                    HeaderValue::from_str(s.as_str())
1026                                        .context(InvalidHeaderValueSnafu)
1027                                })
1028                                .collect::<Result<Vec<HeaderValue>>>()?,
1029                        )
1030                    })
1031                    .allow_headers(Any),
1032            )
1033        } else {
1034            info!("HTTP server cross-origin is disabled");
1035            None
1036        };
1037
1038        let router = router
1039            // middlewares
1040            .layer(
1041                ServiceBuilder::new()
1042                    // disable on failure tracing. because printing out isn't very helpful,
1043                    // and we have impl IntoResponse for Error. It will print out more detailed error messages
1044                    .layer(TraceLayer::new_for_http().on_failure(()))
1045                    .option_layer(cors_layer)
1046                    .option_layer(timeout_layer)
1047                    .option_layer(body_limit_layer)
1048                    // memory limit layer - must be before body is consumed
1049                    .layer(middleware::from_fn_with_state(
1050                        self.memory_limiter.clone(),
1051                        memory_limit::memory_limit_middleware,
1052                    ))
1053                    // auth layer
1054                    .layer(middleware::from_fn_with_state(
1055                        AuthState::new(self.user_provider.clone()),
1056                        authorize::check_http_auth,
1057                    ))
1058                    .layer(middleware::from_fn(hints::extract_hints))
1059                    .layer(middleware::from_fn(client_ip::log_error_with_client_ip))
1060                    .layer(middleware::from_fn(
1061                        read_preference::extract_read_preference,
1062                    )),
1063            );
1064
1065        // Debug handlers are part of the complete router; the API listener hides
1066        // them through the outer namespace guard applied below.
1067        let router = router.nest(
1068            "/debug",
1069            Router::new()
1070                // handler for changing log level dynamically
1071                .route("/log_level", routing::post(dyn_log::dyn_log_handler))
1072                .route(
1073                    "/workload_scheduler",
1074                    routing::get(workload_scheduler::get_status_handler),
1075                )
1076                .route(
1077                    "/workload_scheduler/enabled",
1078                    routing::post(workload_scheduler::set_enabled_handler),
1079                )
1080                .route(
1081                    "/workload_scheduler/weights",
1082                    routing::post(workload_scheduler::set_weights_handler),
1083                )
1084                .route("/enable_trace", routing::post(dyn_trace::dyn_trace_handler))
1085                .nest(
1086                    "/prof",
1087                    Router::new()
1088                        .route("/cpu", routing::post(pprof::pprof_handler))
1089                        .route("/mem", routing::post(mem_prof::mem_prof_handler))
1090                        .route("/mem/symbol", routing::post(mem_prof::symbolicate_handler))
1091                        .route(
1092                            "/mem/activate",
1093                            routing::post(mem_prof::activate_heap_prof_handler),
1094                        )
1095                        .route(
1096                            "/mem/deactivate",
1097                            routing::post(mem_prof::deactivate_heap_prof_handler),
1098                        )
1099                        .route(
1100                            "/mem/status",
1101                            routing::get(mem_prof::heap_prof_status_handler),
1102                        ) // jemalloc gdump flag status and toggle
1103                        .route(
1104                            "/mem/gdump",
1105                            routing::get(mem_prof::gdump_status_handler)
1106                                .post(mem_prof::gdump_toggle_handler),
1107                        ),
1108                ),
1109        );
1110
1111        // Seal the API listener last: this is the outermost layer, applied after
1112        // all middleware, debug routes and any caller/plugin routes, so that only
1113        // `/v1` and `/dashboard` paths can reach a handler. Excluded paths return
1114        // 404 before auth or any other side effect.
1115        if self.kind == HttpServerKind::Api {
1116            Ok(router.layer(middleware::from_fn(enforce_api_surface)))
1117        } else {
1118            Ok(router)
1119        }
1120    }
1121
1122    fn request_timeout_status_code(request: &Request) -> HttpStatusCode {
1123        if request.uri().path() == "/v1/prometheus/write" {
1124            HttpStatusCode::GATEWAY_TIMEOUT
1125        } else {
1126            HttpStatusCode::REQUEST_TIMEOUT
1127        }
1128    }
1129
1130    fn route_metrics<S>(metrics_handler: MetricsHandler) -> Router<S> {
1131        Router::new()
1132            .route("/metrics", routing::get(handler::metrics))
1133            .with_state(metrics_handler)
1134    }
1135
1136    fn route_loki<S>(log_state: LogState) -> Router<S> {
1137        Router::new()
1138            .route("/api/v1/push", routing::post(loki::loki_ingest))
1139            .layer(
1140                ServiceBuilder::new()
1141                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1142            )
1143            .with_state(log_state)
1144    }
1145
1146    fn route_splunk<S>(log_state: LogState) -> Router<S> {
1147        Router::new()
1148            .route(
1149                "/services/collector/health",
1150                routing::get(splunk::handle_health),
1151            )
1152            .route(
1153                "/services/collector/health/1.0",
1154                routing::get(splunk::handle_health),
1155            )
1156            // The event endpoint plus its base and versioned aliases all serve
1157            // the same handler (Splunk JSON event protocol).
1158            .route(
1159                "/services/collector/event",
1160                routing::post(splunk::handle_event),
1161            )
1162            .route("/services/collector", routing::post(splunk::handle_event))
1163            .route(
1164                "/services/collector/event/1.0",
1165                routing::post(splunk::handle_event),
1166            )
1167            // The raw endpoint (plain-text body, one event per line) plus its
1168            // versioned alias.
1169            .route("/services/collector/raw", routing::post(splunk::handle_raw))
1170            .route(
1171                "/services/collector/raw/1.0",
1172                routing::post(splunk::handle_raw),
1173            )
1174            .layer(
1175                ServiceBuilder::new()
1176                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1177            )
1178            .with_state(log_state)
1179    }
1180
1181    fn route_elasticsearch<S>(log_state: LogState) -> Router<S> {
1182        Router::new()
1183            // Return fake responsefor HEAD '/' request.
1184            .route(
1185                "/",
1186                routing::head((HttpStatusCode::OK, elasticsearch::elasticsearch_headers())),
1187            )
1188            // Return fake response for Elasticsearch version request.
1189            .route("/", routing::get(elasticsearch::handle_get_version))
1190            // Return fake response for Elasticsearch license request.
1191            .route("/_license", routing::get(elasticsearch::handle_get_license))
1192            .route("/_bulk", routing::post(elasticsearch::handle_bulk_api))
1193            .route(
1194                "/{index}/_bulk",
1195                routing::post(elasticsearch::handle_bulk_api_with_index),
1196            )
1197            // Return fake response for Elasticsearch ilm request.
1198            .route(
1199                "/_ilm/policy/{*path}",
1200                routing::any((
1201                    HttpStatusCode::OK,
1202                    elasticsearch::elasticsearch_headers(),
1203                    axum::Json(serde_json::json!({})),
1204                )),
1205            )
1206            // Return fake response for Elasticsearch index template request.
1207            .route(
1208                "/_index_template/{*path}",
1209                routing::any((
1210                    HttpStatusCode::OK,
1211                    elasticsearch::elasticsearch_headers(),
1212                    axum::Json(serde_json::json!({})),
1213                )),
1214            )
1215            // Return fake response for Elasticsearch ingest pipeline request.
1216            // See: https://www.elastic.co/guide/en/elasticsearch/reference/8.8/put-pipeline-api.html.
1217            .route(
1218                "/_ingest/{*path}",
1219                routing::any((
1220                    HttpStatusCode::OK,
1221                    elasticsearch::elasticsearch_headers(),
1222                    axum::Json(serde_json::json!({})),
1223                )),
1224            )
1225            // Return fake response for Elasticsearch nodes discovery request.
1226            // See: https://www.elastic.co/guide/en/elasticsearch/reference/8.8/cluster.html.
1227            .route(
1228                "/_nodes/{*path}",
1229                routing::any((
1230                    HttpStatusCode::OK,
1231                    elasticsearch::elasticsearch_headers(),
1232                    axum::Json(serde_json::json!({})),
1233                )),
1234            )
1235            // Return fake response for Logstash APIs requests.
1236            // See: https://www.elastic.co/guide/en/elasticsearch/reference/8.8/logstash-apis.html
1237            .route(
1238                "/logstash/{*path}",
1239                routing::any((
1240                    HttpStatusCode::OK,
1241                    elasticsearch::elasticsearch_headers(),
1242                    axum::Json(serde_json::json!({})),
1243                )),
1244            )
1245            .route(
1246                "/_logstash/{*path}",
1247                routing::any((
1248                    HttpStatusCode::OK,
1249                    elasticsearch::elasticsearch_headers(),
1250                    axum::Json(serde_json::json!({})),
1251                )),
1252            )
1253            .layer(ServiceBuilder::new().layer(RequestDecompressionLayer::new()))
1254            .with_state(log_state)
1255    }
1256
1257    #[deprecated(since = "0.11.0", note = "Use `route_pipelines()` instead.")]
1258    fn route_log_deprecated<S>(log_state: LogState) -> Router<S> {
1259        Router::new()
1260            .route("/logs", routing::post(event::log_ingester))
1261            .route(
1262                "/pipelines/{pipeline_name}",
1263                routing::get(event::query_pipeline),
1264            )
1265            .route(
1266                "/pipelines/{pipeline_name}",
1267                routing::post(event::add_pipeline),
1268            )
1269            .route(
1270                "/pipelines/{pipeline_name}",
1271                routing::delete(event::delete_pipeline),
1272            )
1273            .route("/pipelines/dryrun", routing::post(event::pipeline_dryrun))
1274            .layer(
1275                ServiceBuilder::new()
1276                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1277            )
1278            .with_state(log_state)
1279    }
1280
1281    fn route_pipelines<S>(log_state: LogState) -> Router<S> {
1282        Router::new()
1283            .route("/ingest", routing::post(event::log_ingester))
1284            .route(
1285                "/pipelines/{pipeline_name}",
1286                routing::get(event::query_pipeline),
1287            )
1288            .route(
1289                "/pipelines/{pipeline_name}/ddl",
1290                routing::get(event::query_pipeline_ddl),
1291            )
1292            .route(
1293                "/pipelines/{pipeline_name}",
1294                routing::post(event::add_pipeline),
1295            )
1296            .route(
1297                "/pipelines/{pipeline_name}",
1298                routing::delete(event::delete_pipeline),
1299            )
1300            .route("/pipelines/_dryrun", routing::post(event::pipeline_dryrun))
1301            .layer(
1302                ServiceBuilder::new()
1303                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1304            )
1305            .with_state(log_state)
1306    }
1307
1308    fn route_sql<S>(api_state: ApiState) -> Router<S> {
1309        Router::new()
1310            .route("/sql", routing::get(handler::sql).post(handler::sql))
1311            .route(
1312                "/sql/parse",
1313                routing::get(handler::sql_parse).post(handler::sql_parse),
1314            )
1315            .route(
1316                "/sql/format",
1317                routing::get(handler::sql_format).post(handler::sql_format),
1318            )
1319            .route(
1320                "/promql",
1321                routing::get(handler::promql).post(handler::promql),
1322            )
1323            .route(
1324                "/sql/analyze/stream",
1325                routing::post(handler::sql_analyze_stream),
1326            )
1327            .with_state(api_state)
1328    }
1329
1330    fn route_logs<S>(log_handler: LogQueryHandlerRef) -> Router<S> {
1331        Router::new()
1332            .route("/logs", routing::get(logs::logs).post(logs::logs))
1333            .with_state(log_handler)
1334    }
1335
1336    /// Route Prometheus [HTTP API].
1337    ///
1338    /// [HTTP API]: https://prometheus.io/docs/prometheus/latest/querying/api/
1339    pub fn route_prometheus<S>(prometheus_handler: PrometheusHandlerRef) -> Router<S> {
1340        Router::new()
1341            .route(
1342                "/format_query",
1343                routing::post(format_query).get(format_query),
1344            )
1345            .route("/status/buildinfo", routing::get(build_info_query))
1346            .route("/query", routing::post(instant_query).get(instant_query))
1347            .route("/query_range", routing::post(range_query).get(range_query))
1348            .route("/labels", routing::post(labels_query).get(labels_query))
1349            .route("/metadata", routing::get(metadata_query))
1350            .route("/series", routing::post(series_query).get(series_query))
1351            .route("/parse_query", routing::post(parse_query).get(parse_query))
1352            .route(
1353                "/label/{label_name}/values",
1354                routing::get(label_values_query),
1355            )
1356            .layer(ServiceBuilder::new().layer(CompressionLayer::new()))
1357            .with_state(prometheus_handler)
1358    }
1359
1360    /// Route Prometheus remote [read] and [write] API. In other places the related modules are
1361    /// called `prom_store`.
1362    ///
1363    /// [read]: https://prometheus.io/docs/prometheus/latest/querying/remote_read_api/
1364    /// [write]: https://prometheus.io/docs/concepts/remote_write_spec/
1365    fn route_prom<S>(state: PromStoreState) -> Router<S> {
1366        Router::new()
1367            .route("/read", routing::post(prom_store::remote_read))
1368            .route("/write", routing::post(prom_store::remote_write))
1369            .with_state(state)
1370    }
1371
1372    fn route_influxdb<S>(influxdb_handler: InfluxdbLineProtocolHandlerRef) -> Router<S> {
1373        Router::new()
1374            .route("/write", routing::post(influxdb_write_v1))
1375            .route("/api/v2/write", routing::post(influxdb_write_v2))
1376            .layer(
1377                ServiceBuilder::new()
1378                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1379            )
1380            .route("/ping", routing::get(influxdb_ping))
1381            .route("/health", routing::get(influxdb_health))
1382            .with_state(influxdb_handler)
1383    }
1384
1385    fn route_opentsdb<S>(opentsdb_handler: OpentsdbProtocolHandlerRef) -> Router<S> {
1386        Router::new()
1387            .route("/api/put", routing::post(opentsdb::put))
1388            .with_state(opentsdb_handler)
1389    }
1390
1391    fn route_otlp<S>(
1392        otlp_handler: OpenTelemetryProtocolHandlerRef,
1393        with_metric_engine: bool,
1394        experimental_enable_exponential_histogram: bool,
1395    ) -> Router<S> {
1396        Router::new()
1397            .route("/v1/metrics", routing::post(otlp::metrics))
1398            .route("/v1/traces", routing::post(otlp::traces))
1399            .route("/v1/logs", routing::post(otlp::logs))
1400            .layer(
1401                ServiceBuilder::new()
1402                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1403            )
1404            .with_state(OtlpState {
1405                with_metric_engine,
1406                experimental_enable_exponential_histogram,
1407                handler: otlp_handler,
1408            })
1409    }
1410
1411    fn route_config<S>(state: GreptimeOptionsConfigState) -> Router<S> {
1412        Router::new()
1413            .route("/config", routing::get(handler::config))
1414            .with_state(state)
1415    }
1416
1417    fn route_jaeger<S>(handler: JaegerQueryHandlerRef) -> Router<S> {
1418        Router::new()
1419            .route("/api/services", routing::get(jaeger::handle_get_services))
1420            .route(
1421                "/api/services/{service_name}/operations",
1422                routing::get(jaeger::handle_get_operations_by_service),
1423            )
1424            .route(
1425                "/api/operations",
1426                routing::get(jaeger::handle_get_operations),
1427            )
1428            .route("/api/traces", routing::get(jaeger::handle_find_traces))
1429            .route(
1430                "/api/traces/{trace_id}",
1431                routing::get(jaeger::handle_get_trace),
1432            )
1433            .with_state(handler)
1434    }
1435
1436    #[cfg(feature = "dashboard")]
1437    fn route_dashboard<S>(handler: DashboardHandlerRef) -> Router<S> {
1438        use crate::http::dashboard::{add_dashboard, delete_dashboard, list_dashboards};
1439
1440        Router::new()
1441            .route("/", routing::get(list_dashboards))
1442            .route("/{dashboard_name}", routing::post(add_dashboard))
1443            .route("/{dashboard_name}", routing::delete(delete_dashboard))
1444            .layer(
1445                ServiceBuilder::new()
1446                    .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1447            )
1448            .with_state(DashboardState { handler })
1449    }
1450
1451    #[cfg(not(feature = "dashboard"))]
1452    fn route_dashboard<S>(handler: DashboardHandlerRef) -> Router<S> {
1453        Router::new().with_state(DashboardState { handler })
1454    }
1455}
1456
1457pub const HTTP_SERVER: &str = "HTTP_SERVER";
1458pub const HTTP_API_SERVER: &str = "HTTP_API_SERVER";
1459
1460#[async_trait]
1461impl Server for HttpServer {
1462    async fn shutdown(&self) -> Result<()> {
1463        let mut shutdown_tx = self.shutdown_tx.lock().await;
1464        if let Some(tx) = shutdown_tx.take()
1465            && tx.send(()).is_err()
1466        {
1467            info!("Receiver dropped, the HTTP server has already exited");
1468        }
1469        info!("Shutdown {}", self.kind());
1470
1471        Ok(())
1472    }
1473
1474    async fn start(&mut self, listening: SocketAddr) -> Result<()> {
1475        let (tx, rx) = oneshot::channel();
1476        let serve = {
1477            let mut shutdown_tx = self.shutdown_tx.lock().await;
1478            ensure!(
1479                shutdown_tx.is_none(),
1480                AlreadyStartedSnafu {
1481                    server: self.kind()
1482                }
1483            );
1484
1485            let app = self.build(self.make_app())?;
1486            let listener = tokio::net::TcpListener::bind(listening)
1487                .await
1488                .context(AddressBindSnafu { addr: listening })?
1489                .tap_io(|tcp_stream| {
1490                    if let Err(e) = tcp_stream.set_nodelay(true) {
1491                        error!(e; "Failed to set TCP_NODELAY on incoming connection");
1492                    }
1493                });
1494            let serve = axum::serve(
1495                listener,
1496                app.into_make_service_with_connect_info::<SocketAddr>(),
1497            );
1498
1499            // FIXME(yingwen): Support keepalive.
1500            // See:
1501            // - https://github.com/tokio-rs/axum/discussions/2939
1502            // - https://stackoverflow.com/questions/73069718/how-do-i-keep-alive-tokiotcpstream-in-rust
1503            // let server = axum::Server::try_bind(&listening)
1504            //     .with_context(|_| AddressBindSnafu { addr: listening })?
1505            //     .tcp_nodelay(true)
1506            //     // Enable TCP keepalive to close the dangling established connections.
1507            //     // It's configured to let the keepalive probes first send after the connection sits
1508            //     // idle for 59 minutes, and then send every 10 seconds for 6 times.
1509            //     // So the connection will be closed after roughly 1 hour.
1510            //     .tcp_keepalive(Some(Duration::from_secs(59 * 60)))
1511            //     .tcp_keepalive_interval(Some(Duration::from_secs(10)))
1512            //     .tcp_keepalive_retries(Some(6))
1513            //     .serve(app.into_make_service());
1514
1515            *shutdown_tx = Some(tx);
1516
1517            serve
1518        };
1519        let listening = serve.local_addr().context(InternalIoSnafu)?;
1520        info!("{} server is bound to {}", self.kind(), listening);
1521
1522        common_runtime::spawn_global(async move {
1523            if let Err(e) = serve
1524                .with_graceful_shutdown(rx.map(drop))
1525                .await
1526                .context(InternalIoSnafu)
1527            {
1528                error!(e; "Failed to shutdown http server");
1529            }
1530        });
1531
1532        self.bind_addr = Some(listening);
1533        Ok(())
1534    }
1535
1536    fn name(&self) -> &str {
1537        match self.kind {
1538            HttpServerKind::Api => HTTP_API_SERVER,
1539            HttpServerKind::Full => HTTP_SERVER,
1540        }
1541    }
1542
1543    fn bind_addr(&self) -> Option<SocketAddr> {
1544        self.bind_addr
1545    }
1546
1547    fn as_any(&self) -> &dyn std::any::Any {
1548        self
1549    }
1550}
1551
1552#[cfg(test)]
1553mod test {
1554    use std::future::pending;
1555    use std::io::Cursor;
1556    use std::sync::Arc;
1557
1558    use arrow_ipc::reader::StreamReader;
1559    use arrow_schema::DataType;
1560    use axum::http::StatusCode;
1561    use axum::routing::{get, post};
1562    use common_query::{Output, OutputData};
1563    use common_recordbatch::RecordBatches;
1564    use datafusion_expr::LogicalPlan;
1565    use datatypes::prelude::*;
1566    use datatypes::schema::{ColumnSchema, Schema};
1567    use datatypes::vectors::{StringVector, UInt32Vector};
1568    use header::constants::GREPTIME_DB_HEADER_TIMEOUT;
1569    use query::parser::PromQuery;
1570    use query::query_engine::DescribeResult;
1571    use session::context::QueryContextRef;
1572    use sql::statements::statement::Statement;
1573    use tokio::sync::mpsc;
1574    use tokio::time::Instant;
1575
1576    use super::*;
1577    use crate::http::test_helpers::TestClient;
1578    use crate::prom_remote_write::validation::validate_label_name;
1579    use crate::query_handler::sql::SqlQueryHandler;
1580
1581    struct DummyInstance {
1582        _tx: mpsc::Sender<(String, Vec<u8>)>,
1583    }
1584
1585    #[async_trait]
1586    impl SqlQueryHandler for DummyInstance {
1587        async fn do_query(&self, _: &str, _: QueryContextRef) -> Vec<Result<Output>> {
1588            unimplemented!()
1589        }
1590
1591        async fn do_analyze_stream_query(&self, _: &str, _: QueryContextRef) -> Result<Output> {
1592            let stream = common_recordbatch::RecordBatches::empty().as_stream();
1593            Ok(Output::new(OutputData::Stream(stream), Default::default()))
1594        }
1595
1596        async fn do_promql_query(&self, _: &PromQuery, _: QueryContextRef) -> Vec<Result<Output>> {
1597            unimplemented!()
1598        }
1599
1600        async fn do_exec_plan(
1601            &self,
1602            _plan: LogicalPlan,
1603            _stmt: Option<Statement>,
1604            _query_ctx: QueryContextRef,
1605        ) -> Result<Output> {
1606            unimplemented!()
1607        }
1608
1609        async fn do_describe(
1610            &self,
1611            _stmt: sql::statements::statement::Statement,
1612            _query_ctx: QueryContextRef,
1613        ) -> Result<Option<DescribeResult>> {
1614            unimplemented!()
1615        }
1616
1617        async fn is_valid_schema(&self, _catalog: &str, _schema: &str) -> Result<bool> {
1618            Ok(true)
1619        }
1620    }
1621
1622    async fn forever() {
1623        pending().await
1624    }
1625
1626    fn make_test_app(tx: mpsc::Sender<(String, Vec<u8>)>) -> Router {
1627        make_test_app_custom(tx, HttpOptions::default())
1628    }
1629
1630    fn make_test_app_custom(tx: mpsc::Sender<(String, Vec<u8>)>, options: HttpOptions) -> Router {
1631        let instance = Arc::new(DummyInstance { _tx: tx });
1632        let server = HttpServerBuilder::new(options)
1633            .with_sql_handler(instance.clone())
1634            .build();
1635        let app = server
1636            .make_app()
1637            .route("/test/timeout", get(forever))
1638            .route("/v1/prometheus/write", post(forever));
1639        server.build(app).unwrap()
1640    }
1641
1642    fn make_split_builder() -> HttpServerBuilder {
1643        let (tx, _rx) = mpsc::channel(100);
1644        let instance = Arc::new(DummyInstance { _tx: tx });
1645        // Enable the dedicated API server so the split behavior can be exercised.
1646        let options = HttpOptions {
1647            enable_api_server: true,
1648            ..HttpOptions::default()
1649        };
1650        HttpServerBuilder::new(options)
1651            .with_sql_handler(instance)
1652            .with_metrics_handler(MetricsHandler)
1653            .with_greptime_config_options("dummy = \"value\"".to_string())
1654    }
1655
1656    #[tokio::test]
1657    pub async fn test_http_api_options_defaults() {
1658        let opts = HttpOptions::default();
1659        // The API server is disabled by default.
1660        assert!(!opts.enable_api_server);
1661        assert_eq!(opts.api_server_addr, "127.0.0.1:4006");
1662    }
1663
1664    #[tokio::test]
1665    pub async fn test_build_serves_all_routes() {
1666        // The single (internal/full) server built via `build()` keeps serving both
1667        // the public v1 interfaces and the internal ones. This is the historical
1668        // single-server behavior and must not change.
1669        let server = make_split_builder().build();
1670        assert_eq!(server.name(), HTTP_SERVER);
1671
1672        let app = server.build(server.make_app()).unwrap();
1673        let client = TestClient::new(app).await;
1674
1675        for path in ["/v1/health", "/health", "/status", "/metrics", "/config"] {
1676            let res = client.get(path).send().await;
1677            assert_eq!(
1678                res.status(),
1679                StatusCode::OK,
1680                "internal/full server should serve {path}"
1681            );
1682        }
1683    }
1684
1685    #[tokio::test]
1686    pub async fn test_build_servers_separates_api_and_internal_routes() {
1687        // `build_servers` produces the internal/full server (everything) plus a
1688        // dedicated API server that only serves the v1 interfaces (and dashboard).
1689        let (internal, api) = make_split_builder().build_servers();
1690        let api = api.expect("API server is explicitly enabled in make_split_builder");
1691        assert_eq!(internal.name(), HTTP_SERVER);
1692        assert_eq!(api.name(), HTTP_API_SERVER);
1693
1694        let internal_app = internal.build(internal.make_app()).unwrap();
1695        let api_app = api.build(api.make_app()).unwrap();
1696
1697        let internal_client = TestClient::new(internal_app).await;
1698        let api_client = TestClient::new(api_app).await;
1699
1700        // Both servers serve the v1 interfaces (e.g. the v1 health check).
1701        assert_eq!(
1702            internal_client.get("/v1/health").send().await.status(),
1703            StatusCode::OK
1704        );
1705        assert_eq!(
1706            api_client.get("/v1/health").send().await.status(),
1707            StatusCode::OK
1708        );
1709
1710        // The internal-only interfaces are served by the internal/full server...
1711        for path in ["/health", "/status", "/metrics", "/config"] {
1712            assert_eq!(
1713                internal_client.get(path).send().await.status(),
1714                StatusCode::OK,
1715                "internal/full server should serve {path}"
1716            );
1717            // ...and NOT by the API-only server.
1718            assert_eq!(
1719                api_client.get(path).send().await.status(),
1720                StatusCode::NOT_FOUND,
1721                "API server should NOT serve {path}"
1722            );
1723        }
1724    }
1725
1726    #[test]
1727    fn test_build_servers_api_inherits_http_options() {
1728        // The API server must inherit every `[http]` option except the bound addr.
1729        let http_opts = HttpOptions {
1730            timeout: Duration::from_secs(42),
1731            body_limit: ReadableSize::mb(128),
1732            cors_allowed_origins: vec!["https://example.com".to_string()],
1733            enable_api_server: true,
1734            ..HttpOptions::default()
1735        };
1736        let (internal, api) = HttpServerBuilder::new(http_opts.clone()).build_servers();
1737        let api = api.expect("API server is explicitly enabled");
1738
1739        // Only the bound address differs.
1740        assert_eq!(internal.options.addr, http_opts.addr);
1741        assert_eq!(api.options.addr, "127.0.0.1:4006");
1742        // Everything else is inherited from `[http]`.
1743        assert_eq!(api.options.timeout, http_opts.timeout);
1744        assert_eq!(api.options.body_limit, http_opts.body_limit);
1745        assert_eq!(
1746            api.options.cors_allowed_origins,
1747            http_opts.cors_allowed_origins
1748        );
1749    }
1750
1751    #[test]
1752    fn test_is_api_listener_path() {
1753        // `/v1` namespace (exact slash boundary).
1754        assert!(is_api_listener_path("/v1"));
1755        assert!(is_api_listener_path("/v1/"));
1756        assert!(is_api_listener_path("/v1/sql"));
1757        assert!(!is_api_listener_path("/v10/sql"));
1758        assert!(!is_api_listener_path("/v1-internal"));
1759        // `/dashboard` namespace.
1760        assert!(is_api_listener_path("/dashboard"));
1761        assert!(is_api_listener_path("/dashboard/app.js"));
1762        assert!(!is_api_listener_path("/dashboard-admin"));
1763        // Operational paths are never API-visible.
1764        assert!(!is_api_listener_path("/metrics"));
1765        assert!(!is_api_listener_path("/status/plugin"));
1766        assert!(!is_api_listener_path("/health"));
1767    }
1768
1769    #[tokio::test]
1770    pub async fn test_extra_router_respects_api_surface() {
1771        // Downstream routes added through `with_extra_router()` (e.g. enterprise
1772        // plugins or the `RouterConfigurator`) are present on the full listener
1773        // and on the API listener's router, but the API listener's outer guard
1774        // hides every non-`/v1` path automatically. This is what keeps the
1775        // feature correct for greptimedb-enterprise with no downstream changes.
1776        let (tx, _rx) = mpsc::channel(100);
1777        let instance = Arc::new(DummyInstance { _tx: tx });
1778        let options = HttpOptions {
1779            enable_api_server: true,
1780            ..HttpOptions::default()
1781        };
1782        let builder = HttpServerBuilder::new(options)
1783            .with_sql_handler(instance)
1784            .with_extra_router(
1785                Router::new()
1786                    .route("/status/plugin", routing::get(|| async { "ok" }))
1787                    .route("/v1/plugin", routing::get(|| async { "ok" })),
1788            );
1789        let (full, api) = builder.build_servers();
1790        let api = api.expect("API server is explicitly enabled");
1791
1792        let full_client = TestClient::new(full.build(full.make_app()).unwrap()).await;
1793        let api_client = TestClient::new(api.build(api.make_app()).unwrap()).await;
1794
1795        // A non-`/v1` route is reachable on the full listener...
1796        assert_eq!(
1797            full_client.get("/status/plugin").send().await.status(),
1798            StatusCode::OK
1799        );
1800        // ...and hidden (404) on the API listener, without the caller classifying it.
1801        assert_eq!(
1802            api_client.get("/status/plugin").send().await.status(),
1803            StatusCode::NOT_FOUND
1804        );
1805
1806        // A `/v1` route is reachable on both.
1807        assert_eq!(
1808            full_client.get("/v1/plugin").send().await.status(),
1809            StatusCode::OK
1810        );
1811        assert_eq!(
1812            api_client.get("/v1/plugin").send().await.status(),
1813            StatusCode::OK
1814        );
1815
1816        // Slash-boundary correctness on the API listener.
1817        assert_eq!(
1818            api_client.get("/v10/plugin").send().await.status(),
1819            StatusCode::NOT_FOUND
1820        );
1821    }
1822
1823    #[test]
1824    fn test_build_servers_api_disabled_by_default() {
1825        // The API server is disabled by default, so `build_servers` returns no
1826        // second server unless `enable_api_server` is set.
1827        let (_internal, api) = HttpServerBuilder::new(HttpOptions::default()).build_servers();
1828        assert!(api.is_none());
1829    }
1830
1831    #[tokio::test]
1832    pub async fn test_cors() {
1833        // cors is on by default
1834        let (tx, _rx) = mpsc::channel(100);
1835        let app = make_test_app(tx);
1836        let client = TestClient::new(app).await;
1837
1838        let res = client.get("/health").send().await;
1839
1840        assert_eq!(res.status(), StatusCode::OK);
1841        assert_eq!(
1842            res.headers()
1843                .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1844                .expect("expect cors header origin"),
1845            "*"
1846        );
1847
1848        let res = client.get("/v1/health").send().await;
1849
1850        assert_eq!(res.status(), StatusCode::OK);
1851        assert_eq!(
1852            res.headers()
1853                .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1854                .expect("expect cors header origin"),
1855            "*"
1856        );
1857
1858        let res = client
1859            .options("/health")
1860            .header("Access-Control-Request-Headers", "x-greptime-auth")
1861            .header("Access-Control-Request-Method", "DELETE")
1862            .header("Origin", "https://example.com")
1863            .send()
1864            .await;
1865        assert_eq!(res.status(), StatusCode::OK);
1866        assert_eq!(
1867            res.headers()
1868                .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1869                .expect("expect cors header origin"),
1870            "*"
1871        );
1872        assert_eq!(
1873            res.headers()
1874                .get(http::header::ACCESS_CONTROL_ALLOW_HEADERS)
1875                .expect("expect cors header headers"),
1876            "*"
1877        );
1878        assert_eq!(
1879            res.headers()
1880                .get(http::header::ACCESS_CONTROL_ALLOW_METHODS)
1881                .expect("expect cors header methods"),
1882            "GET,POST,PUT,DELETE,HEAD"
1883        );
1884    }
1885
1886    #[tokio::test]
1887    pub async fn test_cors_custom_origins() {
1888        // cors is on by default
1889        let (tx, _rx) = mpsc::channel(100);
1890        let origin = "https://example.com";
1891
1892        let options = HttpOptions {
1893            cors_allowed_origins: vec![origin.to_string()],
1894            ..Default::default()
1895        };
1896
1897        let app = make_test_app_custom(tx, options);
1898        let client = TestClient::new(app).await;
1899
1900        let res = client.get("/health").header("Origin", origin).send().await;
1901
1902        assert_eq!(res.status(), StatusCode::OK);
1903        assert_eq!(
1904            res.headers()
1905                .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1906                .expect("expect cors header origin"),
1907            origin
1908        );
1909
1910        let res = client
1911            .get("/health")
1912            .header("Origin", "https://notallowed.com")
1913            .send()
1914            .await;
1915
1916        assert_eq!(res.status(), StatusCode::OK);
1917        assert!(
1918            !res.headers()
1919                .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1920        );
1921    }
1922
1923    #[tokio::test]
1924    pub async fn test_cors_disabled() {
1925        // cors is on by default
1926        let (tx, _rx) = mpsc::channel(100);
1927
1928        let options = HttpOptions {
1929            enable_cors: false,
1930            ..Default::default()
1931        };
1932
1933        let app = make_test_app_custom(tx, options);
1934        let client = TestClient::new(app).await;
1935
1936        let res = client.get("/health").send().await;
1937
1938        assert_eq!(res.status(), StatusCode::OK);
1939        assert!(
1940            !res.headers()
1941                .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1942        );
1943    }
1944
1945    #[test]
1946    fn test_http_options_default() {
1947        let default = HttpOptions::default();
1948        assert_eq!("127.0.0.1:4000".to_string(), default.addr);
1949        assert_eq!(Duration::from_secs(0), default.timeout)
1950    }
1951
1952    #[tokio::test]
1953    async fn test_http_options_legacy_analyze_stream_config_is_ignored() {
1954        let options: HttpOptions = serde_json::from_value(serde_json::json!({
1955            "addr": "127.0.0.1:4000",
1956            "timeout": "0s",
1957            "body_limit": "64MiB",
1958            "cors_allowed_origins": [],
1959            "enable_cors": true,
1960            "experimental_enable_explain_analyze_stream": false,
1961            "enable_api_server": false,
1962            "api_server_addr": "127.0.0.1:4006"
1963        }))
1964        .unwrap();
1965        let serialized = serde_json::to_string(&options).unwrap();
1966        assert!(!serialized.contains("experimental_enable_explain_analyze_stream"));
1967
1968        let (tx, _rx) = mpsc::channel(100);
1969        let app = make_test_app_custom(tx, options);
1970        let client = TestClient::new(app).await;
1971        let response = client
1972            .post("/v1/sql/analyze/stream")
1973            .form(&handler::SqlQuery {
1974                sql: Some("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
1975                ..Default::default()
1976            })
1977            .send()
1978            .await;
1979        assert_ne!(response.status(), StatusCode::NOT_FOUND);
1980    }
1981
1982    #[tokio::test]
1983    async fn test_http_server_request_timeout() {
1984        common_telemetry::init_default_ut_logging();
1985
1986        let (tx, _rx) = mpsc::channel(100);
1987        let options = HttpOptions {
1988            timeout: Duration::from_millis(10),
1989            ..Default::default()
1990        };
1991        let app = make_test_app_custom(tx, options);
1992        let client = TestClient::new(app).await;
1993        let res = client.get("/test/timeout").send().await;
1994        assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
1995
1996        let res = client.post("/v1/prometheus/write").send().await;
1997        assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
1998
1999        let now = Instant::now();
2000        let res = client
2001            .get("/test/timeout")
2002            .header(GREPTIME_DB_HEADER_TIMEOUT, "20ms")
2003            .send()
2004            .await;
2005        assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
2006        let elapsed = now.elapsed();
2007        assert!(elapsed > Duration::from_millis(15));
2008
2009        tokio::time::timeout(
2010            Duration::from_millis(15),
2011            client
2012                .get("/test/timeout")
2013                .header(GREPTIME_DB_HEADER_TIMEOUT, "0s")
2014                .send(),
2015        )
2016        .await
2017        .unwrap_err();
2018
2019        tokio::time::timeout(
2020            Duration::from_millis(15),
2021            client
2022                .get("/test/timeout")
2023                .header(
2024                    GREPTIME_DB_HEADER_TIMEOUT,
2025                    humantime::format_duration(Duration::default()).to_string(),
2026                )
2027                .send(),
2028        )
2029        .await
2030        .unwrap_err();
2031    }
2032
2033    #[tokio::test]
2034    async fn test_schema_for_empty_response() {
2035        let column_schemas = vec![
2036            ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2037            ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2038        ];
2039        let schema = Arc::new(Schema::new(column_schemas));
2040
2041        let recordbatches = RecordBatches::try_new(schema.clone(), vec![]).unwrap();
2042        let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2043
2044        let json_resp = GreptimedbV1Response::from_output(outputs).await;
2045        if let HttpResponse::GreptimedbV1(json_resp) = json_resp {
2046            let json_output = &json_resp.output[0];
2047            if let GreptimeQueryOutput::Records(r) = json_output {
2048                assert_eq!(r.num_rows(), 0);
2049                assert_eq!(r.num_cols(), 2);
2050                assert_eq!(r.schema.column_schemas[0].name, "numbers");
2051                assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2052            } else {
2053                panic!("invalid output type");
2054            }
2055        } else {
2056            panic!("invalid format")
2057        }
2058    }
2059
2060    #[tokio::test]
2061    async fn test_recordbatches_conversion() {
2062        let column_schemas = vec![
2063            ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2064            ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2065        ];
2066        let schema = Arc::new(Schema::new(column_schemas));
2067        let columns: Vec<VectorRef> = vec![
2068            Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
2069            Arc::new(StringVector::from(vec![
2070                None,
2071                Some("hello"),
2072                Some("greptime"),
2073                None,
2074            ])),
2075        ];
2076        let recordbatch = RecordBatch::new(schema.clone(), columns).unwrap();
2077
2078        for format in [
2079            ResponseFormat::GreptimedbV1,
2080            ResponseFormat::InfluxdbV1,
2081            ResponseFormat::Csv(true, true),
2082            ResponseFormat::Table,
2083            ResponseFormat::Arrow,
2084            ResponseFormat::Json,
2085            ResponseFormat::Null,
2086        ] {
2087            let recordbatches =
2088                RecordBatches::try_new(schema.clone(), vec![recordbatch.clone()]).unwrap();
2089            let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2090            let json_resp = match format {
2091                ResponseFormat::Arrow => ArrowResponse::from_output(outputs, None).await,
2092                ResponseFormat::Csv(with_names, with_types) => {
2093                    CsvResponse::from_output(outputs, with_names, with_types).await
2094                }
2095                ResponseFormat::Table => TableResponse::from_output(outputs).await,
2096                ResponseFormat::GreptimedbV1 => GreptimedbV1Response::from_output(outputs).await,
2097                ResponseFormat::InfluxdbV1 => InfluxdbV1Response::from_output(outputs, None).await,
2098                ResponseFormat::Json => JsonResponse::from_output(outputs).await,
2099                ResponseFormat::Null => NullResponse::from_output(outputs).await,
2100            };
2101
2102            match json_resp {
2103                HttpResponse::GreptimedbV1(resp) => {
2104                    let json_output = &resp.output[0];
2105                    if let GreptimeQueryOutput::Records(r) = json_output {
2106                        assert_eq!(r.num_rows(), 4);
2107                        assert_eq!(r.num_cols(), 2);
2108                        assert_eq!(r.schema.column_schemas[0].name, "numbers");
2109                        assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2110                        assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2111                        assert_eq!(r.rows[0][1], serde_json::Value::Null);
2112                    } else {
2113                        panic!("invalid output type");
2114                    }
2115                }
2116                HttpResponse::InfluxdbV1(resp) => {
2117                    let json_output = &resp.results()[0];
2118                    assert_eq!(json_output.num_rows(), 4);
2119                    assert_eq!(json_output.num_cols(), 2);
2120                    assert_eq!(json_output.series[0].columns.clone()[0], "numbers");
2121                    assert_eq!(
2122                        json_output.series[0].values[0][0],
2123                        serde_json::Value::from(1)
2124                    );
2125                    assert_eq!(json_output.series[0].values[0][1], serde_json::Value::Null);
2126                }
2127                HttpResponse::Csv(resp) => {
2128                    let output = &resp.output()[0];
2129                    if let GreptimeQueryOutput::Records(r) = output {
2130                        assert_eq!(r.num_rows(), 4);
2131                        assert_eq!(r.num_cols(), 2);
2132                        assert_eq!(r.schema.column_schemas[0].name, "numbers");
2133                        assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2134                        assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2135                        assert_eq!(r.rows[0][1], serde_json::Value::Null);
2136                    } else {
2137                        panic!("invalid output type");
2138                    }
2139                }
2140
2141                HttpResponse::Table(resp) => {
2142                    let output = &resp.output()[0];
2143                    if let GreptimeQueryOutput::Records(r) = output {
2144                        assert_eq!(r.num_rows(), 4);
2145                        assert_eq!(r.num_cols(), 2);
2146                        assert_eq!(r.schema.column_schemas[0].name, "numbers");
2147                        assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2148                        assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2149                        assert_eq!(r.rows[0][1], serde_json::Value::Null);
2150                    } else {
2151                        panic!("invalid output type");
2152                    }
2153                }
2154
2155                HttpResponse::Arrow(resp) => {
2156                    let output = resp.data;
2157                    let mut reader = StreamReader::try_new(Cursor::new(output), None)
2158                        .expect("Arrow reader error");
2159                    let schema = reader.schema();
2160                    assert_eq!(schema.fields[0].name(), "numbers");
2161                    assert_eq!(schema.fields[0].data_type(), &DataType::UInt32);
2162                    assert_eq!(schema.fields[1].name(), "strings");
2163                    assert_eq!(schema.fields[1].data_type(), &DataType::Utf8);
2164
2165                    let rb = reader.next().unwrap().expect("read record batch failed");
2166                    assert_eq!(rb.num_columns(), 2);
2167                    assert_eq!(rb.num_rows(), 4);
2168                }
2169
2170                HttpResponse::Json(resp) => {
2171                    let output = &resp.output()[0];
2172                    if let GreptimeQueryOutput::Records(r) = output {
2173                        assert_eq!(r.num_rows(), 4);
2174                        assert_eq!(r.num_cols(), 2);
2175                        assert_eq!(r.schema.column_schemas[0].name, "numbers");
2176                        assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2177                        assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2178                        assert_eq!(r.rows[0][1], serde_json::Value::Null);
2179                    } else {
2180                        panic!("invalid output type");
2181                    }
2182                }
2183
2184                HttpResponse::Null(resp) => {
2185                    assert_eq!(resp.rows(), 4);
2186                }
2187
2188                HttpResponse::Error(err) => unreachable!("{err:?}"),
2189            }
2190        }
2191    }
2192
2193    #[test]
2194    fn test_response_format_misc() {
2195        assert_eq!(ResponseFormat::default(), ResponseFormat::GreptimedbV1);
2196        assert_eq!(ResponseFormat::parse("arrow"), Some(ResponseFormat::Arrow));
2197        assert_eq!(
2198            ResponseFormat::parse("csv"),
2199            Some(ResponseFormat::Csv(false, false))
2200        );
2201        assert_eq!(
2202            ResponseFormat::parse("csvwithnames"),
2203            Some(ResponseFormat::Csv(true, false))
2204        );
2205        assert_eq!(
2206            ResponseFormat::parse("csvwithnamesandtypes"),
2207            Some(ResponseFormat::Csv(true, true))
2208        );
2209        assert_eq!(ResponseFormat::parse("table"), Some(ResponseFormat::Table));
2210        assert_eq!(
2211            ResponseFormat::parse("greptimedb_v1"),
2212            Some(ResponseFormat::GreptimedbV1)
2213        );
2214        assert_eq!(
2215            ResponseFormat::parse("influxdb_v1"),
2216            Some(ResponseFormat::InfluxdbV1)
2217        );
2218        assert_eq!(ResponseFormat::parse("json"), Some(ResponseFormat::Json));
2219        assert_eq!(ResponseFormat::parse("null"), Some(ResponseFormat::Null));
2220
2221        // invalid formats
2222        assert_eq!(ResponseFormat::parse("invalid"), None);
2223        assert_eq!(ResponseFormat::parse(""), None);
2224        assert_eq!(ResponseFormat::parse("CSV"), None); // Case sensitive
2225
2226        // as str
2227        assert_eq!(ResponseFormat::Arrow.as_str(), "arrow");
2228        assert_eq!(ResponseFormat::Csv(false, false).as_str(), "csv");
2229        assert_eq!(ResponseFormat::Csv(true, true).as_str(), "csv");
2230        assert_eq!(ResponseFormat::Table.as_str(), "table");
2231        assert_eq!(ResponseFormat::GreptimedbV1.as_str(), "greptimedb_v1");
2232        assert_eq!(ResponseFormat::InfluxdbV1.as_str(), "influxdb_v1");
2233        assert_eq!(ResponseFormat::Json.as_str(), "json");
2234        assert_eq!(ResponseFormat::Null.as_str(), "null");
2235        assert_eq!(ResponseFormat::default().as_str(), "greptimedb_v1");
2236    }
2237
2238    #[test]
2239    fn test_decode_label_name_strict() {
2240        let strict = PromValidationMode::Strict;
2241
2242        // Valid Prometheus label names
2243        assert!(strict.decode_label_name(b"__name__").is_ok());
2244        assert!(strict.decode_label_name(b"job").is_ok());
2245        assert!(strict.decode_label_name(b"instance").is_ok());
2246        assert!(strict.decode_label_name(b"_private").is_ok());
2247        assert!(strict.decode_label_name(b"label_with_underscores").is_ok());
2248        assert!(strict.decode_label_name(b"abc123").is_ok());
2249        assert!(strict.decode_label_name(b"A").is_ok());
2250        assert!(strict.decode_label_name(b"_").is_ok());
2251
2252        // Invalid: starts with digit
2253        assert!(strict.decode_label_name(b"0abc").is_err());
2254        assert!(strict.decode_label_name(b"123").is_err());
2255
2256        // Invalid: contains special characters
2257        assert!(strict.decode_label_name(b"label-name").is_err());
2258        assert!(strict.decode_label_name(b"label.name").is_err());
2259        assert!(strict.decode_label_name(b"label name").is_err());
2260        assert!(strict.decode_label_name(b"label/name").is_err());
2261
2262        // Invalid: empty
2263        assert!(strict.decode_label_name(b"").is_err());
2264
2265        // Invalid: non-ASCII UTF-8
2266        assert!(strict.decode_label_name("ラベル".as_bytes()).is_err());
2267
2268        // Invalid UTF-8 bytes should fail
2269        assert!(strict.decode_label_name(&[0xff, 0xfe]).is_err());
2270    }
2271
2272    #[test]
2273    fn test_decode_label_name_lossy() {
2274        let lossy = PromValidationMode::Lossy;
2275
2276        // Label name validation is always enforced.
2277        assert!(lossy.decode_label_name(b"__name__").is_ok());
2278        assert!(lossy.decode_label_name(b"label-name").is_err());
2279        assert!(lossy.decode_label_name(b"0abc").is_err());
2280
2281        // Invalid UTF-8 bytes fail the label-name byte check.
2282        assert!(lossy.decode_label_name(&[0xff, 0xfe]).is_err());
2283    }
2284
2285    #[test]
2286    fn test_decode_label_name_unchecked() {
2287        let unchecked = PromValidationMode::Unchecked;
2288
2289        // Label name validation is always enforced.
2290        assert!(unchecked.decode_label_name(b"__name__").is_ok());
2291        assert!(unchecked.decode_label_name(b"label-name").is_err());
2292        assert!(unchecked.decode_label_name(b"0abc").is_err());
2293    }
2294
2295    #[test]
2296    fn test_is_valid_prom_label_name_bytes() {
2297        assert!(validate_label_name(b"__name__"));
2298        assert!(validate_label_name(b"job"));
2299        assert!(validate_label_name(b"_"));
2300        assert!(validate_label_name(b"A"));
2301        assert!(validate_label_name(b"abc123"));
2302        assert!(validate_label_name(b"_leading_underscore"));
2303
2304        assert!(!validate_label_name(b""));
2305        assert!(!validate_label_name(b"0starts_with_digit"));
2306        assert!(!validate_label_name(b"has-dash"));
2307        assert!(!validate_label_name(b"has.dot"));
2308        assert!(!validate_label_name(b"has space"));
2309        assert!(!validate_label_name(&[0xff, 0xfe]));
2310    }
2311}