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;
111mod workload_scheduler;
112
113use result::HttpOutputWriter;
114pub(crate) use timeout::DynamicTimeoutLayer;
115
116mod client_ip;
117use crate::prom_remote_write::validation::PromValidationMode;
118mod hints;
119mod read_preference;
120#[cfg(any(test, feature = "testing"))]
121pub mod test_helpers;
122
123pub const HTTP_API_VERSION: &str = "v1";
124pub const HTTP_API_PREFIX: &str = "/v1/";
125pub const HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH: &str = "/v1";
126
127pub trait ExtraHttpRouterProvider: Send + Sync {
129 fn router(&self) -> Router;
131}
132
133pub type ExtraHttpRouterProviderRef = Arc<dyn ExtraHttpRouterProvider>;
134
135#[derive(Clone, Default)]
137pub struct ExtraHttpRouterProviders {
138 providers: Vec<ExtraHttpRouterProviderRef>,
139}
140
141impl ExtraHttpRouterProviders {
142 pub fn new() -> Self {
144 Self::default()
145 }
146
147 pub fn add(&mut self, provider: ExtraHttpRouterProviderRef) {
149 self.providers.push(provider);
150 }
151
152 pub fn iter(&self) -> impl Iterator<Item = &dyn ExtraHttpRouterProvider> {
154 self.providers.iter().map(|x| x.as_ref())
155 }
156}
157
158const DEFAULT_BODY_LIMIT: ReadableSize = ReadableSize::mb(64);
160const DEFAULT_HTTP_API_ADDR_PORT: u16 = 4006;
162
163pub const AUTHORIZATION_HEADER: &str = "x-greptime-auth";
165
166pub static PUBLIC_API_PREFIX: [&str; 4] = [
169 "/v1/influxdb/ping",
170 "/v1/influxdb/health",
171 "/v1/health",
172 "/v1/splunk/services/collector/health",
173];
174
175#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
181pub(crate) enum HttpServerKind {
182 #[default]
185 Full,
186 Api,
188}
189
190#[derive(Default)]
191pub struct HttpServer {
192 router: StdMutex<Router>,
193 shutdown_tx: Mutex<Option<Sender<()>>>,
194 user_provider: Option<UserProviderRef>,
195 memory_limiter: ServerMemoryLimiter,
196
197 options: HttpOptions,
199 bind_addr: Option<SocketAddr>,
200 kind: HttpServerKind,
202}
203
204pub(crate) fn is_namespace(path: &str, root: &str) -> bool {
208 path == root
209 || path
210 .strip_prefix(root)
211 .is_some_and(|suffix| suffix.starts_with('/'))
212}
213
214pub fn is_api_listener_path(path: &str) -> bool {
216 is_namespace(path, HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH) || is_namespace(path, "/dashboard")
217}
218
219async fn enforce_api_surface(req: Request, next: Next) -> Response {
223 if !is_api_listener_path(req.uri().path()) {
224 return HttpStatusCode::NOT_FOUND.into_response();
225 }
226 next.run(req).await
227}
228
229impl HttpServer {
230 fn kind(&self) -> &'static str {
232 match self.kind {
233 HttpServerKind::Api => "HTTP API",
234 HttpServerKind::Full => "HTTP",
235 }
236 }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(default)]
241pub struct HttpOptions {
242 pub addr: String,
243
244 #[serde(with = "humantime_serde")]
245 pub timeout: Duration,
246
247 #[serde(skip)]
248 pub disable_dashboard: bool,
249
250 pub body_limit: ReadableSize,
251
252 pub cors_allowed_origins: Vec<String>,
253
254 pub enable_cors: bool,
255
256 pub enable_api_server: bool,
261 pub api_server_addr: String,
264}
265
266impl Default for HttpOptions {
267 fn default() -> Self {
268 Self {
269 addr: "127.0.0.1:4000".to_string(),
270 timeout: Duration::from_secs(0),
271 disable_dashboard: false,
272 body_limit: DEFAULT_BODY_LIMIT,
273 cors_allowed_origins: Vec::new(),
274 enable_cors: true,
275 enable_api_server: false,
276 api_server_addr: format!("127.0.0.1:{}", DEFAULT_HTTP_API_ADDR_PORT),
277 }
278 }
279}
280
281#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
282pub struct ColumnSchema {
283 name: String,
284 data_type: String,
285}
286
287impl ColumnSchema {
288 pub fn new(name: String, data_type: String) -> ColumnSchema {
289 ColumnSchema { name, data_type }
290 }
291}
292
293#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
294pub struct OutputSchema {
295 column_schemas: Vec<ColumnSchema>,
296}
297
298impl OutputSchema {
299 pub fn new(columns: Vec<ColumnSchema>) -> OutputSchema {
300 OutputSchema {
301 column_schemas: columns,
302 }
303 }
304}
305
306impl From<SchemaRef> for OutputSchema {
307 fn from(schema: SchemaRef) -> OutputSchema {
308 OutputSchema {
309 column_schemas: schema
310 .column_schemas()
311 .iter()
312 .map(|cs| ColumnSchema {
313 name: cs.name.clone(),
314 data_type: cs.data_type.name(),
315 })
316 .collect(),
317 }
318 }
319}
320
321#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
322pub struct HttpRecordsOutput {
323 schema: OutputSchema,
324 rows: Vec<Vec<Value>>,
325 #[serde(default)]
328 total_rows: usize,
329
330 #[serde(skip_serializing_if = "HashMap::is_empty")]
332 #[serde(default)]
333 metrics: HashMap<String, Value>,
334}
335
336impl HttpRecordsOutput {
337 pub fn num_rows(&self) -> usize {
338 self.rows.len()
339 }
340
341 pub fn num_cols(&self) -> usize {
342 self.schema.column_schemas.len()
343 }
344
345 pub fn schema(&self) -> &OutputSchema {
346 &self.schema
347 }
348
349 pub fn rows(&self) -> &Vec<Vec<Value>> {
350 &self.rows
351 }
352}
353
354impl HttpRecordsOutput {
355 pub fn try_new(
356 schema: SchemaRef,
357 recordbatches: Vec<RecordBatch>,
358 ) -> std::result::Result<HttpRecordsOutput, Error> {
359 if recordbatches.is_empty() {
360 Ok(HttpRecordsOutput {
361 schema: OutputSchema::from(schema),
362 rows: vec![],
363 total_rows: 0,
364 metrics: Default::default(),
365 })
366 } else {
367 let num_rows = recordbatches.iter().map(|r| r.num_rows()).sum::<usize>();
368 let mut rows = Vec::with_capacity(num_rows);
369
370 for recordbatch in recordbatches {
371 let mut writer = HttpOutputWriter::new(schema.num_columns(), None);
372 writer.write(recordbatch, &mut rows)?;
373 }
374
375 Ok(HttpRecordsOutput {
376 schema: OutputSchema::from(schema),
377 total_rows: rows.len(),
378 rows,
379 metrics: Default::default(),
380 })
381 }
382 }
383}
384
385#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
386#[serde(rename_all = "lowercase")]
387pub enum GreptimeQueryOutput {
388 AffectedRows(usize),
389 Records(HttpRecordsOutput),
390}
391
392#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
394pub enum ResponseFormat {
395 Arrow,
396 Csv(bool, bool),
398 Table,
399 #[default]
400 GreptimedbV1,
401 InfluxdbV1,
402 Json,
403 Null,
404}
405
406impl ResponseFormat {
407 pub fn parse(s: &str) -> Option<Self> {
408 match s {
409 "arrow" => Some(ResponseFormat::Arrow),
410 "csv" => Some(ResponseFormat::Csv(false, false)),
411 "csvwithnames" => Some(ResponseFormat::Csv(true, false)),
412 "csvwithnamesandtypes" => Some(ResponseFormat::Csv(true, true)),
413 "table" => Some(ResponseFormat::Table),
414 "greptimedb_v1" => Some(ResponseFormat::GreptimedbV1),
415 "influxdb_v1" => Some(ResponseFormat::InfluxdbV1),
416 "json" => Some(ResponseFormat::Json),
417 "null" => Some(ResponseFormat::Null),
418 _ => None,
419 }
420 }
421
422 pub fn as_str(&self) -> &'static str {
423 match self {
424 ResponseFormat::Arrow => "arrow",
425 ResponseFormat::Csv(_, _) => "csv",
426 ResponseFormat::Table => "table",
427 ResponseFormat::GreptimedbV1 => "greptimedb_v1",
428 ResponseFormat::InfluxdbV1 => "influxdb_v1",
429 ResponseFormat::Json => "json",
430 ResponseFormat::Null => "null",
431 }
432 }
433}
434
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436pub enum Epoch {
437 Nanosecond,
438 Microsecond,
439 Millisecond,
440 Second,
441}
442
443impl Epoch {
444 pub fn parse(s: &str) -> Option<Epoch> {
445 match s {
450 "ns" => Some(Epoch::Nanosecond),
451 "u" | "µ" => Some(Epoch::Microsecond),
452 "ms" => Some(Epoch::Millisecond),
453 "s" => Some(Epoch::Second),
454 _ => None, }
456 }
457
458 pub fn convert_timestamp(&self, ts: Timestamp) -> Option<Timestamp> {
459 match self {
460 Epoch::Nanosecond => ts.convert_to(TimeUnit::Nanosecond),
461 Epoch::Microsecond => ts.convert_to(TimeUnit::Microsecond),
462 Epoch::Millisecond => ts.convert_to(TimeUnit::Millisecond),
463 Epoch::Second => ts.convert_to(TimeUnit::Second),
464 }
465 }
466}
467
468impl Display for Epoch {
469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470 match self {
471 Epoch::Nanosecond => write!(f, "Epoch::Nanosecond"),
472 Epoch::Microsecond => write!(f, "Epoch::Microsecond"),
473 Epoch::Millisecond => write!(f, "Epoch::Millisecond"),
474 Epoch::Second => write!(f, "Epoch::Second"),
475 }
476 }
477}
478
479#[derive(Serialize, Deserialize, Debug)]
480pub enum HttpResponse {
481 Arrow(ArrowResponse),
482 Csv(CsvResponse),
483 Table(TableResponse),
484 Error(ErrorResponse),
485 GreptimedbV1(GreptimedbV1Response),
486 InfluxdbV1(InfluxdbV1Response),
487 Json(JsonResponse),
488 Null(NullResponse),
489}
490
491impl HttpResponse {
492 pub fn with_execution_time(self, execution_time: u64) -> Self {
493 match self {
494 HttpResponse::Arrow(resp) => resp.with_execution_time(execution_time).into(),
495 HttpResponse::Csv(resp) => resp.with_execution_time(execution_time).into(),
496 HttpResponse::Table(resp) => resp.with_execution_time(execution_time).into(),
497 HttpResponse::GreptimedbV1(resp) => resp.with_execution_time(execution_time).into(),
498 HttpResponse::InfluxdbV1(resp) => resp.with_execution_time(execution_time).into(),
499 HttpResponse::Json(resp) => resp.with_execution_time(execution_time).into(),
500 HttpResponse::Null(resp) => resp.with_execution_time(execution_time).into(),
501 HttpResponse::Error(resp) => resp.with_execution_time(execution_time).into(),
502 }
503 }
504
505 pub fn with_limit(self, limit: usize) -> Self {
506 match self {
507 HttpResponse::Csv(resp) => resp.with_limit(limit).into(),
508 HttpResponse::Table(resp) => resp.with_limit(limit).into(),
509 HttpResponse::GreptimedbV1(resp) => resp.with_limit(limit).into(),
510 HttpResponse::Json(resp) => resp.with_limit(limit).into(),
511 _ => self,
512 }
513 }
514}
515
516pub fn process_with_limit(
517 mut outputs: Vec<GreptimeQueryOutput>,
518 limit: usize,
519) -> Vec<GreptimeQueryOutput> {
520 outputs
521 .drain(..)
522 .map(|data| match data {
523 GreptimeQueryOutput::Records(mut records) => {
524 if records.rows.len() > limit {
525 records.rows.truncate(limit);
526 records.total_rows = limit;
527 }
528 GreptimeQueryOutput::Records(records)
529 }
530 _ => data,
531 })
532 .collect()
533}
534
535impl IntoResponse for HttpResponse {
536 fn into_response(self) -> Response {
537 match self {
538 HttpResponse::Arrow(resp) => resp.into_response(),
539 HttpResponse::Csv(resp) => resp.into_response(),
540 HttpResponse::Table(resp) => resp.into_response(),
541 HttpResponse::GreptimedbV1(resp) => resp.into_response(),
542 HttpResponse::InfluxdbV1(resp) => resp.into_response(),
543 HttpResponse::Json(resp) => resp.into_response(),
544 HttpResponse::Null(resp) => resp.into_response(),
545 HttpResponse::Error(resp) => resp.into_response(),
546 }
547 }
548}
549
550impl From<ArrowResponse> for HttpResponse {
551 fn from(value: ArrowResponse) -> Self {
552 HttpResponse::Arrow(value)
553 }
554}
555
556impl From<CsvResponse> for HttpResponse {
557 fn from(value: CsvResponse) -> Self {
558 HttpResponse::Csv(value)
559 }
560}
561
562impl From<TableResponse> for HttpResponse {
563 fn from(value: TableResponse) -> Self {
564 HttpResponse::Table(value)
565 }
566}
567
568impl From<ErrorResponse> for HttpResponse {
569 fn from(value: ErrorResponse) -> Self {
570 HttpResponse::Error(value)
571 }
572}
573
574impl From<GreptimedbV1Response> for HttpResponse {
575 fn from(value: GreptimedbV1Response) -> Self {
576 HttpResponse::GreptimedbV1(value)
577 }
578}
579
580impl From<InfluxdbV1Response> for HttpResponse {
581 fn from(value: InfluxdbV1Response) -> Self {
582 HttpResponse::InfluxdbV1(value)
583 }
584}
585
586impl From<JsonResponse> for HttpResponse {
587 fn from(value: JsonResponse) -> Self {
588 HttpResponse::Json(value)
589 }
590}
591
592impl From<NullResponse> for HttpResponse {
593 fn from(value: NullResponse) -> Self {
594 HttpResponse::Null(value)
595 }
596}
597
598#[derive(Clone)]
599pub struct ApiState {
600 pub sql_handler: ServerSqlQueryHandlerRef,
601}
602
603#[derive(Clone)]
604pub struct GreptimeOptionsConfigState {
605 pub greptime_config_options: String,
606}
607
608#[derive(Clone)]
609pub struct DashboardState {
610 pub handler: DashboardHandlerRef,
611}
612
613pub struct HttpServerBuilder {
614 options: HttpOptions,
615 user_provider: Option<UserProviderRef>,
616 router: Router,
617 memory_limiter: ServerMemoryLimiter,
618}
619
620impl HttpServerBuilder {
621 pub fn new(options: HttpOptions) -> Self {
622 Self {
623 options,
624 user_provider: None,
625 router: Router::new(),
626 memory_limiter: ServerMemoryLimiter::default(),
627 }
628 }
629
630 pub fn with_memory_limiter(mut self, limiter: ServerMemoryLimiter) -> Self {
632 self.memory_limiter = limiter;
633 self
634 }
635
636 pub fn with_sql_handler(self, sql_handler: ServerSqlQueryHandlerRef) -> Self {
637 let sql_router = HttpServer::route_sql(ApiState { sql_handler });
638
639 Self {
640 router: self
641 .router
642 .nest(&format!("/{HTTP_API_VERSION}"), sql_router),
643 ..self
644 }
645 }
646
647 pub fn with_logs_handler(self, logs_handler: LogQueryHandlerRef) -> Self {
648 let logs_router = HttpServer::route_logs(logs_handler);
649
650 Self {
651 router: self
652 .router
653 .nest(&format!("/{HTTP_API_VERSION}"), logs_router),
654 ..self
655 }
656 }
657
658 pub fn with_opentsdb_handler(self, handler: OpentsdbProtocolHandlerRef) -> Self {
659 Self {
660 router: self.router.nest(
661 &format!("/{HTTP_API_VERSION}/opentsdb"),
662 HttpServer::route_opentsdb(handler),
663 ),
664 ..self
665 }
666 }
667
668 pub fn with_influxdb_handler(self, handler: InfluxdbLineProtocolHandlerRef) -> Self {
669 Self {
670 router: self.router.nest(
671 &format!("/{HTTP_API_VERSION}/influxdb"),
672 HttpServer::route_influxdb(handler),
673 ),
674 ..self
675 }
676 }
677
678 pub fn with_prom_handler(
679 self,
680 handler: PromStoreProtocolHandlerRef,
681 pipeline_handler: Option<PipelineHandlerRef>,
682 prom_store_with_metric_engine: bool,
683 prom_validation_mode: PromValidationMode,
684 experimental_enable_prometheus_native_histogram: bool,
685 pending_rows_batcher: Option<Arc<PendingRowsBatcher>>,
686 ) -> Self {
687 let state = PromStoreState {
688 prom_store_handler: handler,
689 pipeline_handler,
690 prom_store_with_metric_engine,
691 prom_validation_mode,
692 experimental_enable_prometheus_native_histogram,
693 pending_rows_batcher,
694 };
695
696 Self {
697 router: self.router.nest(
698 &format!("/{HTTP_API_VERSION}/prometheus"),
699 HttpServer::route_prom(state),
700 ),
701 ..self
702 }
703 }
704
705 pub fn with_prometheus_handler(self, handler: PrometheusHandlerRef) -> Self {
706 Self {
707 router: self.router.nest(
708 &format!("/{HTTP_API_VERSION}/prometheus/api/v1"),
709 HttpServer::route_prometheus(handler),
710 ),
711 ..self
712 }
713 }
714
715 pub fn with_otlp_handler(
716 self,
717 handler: OpenTelemetryProtocolHandlerRef,
718 with_metric_engine: bool,
719 experimental_enable_exponential_histogram: bool,
720 ) -> Self {
721 Self {
722 router: self.router.nest(
723 &format!("/{HTTP_API_VERSION}/otlp"),
724 HttpServer::route_otlp(
725 handler,
726 with_metric_engine,
727 experimental_enable_exponential_histogram,
728 ),
729 ),
730 ..self
731 }
732 }
733
734 pub fn with_user_provider(self, user_provider: UserProviderRef) -> Self {
735 Self {
736 user_provider: Some(user_provider),
737 ..self
738 }
739 }
740
741 pub fn with_metrics_handler(self, handler: MetricsHandler) -> Self {
742 Self {
743 router: self.router.merge(HttpServer::route_metrics(handler)),
744 ..self
745 }
746 }
747
748 pub fn with_log_ingest_handler(
749 self,
750 handler: PipelineHandlerRef,
751 validator: Option<LogValidatorRef>,
752 ingest_interceptor: Option<LogIngestInterceptorRef<Error>>,
753 ) -> Self {
754 let log_state = LogState {
755 log_handler: handler,
756 log_validator: validator,
757 ingest_interceptor,
758 };
759
760 let router = self.router.nest(
761 &format!("/{HTTP_API_VERSION}"),
762 HttpServer::route_pipelines(log_state.clone()),
763 );
764 let router = router.nest(
766 &format!("/{HTTP_API_VERSION}/events"),
767 #[allow(deprecated)]
768 HttpServer::route_log_deprecated(log_state.clone()),
769 );
770
771 let router = router.nest(
772 &format!("/{HTTP_API_VERSION}/loki"),
773 HttpServer::route_loki(log_state.clone()),
774 );
775
776 let router = router.nest(
777 &format!("/{HTTP_API_VERSION}/elasticsearch"),
778 HttpServer::route_elasticsearch(log_state.clone()),
779 );
780
781 let router = router.nest(
782 &format!("/{HTTP_API_VERSION}/elasticsearch/"),
783 Router::new()
784 .route("/", routing::get(elasticsearch::handle_get_version))
785 .with_state(log_state.clone()),
786 );
787
788 let router = router.nest(
789 &format!("/{HTTP_API_VERSION}/splunk"),
790 HttpServer::route_splunk(log_state),
791 );
792
793 Self { router, ..self }
794 }
795
796 pub fn with_greptime_config_options(self, opts: String) -> Self {
797 let config_router = HttpServer::route_config(GreptimeOptionsConfigState {
798 greptime_config_options: opts,
799 });
800
801 Self {
802 router: self.router.merge(config_router),
803 ..self
804 }
805 }
806
807 pub fn with_jaeger_handler(self, handler: JaegerQueryHandlerRef) -> Self {
808 Self {
809 router: self.router.nest(
810 &format!("/{HTTP_API_VERSION}/jaeger"),
811 HttpServer::route_jaeger(handler),
812 ),
813 ..self
814 }
815 }
816
817 pub fn with_dashboard_handler(self, handler: DashboardHandlerRef) -> Self {
818 Self {
819 router: self.router.nest(
820 &format!("/{HTTP_API_VERSION}/dashboards"),
821 HttpServer::route_dashboard(handler),
822 ),
823 ..self
824 }
825 }
826
827 pub fn with_extra_router(self, router: Router) -> Self {
828 Self {
829 router: self.router.merge(router),
830 ..self
831 }
832 }
833
834 pub fn add_layer<L>(self, layer: L) -> Self
835 where
836 L: Layer<Route> + Clone + Send + Sync + 'static,
837 L::Service: Service<Request> + Clone + Send + Sync + 'static,
838 <L::Service as Service<Request>>::Response: IntoResponse + 'static,
839 <L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
840 <L::Service as Service<Request>>::Future: Send + 'static,
841 {
842 Self {
843 router: self.router.layer(layer),
844 ..self
845 }
846 }
847
848 pub fn build(self) -> HttpServer {
849 HttpServer {
850 options: self.options,
851 user_provider: self.user_provider,
852 shutdown_tx: Mutex::new(None),
853 router: StdMutex::new(self.router),
854 bind_addr: None,
855 memory_limiter: self.memory_limiter,
856 kind: HttpServerKind::Full,
857 }
858 }
859
860 pub fn build_servers(self) -> (HttpServer, Option<HttpServer>) {
879 let api_enabled = self.options.enable_api_server;
880 let api_addr = self.options.api_server_addr.clone();
881
882 let internal = HttpServer {
883 options: self.options,
884 user_provider: self.user_provider.clone(),
885 shutdown_tx: Mutex::new(None),
886 router: StdMutex::new(self.router.clone()),
887 bind_addr: None,
888 memory_limiter: self.memory_limiter.clone(),
889 kind: HttpServerKind::Full,
890 };
891
892 let api = if api_enabled {
893 let api_options = HttpOptions {
896 addr: api_addr,
897 ..internal.options.clone()
898 };
899 Some(HttpServer {
900 options: api_options,
901 user_provider: self.user_provider.clone(),
902 shutdown_tx: Mutex::new(None),
903 router: StdMutex::new(self.router),
904 bind_addr: None,
905 memory_limiter: self.memory_limiter,
906 kind: HttpServerKind::Api,
907 })
908 } else {
909 None
910 };
911
912 (internal, api)
913 }
914}
915
916impl HttpServer {
917 pub fn make_app(&self) -> Router {
927 let mut router = self.router.lock().unwrap().clone();
928
929 router = router
930 .route(
931 &format!("/{HTTP_API_VERSION}/health"),
932 routing::get(handler::health).post(handler::health),
933 )
934 .route("/", routing::get(handler::index))
935 .route(
936 "/health",
937 routing::get(handler::health).post(handler::health),
938 )
939 .route(
940 "/ready",
941 routing::get(handler::health).post(handler::health),
942 )
943 .route("/status", routing::get(handler::status));
944
945 #[cfg(feature = "dashboard")]
948 {
949 if !self.options.disable_dashboard {
950 info!("Enable dashboard service at '/dashboard'");
951 router = router.route(
953 "/dashboard",
954 routing::get(|uri: axum::http::uri::Uri| async move {
955 let path = uri.path();
956 let query = uri.query().map(|q| format!("?{}", q)).unwrap_or_default();
957
958 let new_uri = format!("{}/{}", path, query);
959 axum::response::Redirect::permanent(&new_uri)
960 }),
961 );
962
963 router = router
967 .route(
968 "/dashboard/",
969 routing::get(dashboard::static_handler).post(dashboard::static_handler),
970 )
971 .route(
972 "/dashboard/{*x}",
973 routing::get(dashboard::static_handler).post(dashboard::static_handler),
974 );
975 }
976 }
977
978 router = router.route_layer(middleware::from_fn(http_metrics_layer));
980
981 router
982 }
983
984 pub fn build(&self, router: Router) -> Result<Router> {
987 let timeout_layer = if self.options.timeout != Duration::default() {
988 Some(
989 ServiceBuilder::new().layer(
990 DynamicTimeoutLayer::new(self.options.timeout)
991 .with_status_code_fn(Self::request_timeout_status_code),
992 ),
993 )
994 } else {
995 info!("HTTP server timeout is disabled");
996 None
997 };
998 let body_limit_layer = if self.options.body_limit != ReadableSize(0) {
999 Some(
1000 ServiceBuilder::new()
1001 .layer(DefaultBodyLimit::max(self.options.body_limit.0 as usize)),
1002 )
1003 } else {
1004 info!("HTTP server body limit is disabled");
1005 None
1006 };
1007 let cors_layer = if self.options.enable_cors {
1008 Some(
1009 CorsLayer::new()
1010 .allow_methods([
1011 Method::GET,
1012 Method::POST,
1013 Method::PUT,
1014 Method::DELETE,
1015 Method::HEAD,
1016 ])
1017 .allow_origin(if self.options.cors_allowed_origins.is_empty() {
1018 AllowOrigin::from(Any)
1019 } else {
1020 AllowOrigin::from(
1021 self.options
1022 .cors_allowed_origins
1023 .iter()
1024 .map(|s| {
1025 HeaderValue::from_str(s.as_str())
1026 .context(InvalidHeaderValueSnafu)
1027 })
1028 .collect::<Result<Vec<HeaderValue>>>()?,
1029 )
1030 })
1031 .allow_headers(Any),
1032 )
1033 } else {
1034 info!("HTTP server cross-origin is disabled");
1035 None
1036 };
1037
1038 let router = router
1039 .layer(
1041 ServiceBuilder::new()
1042 .layer(TraceLayer::new_for_http().on_failure(()))
1045 .option_layer(cors_layer)
1046 .option_layer(timeout_layer)
1047 .option_layer(body_limit_layer)
1048 .layer(middleware::from_fn_with_state(
1050 self.memory_limiter.clone(),
1051 memory_limit::memory_limit_middleware,
1052 ))
1053 .layer(middleware::from_fn_with_state(
1055 AuthState::new(self.user_provider.clone()),
1056 authorize::check_http_auth,
1057 ))
1058 .layer(middleware::from_fn(hints::extract_hints))
1059 .layer(middleware::from_fn(client_ip::log_error_with_client_ip))
1060 .layer(middleware::from_fn(
1061 read_preference::extract_read_preference,
1062 )),
1063 );
1064
1065 let router = router.nest(
1068 "/debug",
1069 Router::new()
1070 .route("/log_level", routing::post(dyn_log::dyn_log_handler))
1072 .route(
1073 "/workload_scheduler",
1074 routing::get(workload_scheduler::get_status_handler),
1075 )
1076 .route(
1077 "/workload_scheduler/enabled",
1078 routing::post(workload_scheduler::set_enabled_handler),
1079 )
1080 .route(
1081 "/workload_scheduler/weights",
1082 routing::post(workload_scheduler::set_weights_handler),
1083 )
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 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 .route(
1324 "/sql/analyze/stream",
1325 routing::post(handler::sql_analyze_stream),
1326 )
1327 .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 fn make_split_builder() -> HttpServerBuilder {
1643 let (tx, _rx) = mpsc::channel(100);
1644 let instance = Arc::new(DummyInstance { _tx: tx });
1645 let options = HttpOptions {
1647 enable_api_server: true,
1648 ..HttpOptions::default()
1649 };
1650 HttpServerBuilder::new(options)
1651 .with_sql_handler(instance)
1652 .with_metrics_handler(MetricsHandler)
1653 .with_greptime_config_options("dummy = \"value\"".to_string())
1654 }
1655
1656 #[tokio::test]
1657 pub async fn test_http_api_options_defaults() {
1658 let opts = HttpOptions::default();
1659 assert!(!opts.enable_api_server);
1661 assert_eq!(opts.api_server_addr, "127.0.0.1:4006");
1662 }
1663
1664 #[tokio::test]
1665 pub async fn test_build_serves_all_routes() {
1666 let server = make_split_builder().build();
1670 assert_eq!(server.name(), HTTP_SERVER);
1671
1672 let app = server.build(server.make_app()).unwrap();
1673 let client = TestClient::new(app).await;
1674
1675 for path in ["/v1/health", "/health", "/status", "/metrics", "/config"] {
1676 let res = client.get(path).send().await;
1677 assert_eq!(
1678 res.status(),
1679 StatusCode::OK,
1680 "internal/full server should serve {path}"
1681 );
1682 }
1683 }
1684
1685 #[tokio::test]
1686 pub async fn test_build_servers_separates_api_and_internal_routes() {
1687 let (internal, api) = make_split_builder().build_servers();
1690 let api = api.expect("API server is explicitly enabled in make_split_builder");
1691 assert_eq!(internal.name(), HTTP_SERVER);
1692 assert_eq!(api.name(), HTTP_API_SERVER);
1693
1694 let internal_app = internal.build(internal.make_app()).unwrap();
1695 let api_app = api.build(api.make_app()).unwrap();
1696
1697 let internal_client = TestClient::new(internal_app).await;
1698 let api_client = TestClient::new(api_app).await;
1699
1700 assert_eq!(
1702 internal_client.get("/v1/health").send().await.status(),
1703 StatusCode::OK
1704 );
1705 assert_eq!(
1706 api_client.get("/v1/health").send().await.status(),
1707 StatusCode::OK
1708 );
1709
1710 for path in ["/health", "/status", "/metrics", "/config"] {
1712 assert_eq!(
1713 internal_client.get(path).send().await.status(),
1714 StatusCode::OK,
1715 "internal/full server should serve {path}"
1716 );
1717 assert_eq!(
1719 api_client.get(path).send().await.status(),
1720 StatusCode::NOT_FOUND,
1721 "API server should NOT serve {path}"
1722 );
1723 }
1724 }
1725
1726 #[test]
1727 fn test_build_servers_api_inherits_http_options() {
1728 let http_opts = HttpOptions {
1730 timeout: Duration::from_secs(42),
1731 body_limit: ReadableSize::mb(128),
1732 cors_allowed_origins: vec!["https://example.com".to_string()],
1733 enable_api_server: true,
1734 ..HttpOptions::default()
1735 };
1736 let (internal, api) = HttpServerBuilder::new(http_opts.clone()).build_servers();
1737 let api = api.expect("API server is explicitly enabled");
1738
1739 assert_eq!(internal.options.addr, http_opts.addr);
1741 assert_eq!(api.options.addr, "127.0.0.1:4006");
1742 assert_eq!(api.options.timeout, http_opts.timeout);
1744 assert_eq!(api.options.body_limit, http_opts.body_limit);
1745 assert_eq!(
1746 api.options.cors_allowed_origins,
1747 http_opts.cors_allowed_origins
1748 );
1749 }
1750
1751 #[test]
1752 fn test_is_api_listener_path() {
1753 assert!(is_api_listener_path("/v1"));
1755 assert!(is_api_listener_path("/v1/"));
1756 assert!(is_api_listener_path("/v1/sql"));
1757 assert!(!is_api_listener_path("/v10/sql"));
1758 assert!(!is_api_listener_path("/v1-internal"));
1759 assert!(is_api_listener_path("/dashboard"));
1761 assert!(is_api_listener_path("/dashboard/app.js"));
1762 assert!(!is_api_listener_path("/dashboard-admin"));
1763 assert!(!is_api_listener_path("/metrics"));
1765 assert!(!is_api_listener_path("/status/plugin"));
1766 assert!(!is_api_listener_path("/health"));
1767 }
1768
1769 #[tokio::test]
1770 pub async fn test_extra_router_respects_api_surface() {
1771 let (tx, _rx) = mpsc::channel(100);
1777 let instance = Arc::new(DummyInstance { _tx: tx });
1778 let options = HttpOptions {
1779 enable_api_server: true,
1780 ..HttpOptions::default()
1781 };
1782 let builder = HttpServerBuilder::new(options)
1783 .with_sql_handler(instance)
1784 .with_extra_router(
1785 Router::new()
1786 .route("/status/plugin", routing::get(|| async { "ok" }))
1787 .route("/v1/plugin", routing::get(|| async { "ok" })),
1788 );
1789 let (full, api) = builder.build_servers();
1790 let api = api.expect("API server is explicitly enabled");
1791
1792 let full_client = TestClient::new(full.build(full.make_app()).unwrap()).await;
1793 let api_client = TestClient::new(api.build(api.make_app()).unwrap()).await;
1794
1795 assert_eq!(
1797 full_client.get("/status/plugin").send().await.status(),
1798 StatusCode::OK
1799 );
1800 assert_eq!(
1802 api_client.get("/status/plugin").send().await.status(),
1803 StatusCode::NOT_FOUND
1804 );
1805
1806 assert_eq!(
1808 full_client.get("/v1/plugin").send().await.status(),
1809 StatusCode::OK
1810 );
1811 assert_eq!(
1812 api_client.get("/v1/plugin").send().await.status(),
1813 StatusCode::OK
1814 );
1815
1816 assert_eq!(
1818 api_client.get("/v10/plugin").send().await.status(),
1819 StatusCode::NOT_FOUND
1820 );
1821 }
1822
1823 #[test]
1824 fn test_build_servers_api_disabled_by_default() {
1825 let (_internal, api) = HttpServerBuilder::new(HttpOptions::default()).build_servers();
1828 assert!(api.is_none());
1829 }
1830
1831 #[tokio::test]
1832 pub async fn test_cors() {
1833 let (tx, _rx) = mpsc::channel(100);
1835 let app = make_test_app(tx);
1836 let client = TestClient::new(app).await;
1837
1838 let res = client.get("/health").send().await;
1839
1840 assert_eq!(res.status(), StatusCode::OK);
1841 assert_eq!(
1842 res.headers()
1843 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1844 .expect("expect cors header origin"),
1845 "*"
1846 );
1847
1848 let res = client.get("/v1/health").send().await;
1849
1850 assert_eq!(res.status(), StatusCode::OK);
1851 assert_eq!(
1852 res.headers()
1853 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1854 .expect("expect cors header origin"),
1855 "*"
1856 );
1857
1858 let res = client
1859 .options("/health")
1860 .header("Access-Control-Request-Headers", "x-greptime-auth")
1861 .header("Access-Control-Request-Method", "DELETE")
1862 .header("Origin", "https://example.com")
1863 .send()
1864 .await;
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 assert_eq!(
1873 res.headers()
1874 .get(http::header::ACCESS_CONTROL_ALLOW_HEADERS)
1875 .expect("expect cors header headers"),
1876 "*"
1877 );
1878 assert_eq!(
1879 res.headers()
1880 .get(http::header::ACCESS_CONTROL_ALLOW_METHODS)
1881 .expect("expect cors header methods"),
1882 "GET,POST,PUT,DELETE,HEAD"
1883 );
1884 }
1885
1886 #[tokio::test]
1887 pub async fn test_cors_custom_origins() {
1888 let (tx, _rx) = mpsc::channel(100);
1890 let origin = "https://example.com";
1891
1892 let options = HttpOptions {
1893 cors_allowed_origins: vec![origin.to_string()],
1894 ..Default::default()
1895 };
1896
1897 let app = make_test_app_custom(tx, options);
1898 let client = TestClient::new(app).await;
1899
1900 let res = client.get("/health").header("Origin", origin).send().await;
1901
1902 assert_eq!(res.status(), StatusCode::OK);
1903 assert_eq!(
1904 res.headers()
1905 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1906 .expect("expect cors header origin"),
1907 origin
1908 );
1909
1910 let res = client
1911 .get("/health")
1912 .header("Origin", "https://notallowed.com")
1913 .send()
1914 .await;
1915
1916 assert_eq!(res.status(), StatusCode::OK);
1917 assert!(
1918 !res.headers()
1919 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1920 );
1921 }
1922
1923 #[tokio::test]
1924 pub async fn test_cors_disabled() {
1925 let (tx, _rx) = mpsc::channel(100);
1927
1928 let options = HttpOptions {
1929 enable_cors: false,
1930 ..Default::default()
1931 };
1932
1933 let app = make_test_app_custom(tx, options);
1934 let client = TestClient::new(app).await;
1935
1936 let res = client.get("/health").send().await;
1937
1938 assert_eq!(res.status(), StatusCode::OK);
1939 assert!(
1940 !res.headers()
1941 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1942 );
1943 }
1944
1945 #[test]
1946 fn test_http_options_default() {
1947 let default = HttpOptions::default();
1948 assert_eq!("127.0.0.1:4000".to_string(), default.addr);
1949 assert_eq!(Duration::from_secs(0), default.timeout)
1950 }
1951
1952 #[tokio::test]
1953 async fn test_http_options_legacy_analyze_stream_config_is_ignored() {
1954 let options: HttpOptions = serde_json::from_value(serde_json::json!({
1955 "addr": "127.0.0.1:4000",
1956 "timeout": "0s",
1957 "body_limit": "64MiB",
1958 "cors_allowed_origins": [],
1959 "enable_cors": true,
1960 "experimental_enable_explain_analyze_stream": false,
1961 "enable_api_server": false,
1962 "api_server_addr": "127.0.0.1:4006"
1963 }))
1964 .unwrap();
1965 let serialized = serde_json::to_string(&options).unwrap();
1966 assert!(!serialized.contains("experimental_enable_explain_analyze_stream"));
1967
1968 let (tx, _rx) = mpsc::channel(100);
1969 let app = make_test_app_custom(tx, options);
1970 let client = TestClient::new(app).await;
1971 let response = client
1972 .post("/v1/sql/analyze/stream")
1973 .form(&handler::SqlQuery {
1974 sql: Some("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
1975 ..Default::default()
1976 })
1977 .send()
1978 .await;
1979 assert_ne!(response.status(), StatusCode::NOT_FOUND);
1980 }
1981
1982 #[tokio::test]
1983 async fn test_http_server_request_timeout() {
1984 common_telemetry::init_default_ut_logging();
1985
1986 let (tx, _rx) = mpsc::channel(100);
1987 let options = HttpOptions {
1988 timeout: Duration::from_millis(10),
1989 ..Default::default()
1990 };
1991 let app = make_test_app_custom(tx, options);
1992 let client = TestClient::new(app).await;
1993 let res = client.get("/test/timeout").send().await;
1994 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
1995
1996 let res = client.post("/v1/prometheus/write").send().await;
1997 assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
1998
1999 let now = Instant::now();
2000 let res = client
2001 .get("/test/timeout")
2002 .header(GREPTIME_DB_HEADER_TIMEOUT, "20ms")
2003 .send()
2004 .await;
2005 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
2006 let elapsed = now.elapsed();
2007 assert!(elapsed > Duration::from_millis(15));
2008
2009 tokio::time::timeout(
2010 Duration::from_millis(15),
2011 client
2012 .get("/test/timeout")
2013 .header(GREPTIME_DB_HEADER_TIMEOUT, "0s")
2014 .send(),
2015 )
2016 .await
2017 .unwrap_err();
2018
2019 tokio::time::timeout(
2020 Duration::from_millis(15),
2021 client
2022 .get("/test/timeout")
2023 .header(
2024 GREPTIME_DB_HEADER_TIMEOUT,
2025 humantime::format_duration(Duration::default()).to_string(),
2026 )
2027 .send(),
2028 )
2029 .await
2030 .unwrap_err();
2031 }
2032
2033 #[tokio::test]
2034 async fn test_schema_for_empty_response() {
2035 let column_schemas = vec![
2036 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2037 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2038 ];
2039 let schema = Arc::new(Schema::new(column_schemas));
2040
2041 let recordbatches = RecordBatches::try_new(schema.clone(), vec![]).unwrap();
2042 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2043
2044 let json_resp = GreptimedbV1Response::from_output(outputs).await;
2045 if let HttpResponse::GreptimedbV1(json_resp) = json_resp {
2046 let json_output = &json_resp.output[0];
2047 if let GreptimeQueryOutput::Records(r) = json_output {
2048 assert_eq!(r.num_rows(), 0);
2049 assert_eq!(r.num_cols(), 2);
2050 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2051 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2052 } else {
2053 panic!("invalid output type");
2054 }
2055 } else {
2056 panic!("invalid format")
2057 }
2058 }
2059
2060 #[tokio::test]
2061 async fn test_recordbatches_conversion() {
2062 let column_schemas = vec![
2063 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2064 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2065 ];
2066 let schema = Arc::new(Schema::new(column_schemas));
2067 let columns: Vec<VectorRef> = vec![
2068 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
2069 Arc::new(StringVector::from(vec![
2070 None,
2071 Some("hello"),
2072 Some("greptime"),
2073 None,
2074 ])),
2075 ];
2076 let recordbatch = RecordBatch::new(schema.clone(), columns).unwrap();
2077
2078 for format in [
2079 ResponseFormat::GreptimedbV1,
2080 ResponseFormat::InfluxdbV1,
2081 ResponseFormat::Csv(true, true),
2082 ResponseFormat::Table,
2083 ResponseFormat::Arrow,
2084 ResponseFormat::Json,
2085 ResponseFormat::Null,
2086 ] {
2087 let recordbatches =
2088 RecordBatches::try_new(schema.clone(), vec![recordbatch.clone()]).unwrap();
2089 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2090 let json_resp = match format {
2091 ResponseFormat::Arrow => ArrowResponse::from_output(outputs, None).await,
2092 ResponseFormat::Csv(with_names, with_types) => {
2093 CsvResponse::from_output(outputs, with_names, with_types).await
2094 }
2095 ResponseFormat::Table => TableResponse::from_output(outputs).await,
2096 ResponseFormat::GreptimedbV1 => GreptimedbV1Response::from_output(outputs).await,
2097 ResponseFormat::InfluxdbV1 => InfluxdbV1Response::from_output(outputs, None).await,
2098 ResponseFormat::Json => JsonResponse::from_output(outputs).await,
2099 ResponseFormat::Null => NullResponse::from_output(outputs).await,
2100 };
2101
2102 match json_resp {
2103 HttpResponse::GreptimedbV1(resp) => {
2104 let json_output = &resp.output[0];
2105 if let GreptimeQueryOutput::Records(r) = json_output {
2106 assert_eq!(r.num_rows(), 4);
2107 assert_eq!(r.num_cols(), 2);
2108 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2109 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2110 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2111 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2112 } else {
2113 panic!("invalid output type");
2114 }
2115 }
2116 HttpResponse::InfluxdbV1(resp) => {
2117 let json_output = &resp.results()[0];
2118 assert_eq!(json_output.num_rows(), 4);
2119 assert_eq!(json_output.num_cols(), 2);
2120 assert_eq!(json_output.series[0].columns.clone()[0], "numbers");
2121 assert_eq!(
2122 json_output.series[0].values[0][0],
2123 serde_json::Value::from(1)
2124 );
2125 assert_eq!(json_output.series[0].values[0][1], serde_json::Value::Null);
2126 }
2127 HttpResponse::Csv(resp) => {
2128 let output = &resp.output()[0];
2129 if let GreptimeQueryOutput::Records(r) = output {
2130 assert_eq!(r.num_rows(), 4);
2131 assert_eq!(r.num_cols(), 2);
2132 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2133 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2134 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2135 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2136 } else {
2137 panic!("invalid output type");
2138 }
2139 }
2140
2141 HttpResponse::Table(resp) => {
2142 let output = &resp.output()[0];
2143 if let GreptimeQueryOutput::Records(r) = output {
2144 assert_eq!(r.num_rows(), 4);
2145 assert_eq!(r.num_cols(), 2);
2146 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2147 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2148 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2149 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2150 } else {
2151 panic!("invalid output type");
2152 }
2153 }
2154
2155 HttpResponse::Arrow(resp) => {
2156 let output = resp.data;
2157 let mut reader = StreamReader::try_new(Cursor::new(output), None)
2158 .expect("Arrow reader error");
2159 let schema = reader.schema();
2160 assert_eq!(schema.fields[0].name(), "numbers");
2161 assert_eq!(schema.fields[0].data_type(), &DataType::UInt32);
2162 assert_eq!(schema.fields[1].name(), "strings");
2163 assert_eq!(schema.fields[1].data_type(), &DataType::Utf8);
2164
2165 let rb = reader.next().unwrap().expect("read record batch failed");
2166 assert_eq!(rb.num_columns(), 2);
2167 assert_eq!(rb.num_rows(), 4);
2168 }
2169
2170 HttpResponse::Json(resp) => {
2171 let output = &resp.output()[0];
2172 if let GreptimeQueryOutput::Records(r) = output {
2173 assert_eq!(r.num_rows(), 4);
2174 assert_eq!(r.num_cols(), 2);
2175 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2176 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2177 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2178 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2179 } else {
2180 panic!("invalid output type");
2181 }
2182 }
2183
2184 HttpResponse::Null(resp) => {
2185 assert_eq!(resp.rows(), 4);
2186 }
2187
2188 HttpResponse::Error(err) => unreachable!("{err:?}"),
2189 }
2190 }
2191 }
2192
2193 #[test]
2194 fn test_response_format_misc() {
2195 assert_eq!(ResponseFormat::default(), ResponseFormat::GreptimedbV1);
2196 assert_eq!(ResponseFormat::parse("arrow"), Some(ResponseFormat::Arrow));
2197 assert_eq!(
2198 ResponseFormat::parse("csv"),
2199 Some(ResponseFormat::Csv(false, false))
2200 );
2201 assert_eq!(
2202 ResponseFormat::parse("csvwithnames"),
2203 Some(ResponseFormat::Csv(true, false))
2204 );
2205 assert_eq!(
2206 ResponseFormat::parse("csvwithnamesandtypes"),
2207 Some(ResponseFormat::Csv(true, true))
2208 );
2209 assert_eq!(ResponseFormat::parse("table"), Some(ResponseFormat::Table));
2210 assert_eq!(
2211 ResponseFormat::parse("greptimedb_v1"),
2212 Some(ResponseFormat::GreptimedbV1)
2213 );
2214 assert_eq!(
2215 ResponseFormat::parse("influxdb_v1"),
2216 Some(ResponseFormat::InfluxdbV1)
2217 );
2218 assert_eq!(ResponseFormat::parse("json"), Some(ResponseFormat::Json));
2219 assert_eq!(ResponseFormat::parse("null"), Some(ResponseFormat::Null));
2220
2221 assert_eq!(ResponseFormat::parse("invalid"), None);
2223 assert_eq!(ResponseFormat::parse(""), None);
2224 assert_eq!(ResponseFormat::parse("CSV"), None); assert_eq!(ResponseFormat::Arrow.as_str(), "arrow");
2228 assert_eq!(ResponseFormat::Csv(false, false).as_str(), "csv");
2229 assert_eq!(ResponseFormat::Csv(true, true).as_str(), "csv");
2230 assert_eq!(ResponseFormat::Table.as_str(), "table");
2231 assert_eq!(ResponseFormat::GreptimedbV1.as_str(), "greptimedb_v1");
2232 assert_eq!(ResponseFormat::InfluxdbV1.as_str(), "influxdb_v1");
2233 assert_eq!(ResponseFormat::Json.as_str(), "json");
2234 assert_eq!(ResponseFormat::Null.as_str(), "null");
2235 assert_eq!(ResponseFormat::default().as_str(), "greptimedb_v1");
2236 }
2237
2238 #[test]
2239 fn test_decode_label_name_strict() {
2240 let strict = PromValidationMode::Strict;
2241
2242 assert!(strict.decode_label_name(b"__name__").is_ok());
2244 assert!(strict.decode_label_name(b"job").is_ok());
2245 assert!(strict.decode_label_name(b"instance").is_ok());
2246 assert!(strict.decode_label_name(b"_private").is_ok());
2247 assert!(strict.decode_label_name(b"label_with_underscores").is_ok());
2248 assert!(strict.decode_label_name(b"abc123").is_ok());
2249 assert!(strict.decode_label_name(b"A").is_ok());
2250 assert!(strict.decode_label_name(b"_").is_ok());
2251
2252 assert!(strict.decode_label_name(b"0abc").is_err());
2254 assert!(strict.decode_label_name(b"123").is_err());
2255
2256 assert!(strict.decode_label_name(b"label-name").is_err());
2258 assert!(strict.decode_label_name(b"label.name").is_err());
2259 assert!(strict.decode_label_name(b"label name").is_err());
2260 assert!(strict.decode_label_name(b"label/name").is_err());
2261
2262 assert!(strict.decode_label_name(b"").is_err());
2264
2265 assert!(strict.decode_label_name("ラベル".as_bytes()).is_err());
2267
2268 assert!(strict.decode_label_name(&[0xff, 0xfe]).is_err());
2270 }
2271
2272 #[test]
2273 fn test_decode_label_name_lossy() {
2274 let lossy = PromValidationMode::Lossy;
2275
2276 assert!(lossy.decode_label_name(b"__name__").is_ok());
2278 assert!(lossy.decode_label_name(b"label-name").is_err());
2279 assert!(lossy.decode_label_name(b"0abc").is_err());
2280
2281 assert!(lossy.decode_label_name(&[0xff, 0xfe]).is_err());
2283 }
2284
2285 #[test]
2286 fn test_decode_label_name_unchecked() {
2287 let unchecked = PromValidationMode::Unchecked;
2288
2289 assert!(unchecked.decode_label_name(b"__name__").is_ok());
2291 assert!(unchecked.decode_label_name(b"label-name").is_err());
2292 assert!(unchecked.decode_label_name(b"0abc").is_err());
2293 }
2294
2295 #[test]
2296 fn test_is_valid_prom_label_name_bytes() {
2297 assert!(validate_label_name(b"__name__"));
2298 assert!(validate_label_name(b"job"));
2299 assert!(validate_label_name(b"_"));
2300 assert!(validate_label_name(b"A"));
2301 assert!(validate_label_name(b"abc123"));
2302 assert!(validate_label_name(b"_leading_underscore"));
2303
2304 assert!(!validate_label_name(b""));
2305 assert!(!validate_label_name(b"0starts_with_digit"));
2306 assert!(!validate_label_name(b"has-dash"));
2307 assert!(!validate_label_name(b"has.dot"));
2308 assert!(!validate_label_name(b"has space"));
2309 assert!(!validate_label_name(&[0xff, 0xfe]));
2310 }
2311}