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