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