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