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