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