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::response::{IntoResponse, Response};
27use axum::routing::Route;
28use axum::serve::ListenerExt;
29use axum::{Router, middleware, routing};
30use common_base::readable_size::ReadableSize;
31use common_recordbatch::RecordBatch;
32use common_telemetry::{error, info};
33use common_time::Timestamp;
34use common_time::timestamp::TimeUnit;
35use datatypes::data_type::DataType;
36use datatypes::schema::SchemaRef;
37use event::{LogState, LogValidatorRef};
38use futures::FutureExt;
39use http::{HeaderValue, Method};
40use serde::{Deserialize, Serialize};
41use serde_json::Value;
42use snafu::{ResultExt, ensure};
43use tokio::sync::Mutex;
44use tokio::sync::oneshot::{self, Sender};
45use tonic::codegen::Service;
46use tower::{Layer, ServiceBuilder};
47use tower_http::compression::CompressionLayer;
48use tower_http::cors::{AllowOrigin, Any, CorsLayer};
49use tower_http::decompression::RequestDecompressionLayer;
50use tower_http::trace::TraceLayer;
51
52use self::authorize::AuthState;
53use self::result::table_result::TableResponse;
54use crate::elasticsearch;
55use crate::error::{
56 AddressBindSnafu, AlreadyStartedSnafu, Error, InternalIoSnafu, InvalidHeaderValueSnafu, Result,
57};
58use crate::http::influxdb::{influxdb_health, influxdb_ping, influxdb_write_v1, influxdb_write_v2};
59use crate::http::otlp::OtlpState;
60use crate::http::prom_store::PromStoreState;
61use crate::http::prometheus::{
62 build_info_query, format_query, instant_query, label_values_query, labels_query, parse_query,
63 range_query, series_query,
64};
65use crate::http::result::arrow_result::ArrowResponse;
66use crate::http::result::csv_result::CsvResponse;
67use crate::http::result::error_result::ErrorResponse;
68use crate::http::result::greptime_result_v1::GreptimedbV1Response;
69use crate::http::result::influxdb_result_v1::InfluxdbV1Response;
70use crate::http::result::json_result::JsonResponse;
71use crate::http::result::null_result::NullResponse;
72use crate::interceptor::LogIngestInterceptorRef;
73use crate::metrics::http_metrics_layer;
74use crate::metrics_handler::MetricsHandler;
75use crate::pending_rows_batcher::PendingRowsBatcher;
76use crate::prometheus_handler::PrometheusHandlerRef;
77use crate::query_handler::sql::ServerSqlQueryHandlerRef;
78use crate::query_handler::{
79 DashboardHandlerRef, InfluxdbLineProtocolHandlerRef, JaegerQueryHandlerRef, LogQueryHandlerRef,
80 OpenTelemetryProtocolHandlerRef, OpentsdbProtocolHandlerRef, PipelineHandlerRef,
81 PromStoreProtocolHandlerRef,
82};
83use crate::request_memory_limiter::ServerMemoryLimiter;
84use crate::server::Server;
85
86pub mod authorize;
87#[cfg(feature = "dashboard")]
88mod dashboard;
89pub mod dyn_log;
90pub mod dyn_trace;
91pub mod event;
92pub mod extractor;
93pub mod handler;
94pub mod header;
95pub mod influxdb;
96pub mod jaeger;
97pub mod logs;
98pub mod loki;
99pub mod mem_prof;
100mod memory_limit;
101pub mod opentsdb;
102pub mod otlp;
103pub mod pprof;
104pub mod prom_store;
105pub mod prometheus;
106pub mod result;
107pub mod splunk;
108mod timeout;
109pub mod utils;
110
111use result::HttpOutputWriter;
112pub(crate) use timeout::DynamicTimeoutLayer;
113
114mod client_ip;
115use crate::prom_remote_write::validation::PromValidationMode;
116mod hints;
117mod read_preference;
118#[cfg(any(test, feature = "testing"))]
119pub mod test_helpers;
120
121pub const HTTP_API_VERSION: &str = "v1";
122pub const HTTP_API_PREFIX: &str = "/v1/";
123pub const HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH: &str = "/v1";
124const DEFAULT_BODY_LIMIT: ReadableSize = ReadableSize::mb(64);
126
127pub const AUTHORIZATION_HEADER: &str = "x-greptime-auth";
129
130pub static PUBLIC_API_PREFIX: [&str; 4] = [
132 "/v1/influxdb/ping",
133 "/v1/influxdb/health",
134 "/v1/health",
135 "/v1/splunk/services/collector/health",
136];
137
138#[derive(Default)]
139pub struct HttpServer {
140 router: StdMutex<Router>,
141 shutdown_tx: Mutex<Option<Sender<()>>>,
142 user_provider: Option<UserProviderRef>,
143 memory_limiter: ServerMemoryLimiter,
144
145 options: HttpOptions,
147 bind_addr: Option<SocketAddr>,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151#[serde(default)]
152pub struct HttpOptions {
153 pub addr: String,
154
155 #[serde(with = "humantime_serde")]
156 pub timeout: Duration,
157
158 #[serde(skip)]
159 pub disable_dashboard: bool,
160
161 pub body_limit: ReadableSize,
162
163 pub prom_validation_mode: PromValidationMode,
165
166 pub experimental_enable_prometheus_native_histogram: bool,
168
169 pub cors_allowed_origins: Vec<String>,
170
171 pub enable_cors: bool,
172
173 pub experimental_enable_explain_analyze_stream: bool,
174}
175
176impl Default for HttpOptions {
177 fn default() -> Self {
178 Self {
179 addr: "127.0.0.1:4000".to_string(),
180 timeout: Duration::from_secs(0),
181 disable_dashboard: false,
182 body_limit: DEFAULT_BODY_LIMIT,
183 cors_allowed_origins: Vec::new(),
184 enable_cors: true,
185 prom_validation_mode: PromValidationMode::Strict,
186 experimental_enable_prometheus_native_histogram: false,
187 experimental_enable_explain_analyze_stream: true,
188 }
189 }
190}
191
192#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
193pub struct ColumnSchema {
194 name: String,
195 data_type: String,
196}
197
198impl ColumnSchema {
199 pub fn new(name: String, data_type: String) -> ColumnSchema {
200 ColumnSchema { name, data_type }
201 }
202}
203
204#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
205pub struct OutputSchema {
206 column_schemas: Vec<ColumnSchema>,
207}
208
209impl OutputSchema {
210 pub fn new(columns: Vec<ColumnSchema>) -> OutputSchema {
211 OutputSchema {
212 column_schemas: columns,
213 }
214 }
215}
216
217impl From<SchemaRef> for OutputSchema {
218 fn from(schema: SchemaRef) -> OutputSchema {
219 OutputSchema {
220 column_schemas: schema
221 .column_schemas()
222 .iter()
223 .map(|cs| ColumnSchema {
224 name: cs.name.clone(),
225 data_type: cs.data_type.name(),
226 })
227 .collect(),
228 }
229 }
230}
231
232#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
233pub struct HttpRecordsOutput {
234 schema: OutputSchema,
235 rows: Vec<Vec<Value>>,
236 #[serde(default)]
239 total_rows: usize,
240
241 #[serde(skip_serializing_if = "HashMap::is_empty")]
243 #[serde(default)]
244 metrics: HashMap<String, Value>,
245}
246
247impl HttpRecordsOutput {
248 pub fn num_rows(&self) -> usize {
249 self.rows.len()
250 }
251
252 pub fn num_cols(&self) -> usize {
253 self.schema.column_schemas.len()
254 }
255
256 pub fn schema(&self) -> &OutputSchema {
257 &self.schema
258 }
259
260 pub fn rows(&self) -> &Vec<Vec<Value>> {
261 &self.rows
262 }
263}
264
265impl HttpRecordsOutput {
266 pub fn try_new(
267 schema: SchemaRef,
268 recordbatches: Vec<RecordBatch>,
269 ) -> std::result::Result<HttpRecordsOutput, Error> {
270 if recordbatches.is_empty() {
271 Ok(HttpRecordsOutput {
272 schema: OutputSchema::from(schema),
273 rows: vec![],
274 total_rows: 0,
275 metrics: Default::default(),
276 })
277 } else {
278 let num_rows = recordbatches.iter().map(|r| r.num_rows()).sum::<usize>();
279 let mut rows = Vec::with_capacity(num_rows);
280
281 for recordbatch in recordbatches {
282 let mut writer = HttpOutputWriter::new(schema.num_columns(), None);
283 writer.write(recordbatch, &mut rows)?;
284 }
285
286 Ok(HttpRecordsOutput {
287 schema: OutputSchema::from(schema),
288 total_rows: rows.len(),
289 rows,
290 metrics: Default::default(),
291 })
292 }
293 }
294}
295
296#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
297#[serde(rename_all = "lowercase")]
298pub enum GreptimeQueryOutput {
299 AffectedRows(usize),
300 Records(HttpRecordsOutput),
301}
302
303#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
305pub enum ResponseFormat {
306 Arrow,
307 Csv(bool, bool),
309 Table,
310 #[default]
311 GreptimedbV1,
312 InfluxdbV1,
313 Json,
314 Null,
315}
316
317impl ResponseFormat {
318 pub fn parse(s: &str) -> Option<Self> {
319 match s {
320 "arrow" => Some(ResponseFormat::Arrow),
321 "csv" => Some(ResponseFormat::Csv(false, false)),
322 "csvwithnames" => Some(ResponseFormat::Csv(true, false)),
323 "csvwithnamesandtypes" => Some(ResponseFormat::Csv(true, true)),
324 "table" => Some(ResponseFormat::Table),
325 "greptimedb_v1" => Some(ResponseFormat::GreptimedbV1),
326 "influxdb_v1" => Some(ResponseFormat::InfluxdbV1),
327 "json" => Some(ResponseFormat::Json),
328 "null" => Some(ResponseFormat::Null),
329 _ => None,
330 }
331 }
332
333 pub fn as_str(&self) -> &'static str {
334 match self {
335 ResponseFormat::Arrow => "arrow",
336 ResponseFormat::Csv(_, _) => "csv",
337 ResponseFormat::Table => "table",
338 ResponseFormat::GreptimedbV1 => "greptimedb_v1",
339 ResponseFormat::InfluxdbV1 => "influxdb_v1",
340 ResponseFormat::Json => "json",
341 ResponseFormat::Null => "null",
342 }
343 }
344}
345
346#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub enum Epoch {
348 Nanosecond,
349 Microsecond,
350 Millisecond,
351 Second,
352}
353
354impl Epoch {
355 pub fn parse(s: &str) -> Option<Epoch> {
356 match s {
361 "ns" => Some(Epoch::Nanosecond),
362 "u" | "µ" => Some(Epoch::Microsecond),
363 "ms" => Some(Epoch::Millisecond),
364 "s" => Some(Epoch::Second),
365 _ => None, }
367 }
368
369 pub fn convert_timestamp(&self, ts: Timestamp) -> Option<Timestamp> {
370 match self {
371 Epoch::Nanosecond => ts.convert_to(TimeUnit::Nanosecond),
372 Epoch::Microsecond => ts.convert_to(TimeUnit::Microsecond),
373 Epoch::Millisecond => ts.convert_to(TimeUnit::Millisecond),
374 Epoch::Second => ts.convert_to(TimeUnit::Second),
375 }
376 }
377}
378
379impl Display for Epoch {
380 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381 match self {
382 Epoch::Nanosecond => write!(f, "Epoch::Nanosecond"),
383 Epoch::Microsecond => write!(f, "Epoch::Microsecond"),
384 Epoch::Millisecond => write!(f, "Epoch::Millisecond"),
385 Epoch::Second => write!(f, "Epoch::Second"),
386 }
387 }
388}
389
390#[derive(Serialize, Deserialize, Debug)]
391pub enum HttpResponse {
392 Arrow(ArrowResponse),
393 Csv(CsvResponse),
394 Table(TableResponse),
395 Error(ErrorResponse),
396 GreptimedbV1(GreptimedbV1Response),
397 InfluxdbV1(InfluxdbV1Response),
398 Json(JsonResponse),
399 Null(NullResponse),
400}
401
402impl HttpResponse {
403 pub fn with_execution_time(self, execution_time: u64) -> Self {
404 match self {
405 HttpResponse::Arrow(resp) => resp.with_execution_time(execution_time).into(),
406 HttpResponse::Csv(resp) => resp.with_execution_time(execution_time).into(),
407 HttpResponse::Table(resp) => resp.with_execution_time(execution_time).into(),
408 HttpResponse::GreptimedbV1(resp) => resp.with_execution_time(execution_time).into(),
409 HttpResponse::InfluxdbV1(resp) => resp.with_execution_time(execution_time).into(),
410 HttpResponse::Json(resp) => resp.with_execution_time(execution_time).into(),
411 HttpResponse::Null(resp) => resp.with_execution_time(execution_time).into(),
412 HttpResponse::Error(resp) => resp.with_execution_time(execution_time).into(),
413 }
414 }
415
416 pub fn with_limit(self, limit: usize) -> Self {
417 match self {
418 HttpResponse::Csv(resp) => resp.with_limit(limit).into(),
419 HttpResponse::Table(resp) => resp.with_limit(limit).into(),
420 HttpResponse::GreptimedbV1(resp) => resp.with_limit(limit).into(),
421 HttpResponse::Json(resp) => resp.with_limit(limit).into(),
422 _ => self,
423 }
424 }
425}
426
427pub fn process_with_limit(
428 mut outputs: Vec<GreptimeQueryOutput>,
429 limit: usize,
430) -> Vec<GreptimeQueryOutput> {
431 outputs
432 .drain(..)
433 .map(|data| match data {
434 GreptimeQueryOutput::Records(mut records) => {
435 if records.rows.len() > limit {
436 records.rows.truncate(limit);
437 records.total_rows = limit;
438 }
439 GreptimeQueryOutput::Records(records)
440 }
441 _ => data,
442 })
443 .collect()
444}
445
446impl IntoResponse for HttpResponse {
447 fn into_response(self) -> Response {
448 match self {
449 HttpResponse::Arrow(resp) => resp.into_response(),
450 HttpResponse::Csv(resp) => resp.into_response(),
451 HttpResponse::Table(resp) => resp.into_response(),
452 HttpResponse::GreptimedbV1(resp) => resp.into_response(),
453 HttpResponse::InfluxdbV1(resp) => resp.into_response(),
454 HttpResponse::Json(resp) => resp.into_response(),
455 HttpResponse::Null(resp) => resp.into_response(),
456 HttpResponse::Error(resp) => resp.into_response(),
457 }
458 }
459}
460
461impl From<ArrowResponse> for HttpResponse {
462 fn from(value: ArrowResponse) -> Self {
463 HttpResponse::Arrow(value)
464 }
465}
466
467impl From<CsvResponse> for HttpResponse {
468 fn from(value: CsvResponse) -> Self {
469 HttpResponse::Csv(value)
470 }
471}
472
473impl From<TableResponse> for HttpResponse {
474 fn from(value: TableResponse) -> Self {
475 HttpResponse::Table(value)
476 }
477}
478
479impl From<ErrorResponse> for HttpResponse {
480 fn from(value: ErrorResponse) -> Self {
481 HttpResponse::Error(value)
482 }
483}
484
485impl From<GreptimedbV1Response> for HttpResponse {
486 fn from(value: GreptimedbV1Response) -> Self {
487 HttpResponse::GreptimedbV1(value)
488 }
489}
490
491impl From<InfluxdbV1Response> for HttpResponse {
492 fn from(value: InfluxdbV1Response) -> Self {
493 HttpResponse::InfluxdbV1(value)
494 }
495}
496
497impl From<JsonResponse> for HttpResponse {
498 fn from(value: JsonResponse) -> Self {
499 HttpResponse::Json(value)
500 }
501}
502
503impl From<NullResponse> for HttpResponse {
504 fn from(value: NullResponse) -> Self {
505 HttpResponse::Null(value)
506 }
507}
508
509#[derive(Clone)]
510pub struct ApiState {
511 pub sql_handler: ServerSqlQueryHandlerRef,
512 pub experimental_enable_explain_analyze_stream: bool,
513}
514
515#[derive(Clone)]
516pub struct GreptimeOptionsConfigState {
517 pub greptime_config_options: String,
518}
519
520#[derive(Clone)]
521pub struct DashboardState {
522 pub handler: DashboardHandlerRef,
523}
524
525pub struct HttpServerBuilder {
526 options: HttpOptions,
527 user_provider: Option<UserProviderRef>,
528 router: Router,
529 memory_limiter: ServerMemoryLimiter,
530}
531
532impl HttpServerBuilder {
533 pub fn new(options: HttpOptions) -> Self {
534 Self {
535 options,
536 user_provider: None,
537 router: Router::new(),
538 memory_limiter: ServerMemoryLimiter::default(),
539 }
540 }
541
542 pub fn with_memory_limiter(mut self, limiter: ServerMemoryLimiter) -> Self {
544 self.memory_limiter = limiter;
545 self
546 }
547
548 pub fn with_sql_handler(self, sql_handler: ServerSqlQueryHandlerRef) -> Self {
549 let sql_router = HttpServer::route_sql(ApiState {
550 sql_handler,
551 experimental_enable_explain_analyze_stream: self
552 .options
553 .experimental_enable_explain_analyze_stream,
554 });
555
556 Self {
557 router: self
558 .router
559 .nest(&format!("/{HTTP_API_VERSION}"), sql_router),
560 ..self
561 }
562 }
563
564 pub fn with_logs_handler(self, logs_handler: LogQueryHandlerRef) -> Self {
565 let logs_router = HttpServer::route_logs(logs_handler);
566
567 Self {
568 router: self
569 .router
570 .nest(&format!("/{HTTP_API_VERSION}"), logs_router),
571 ..self
572 }
573 }
574
575 pub fn with_opentsdb_handler(self, handler: OpentsdbProtocolHandlerRef) -> Self {
576 Self {
577 router: self.router.nest(
578 &format!("/{HTTP_API_VERSION}/opentsdb"),
579 HttpServer::route_opentsdb(handler),
580 ),
581 ..self
582 }
583 }
584
585 pub fn with_influxdb_handler(self, handler: InfluxdbLineProtocolHandlerRef) -> Self {
586 Self {
587 router: self.router.nest(
588 &format!("/{HTTP_API_VERSION}/influxdb"),
589 HttpServer::route_influxdb(handler),
590 ),
591 ..self
592 }
593 }
594
595 pub fn with_prom_handler(
596 self,
597 handler: PromStoreProtocolHandlerRef,
598 pipeline_handler: Option<PipelineHandlerRef>,
599 prom_store_with_metric_engine: bool,
600 prom_validation_mode: PromValidationMode,
601 pending_rows_batcher: Option<Arc<PendingRowsBatcher>>,
602 ) -> Self {
603 let state = PromStoreState {
604 prom_store_handler: handler,
605 pipeline_handler,
606 prom_store_with_metric_engine,
607 prom_validation_mode,
608 experimental_enable_prometheus_native_histogram: self
609 .options
610 .experimental_enable_prometheus_native_histogram,
611 pending_rows_batcher,
612 };
613
614 Self {
615 router: self.router.nest(
616 &format!("/{HTTP_API_VERSION}/prometheus"),
617 HttpServer::route_prom(state),
618 ),
619 ..self
620 }
621 }
622
623 pub fn with_prometheus_handler(self, handler: PrometheusHandlerRef) -> Self {
624 Self {
625 router: self.router.nest(
626 &format!("/{HTTP_API_VERSION}/prometheus/api/v1"),
627 HttpServer::route_prometheus(handler),
628 ),
629 ..self
630 }
631 }
632
633 pub fn with_otlp_handler(
634 self,
635 handler: OpenTelemetryProtocolHandlerRef,
636 with_metric_engine: bool,
637 ) -> Self {
638 Self {
639 router: self.router.nest(
640 &format!("/{HTTP_API_VERSION}/otlp"),
641 HttpServer::route_otlp(handler, with_metric_engine),
642 ),
643 ..self
644 }
645 }
646
647 pub fn with_user_provider(self, user_provider: UserProviderRef) -> Self {
648 Self {
649 user_provider: Some(user_provider),
650 ..self
651 }
652 }
653
654 pub fn with_metrics_handler(self, handler: MetricsHandler) -> Self {
655 Self {
656 router: self.router.merge(HttpServer::route_metrics(handler)),
657 ..self
658 }
659 }
660
661 pub fn with_log_ingest_handler(
662 self,
663 handler: PipelineHandlerRef,
664 validator: Option<LogValidatorRef>,
665 ingest_interceptor: Option<LogIngestInterceptorRef<Error>>,
666 ) -> Self {
667 let log_state = LogState {
668 log_handler: handler,
669 log_validator: validator,
670 ingest_interceptor,
671 };
672
673 let router = self.router.nest(
674 &format!("/{HTTP_API_VERSION}"),
675 HttpServer::route_pipelines(log_state.clone()),
676 );
677 let router = router.nest(
679 &format!("/{HTTP_API_VERSION}/events"),
680 #[allow(deprecated)]
681 HttpServer::route_log_deprecated(log_state.clone()),
682 );
683
684 let router = router.nest(
685 &format!("/{HTTP_API_VERSION}/loki"),
686 HttpServer::route_loki(log_state.clone()),
687 );
688
689 let router = router.nest(
690 &format!("/{HTTP_API_VERSION}/elasticsearch"),
691 HttpServer::route_elasticsearch(log_state.clone()),
692 );
693
694 let router = router.nest(
695 &format!("/{HTTP_API_VERSION}/elasticsearch/"),
696 Router::new()
697 .route("/", routing::get(elasticsearch::handle_get_version))
698 .with_state(log_state.clone()),
699 );
700
701 let router = router.nest(
702 &format!("/{HTTP_API_VERSION}/splunk"),
703 HttpServer::route_splunk(log_state),
704 );
705
706 Self { router, ..self }
707 }
708
709 pub fn with_greptime_config_options(self, opts: String) -> Self {
710 let config_router = HttpServer::route_config(GreptimeOptionsConfigState {
711 greptime_config_options: opts,
712 });
713
714 Self {
715 router: self.router.merge(config_router),
716 ..self
717 }
718 }
719
720 pub fn with_jaeger_handler(self, handler: JaegerQueryHandlerRef) -> Self {
721 Self {
722 router: self.router.nest(
723 &format!("/{HTTP_API_VERSION}/jaeger"),
724 HttpServer::route_jaeger(handler),
725 ),
726 ..self
727 }
728 }
729
730 pub fn with_dashboard_handler(self, handler: DashboardHandlerRef) -> Self {
731 Self {
732 router: self.router.nest(
733 &format!("/{HTTP_API_VERSION}/dashboards"),
734 HttpServer::route_dashboard(handler),
735 ),
736 ..self
737 }
738 }
739
740 pub fn with_extra_router(self, router: Router) -> Self {
741 Self {
742 router: self.router.merge(router),
743 ..self
744 }
745 }
746
747 pub fn add_layer<L>(self, layer: L) -> Self
748 where
749 L: Layer<Route> + Clone + Send + Sync + 'static,
750 L::Service: Service<Request> + Clone + Send + Sync + 'static,
751 <L::Service as Service<Request>>::Response: IntoResponse + 'static,
752 <L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
753 <L::Service as Service<Request>>::Future: Send + 'static,
754 {
755 Self {
756 router: self.router.layer(layer),
757 ..self
758 }
759 }
760
761 pub fn build(self) -> HttpServer {
762 HttpServer {
763 options: self.options,
764 user_provider: self.user_provider,
765 shutdown_tx: Mutex::new(None),
766 router: StdMutex::new(self.router),
767 bind_addr: None,
768 memory_limiter: self.memory_limiter,
769 }
770 }
771}
772
773impl HttpServer {
774 pub fn make_app(&self) -> Router {
776 let mut router = {
777 let router = self.router.lock().unwrap();
778 router.clone()
779 };
780
781 router = router
782 .route("/", routing::get(handler::index))
783 .route(
784 "/health",
785 routing::get(handler::health).post(handler::health),
786 )
787 .route(
788 &format!("/{HTTP_API_VERSION}/health"),
789 routing::get(handler::health).post(handler::health),
790 )
791 .route(
792 "/ready",
793 routing::get(handler::health).post(handler::health),
794 );
795
796 router = router.route("/status", routing::get(handler::status));
797
798 #[cfg(feature = "dashboard")]
799 {
800 if !self.options.disable_dashboard {
801 info!("Enable dashboard service at '/dashboard'");
802 router = router.route(
804 "/dashboard",
805 routing::get(|uri: axum::http::uri::Uri| async move {
806 let path = uri.path();
807 let query = uri.query().map(|q| format!("?{}", q)).unwrap_or_default();
808
809 let new_uri = format!("{}/{}", path, query);
810 axum::response::Redirect::permanent(&new_uri)
811 }),
812 );
813
814 router = router
818 .route(
819 "/dashboard/",
820 routing::get(dashboard::static_handler).post(dashboard::static_handler),
821 )
822 .route(
823 "/dashboard/{*x}",
824 routing::get(dashboard::static_handler).post(dashboard::static_handler),
825 );
826 }
827 }
828
829 router = router.route_layer(middleware::from_fn(http_metrics_layer));
831
832 router
833 }
834
835 pub fn build(&self, router: Router) -> Result<Router> {
838 let timeout_layer = if self.options.timeout != Duration::default() {
839 Some(ServiceBuilder::new().layer(DynamicTimeoutLayer::new(self.options.timeout)))
840 } else {
841 info!("HTTP server timeout is disabled");
842 None
843 };
844 let body_limit_layer = if self.options.body_limit != ReadableSize(0) {
845 Some(
846 ServiceBuilder::new()
847 .layer(DefaultBodyLimit::max(self.options.body_limit.0 as usize)),
848 )
849 } else {
850 info!("HTTP server body limit is disabled");
851 None
852 };
853 let cors_layer = if self.options.enable_cors {
854 Some(
855 CorsLayer::new()
856 .allow_methods([
857 Method::GET,
858 Method::POST,
859 Method::PUT,
860 Method::DELETE,
861 Method::HEAD,
862 ])
863 .allow_origin(if self.options.cors_allowed_origins.is_empty() {
864 AllowOrigin::from(Any)
865 } else {
866 AllowOrigin::from(
867 self.options
868 .cors_allowed_origins
869 .iter()
870 .map(|s| {
871 HeaderValue::from_str(s.as_str())
872 .context(InvalidHeaderValueSnafu)
873 })
874 .collect::<Result<Vec<HeaderValue>>>()?,
875 )
876 })
877 .allow_headers(Any),
878 )
879 } else {
880 info!("HTTP server cross-origin is disabled");
881 None
882 };
883
884 Ok(router
885 .layer(
887 ServiceBuilder::new()
888 .layer(TraceLayer::new_for_http().on_failure(()))
891 .option_layer(cors_layer)
892 .option_layer(timeout_layer)
893 .option_layer(body_limit_layer)
894 .layer(middleware::from_fn_with_state(
896 self.memory_limiter.clone(),
897 memory_limit::memory_limit_middleware,
898 ))
899 .layer(middleware::from_fn_with_state(
901 AuthState::new(self.user_provider.clone()),
902 authorize::check_http_auth,
903 ))
904 .layer(middleware::from_fn(hints::extract_hints))
905 .layer(middleware::from_fn(client_ip::log_error_with_client_ip))
906 .layer(middleware::from_fn(
907 read_preference::extract_read_preference,
908 )),
909 )
910 .nest(
912 "/debug",
913 Router::new()
914 .route("/log_level", routing::post(dyn_log::dyn_log_handler))
916 .route("/enable_trace", routing::post(dyn_trace::dyn_trace_handler))
917 .nest(
918 "/prof",
919 Router::new()
920 .route("/cpu", routing::post(pprof::pprof_handler))
921 .route("/mem", routing::post(mem_prof::mem_prof_handler))
922 .route("/mem/symbol", routing::post(mem_prof::symbolicate_handler))
923 .route(
924 "/mem/activate",
925 routing::post(mem_prof::activate_heap_prof_handler),
926 )
927 .route(
928 "/mem/deactivate",
929 routing::post(mem_prof::deactivate_heap_prof_handler),
930 )
931 .route(
932 "/mem/status",
933 routing::get(mem_prof::heap_prof_status_handler),
934 ) .route(
936 "/mem/gdump",
937 routing::get(mem_prof::gdump_status_handler)
938 .post(mem_prof::gdump_toggle_handler),
939 ),
940 ),
941 ))
942 }
943
944 fn route_metrics<S>(metrics_handler: MetricsHandler) -> Router<S> {
945 Router::new()
946 .route("/metrics", routing::get(handler::metrics))
947 .with_state(metrics_handler)
948 }
949
950 fn route_loki<S>(log_state: LogState) -> Router<S> {
951 Router::new()
952 .route("/api/v1/push", routing::post(loki::loki_ingest))
953 .layer(
954 ServiceBuilder::new()
955 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
956 )
957 .with_state(log_state)
958 }
959
960 fn route_splunk<S>(log_state: LogState) -> Router<S> {
961 Router::new()
962 .route(
963 "/services/collector/health",
964 routing::get(splunk::handle_health),
965 )
966 .route(
967 "/services/collector/health/1.0",
968 routing::get(splunk::handle_health),
969 )
970 .route(
973 "/services/collector/event",
974 routing::post(splunk::handle_event),
975 )
976 .route("/services/collector", routing::post(splunk::handle_event))
977 .route(
978 "/services/collector/event/1.0",
979 routing::post(splunk::handle_event),
980 )
981 .route("/services/collector/raw", routing::post(splunk::handle_raw))
984 .route(
985 "/services/collector/raw/1.0",
986 routing::post(splunk::handle_raw),
987 )
988 .layer(
989 ServiceBuilder::new()
990 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
991 )
992 .with_state(log_state)
993 }
994
995 fn route_elasticsearch<S>(log_state: LogState) -> Router<S> {
996 Router::new()
997 .route(
999 "/",
1000 routing::head((HttpStatusCode::OK, elasticsearch::elasticsearch_headers())),
1001 )
1002 .route("/", routing::get(elasticsearch::handle_get_version))
1004 .route("/_license", routing::get(elasticsearch::handle_get_license))
1006 .route("/_bulk", routing::post(elasticsearch::handle_bulk_api))
1007 .route(
1008 "/{index}/_bulk",
1009 routing::post(elasticsearch::handle_bulk_api_with_index),
1010 )
1011 .route(
1013 "/_ilm/policy/{*path}",
1014 routing::any((
1015 HttpStatusCode::OK,
1016 elasticsearch::elasticsearch_headers(),
1017 axum::Json(serde_json::json!({})),
1018 )),
1019 )
1020 .route(
1022 "/_index_template/{*path}",
1023 routing::any((
1024 HttpStatusCode::OK,
1025 elasticsearch::elasticsearch_headers(),
1026 axum::Json(serde_json::json!({})),
1027 )),
1028 )
1029 .route(
1032 "/_ingest/{*path}",
1033 routing::any((
1034 HttpStatusCode::OK,
1035 elasticsearch::elasticsearch_headers(),
1036 axum::Json(serde_json::json!({})),
1037 )),
1038 )
1039 .route(
1042 "/_nodes/{*path}",
1043 routing::any((
1044 HttpStatusCode::OK,
1045 elasticsearch::elasticsearch_headers(),
1046 axum::Json(serde_json::json!({})),
1047 )),
1048 )
1049 .route(
1052 "/logstash/{*path}",
1053 routing::any((
1054 HttpStatusCode::OK,
1055 elasticsearch::elasticsearch_headers(),
1056 axum::Json(serde_json::json!({})),
1057 )),
1058 )
1059 .route(
1060 "/_logstash/{*path}",
1061 routing::any((
1062 HttpStatusCode::OK,
1063 elasticsearch::elasticsearch_headers(),
1064 axum::Json(serde_json::json!({})),
1065 )),
1066 )
1067 .layer(ServiceBuilder::new().layer(RequestDecompressionLayer::new()))
1068 .with_state(log_state)
1069 }
1070
1071 #[deprecated(since = "0.11.0", note = "Use `route_pipelines()` instead.")]
1072 fn route_log_deprecated<S>(log_state: LogState) -> Router<S> {
1073 Router::new()
1074 .route("/logs", routing::post(event::log_ingester))
1075 .route(
1076 "/pipelines/{pipeline_name}",
1077 routing::get(event::query_pipeline),
1078 )
1079 .route(
1080 "/pipelines/{pipeline_name}",
1081 routing::post(event::add_pipeline),
1082 )
1083 .route(
1084 "/pipelines/{pipeline_name}",
1085 routing::delete(event::delete_pipeline),
1086 )
1087 .route("/pipelines/dryrun", routing::post(event::pipeline_dryrun))
1088 .layer(
1089 ServiceBuilder::new()
1090 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1091 )
1092 .with_state(log_state)
1093 }
1094
1095 fn route_pipelines<S>(log_state: LogState) -> Router<S> {
1096 Router::new()
1097 .route("/ingest", routing::post(event::log_ingester))
1098 .route(
1099 "/pipelines/{pipeline_name}",
1100 routing::get(event::query_pipeline),
1101 )
1102 .route(
1103 "/pipelines/{pipeline_name}/ddl",
1104 routing::get(event::query_pipeline_ddl),
1105 )
1106 .route(
1107 "/pipelines/{pipeline_name}",
1108 routing::post(event::add_pipeline),
1109 )
1110 .route(
1111 "/pipelines/{pipeline_name}",
1112 routing::delete(event::delete_pipeline),
1113 )
1114 .route("/pipelines/_dryrun", routing::post(event::pipeline_dryrun))
1115 .layer(
1116 ServiceBuilder::new()
1117 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1118 )
1119 .with_state(log_state)
1120 }
1121
1122 fn route_sql<S>(api_state: ApiState) -> Router<S> {
1123 let mut router = Router::new()
1124 .route("/sql", routing::get(handler::sql).post(handler::sql))
1125 .route(
1126 "/sql/parse",
1127 routing::get(handler::sql_parse).post(handler::sql_parse),
1128 )
1129 .route(
1130 "/sql/format",
1131 routing::get(handler::sql_format).post(handler::sql_format),
1132 )
1133 .route(
1134 "/promql",
1135 routing::get(handler::promql).post(handler::promql),
1136 );
1137
1138 if api_state.experimental_enable_explain_analyze_stream {
1139 router = router.route(
1140 "/sql/analyze/stream",
1141 routing::post(handler::sql_analyze_stream),
1142 );
1143 }
1144
1145 router.with_state(api_state)
1146 }
1147
1148 fn route_logs<S>(log_handler: LogQueryHandlerRef) -> Router<S> {
1149 Router::new()
1150 .route("/logs", routing::get(logs::logs).post(logs::logs))
1151 .with_state(log_handler)
1152 }
1153
1154 pub fn route_prometheus<S>(prometheus_handler: PrometheusHandlerRef) -> Router<S> {
1158 Router::new()
1159 .route(
1160 "/format_query",
1161 routing::post(format_query).get(format_query),
1162 )
1163 .route("/status/buildinfo", routing::get(build_info_query))
1164 .route("/query", routing::post(instant_query).get(instant_query))
1165 .route("/query_range", routing::post(range_query).get(range_query))
1166 .route("/labels", routing::post(labels_query).get(labels_query))
1167 .route("/series", routing::post(series_query).get(series_query))
1168 .route("/parse_query", routing::post(parse_query).get(parse_query))
1169 .route(
1170 "/label/{label_name}/values",
1171 routing::get(label_values_query),
1172 )
1173 .layer(ServiceBuilder::new().layer(CompressionLayer::new()))
1174 .with_state(prometheus_handler)
1175 }
1176
1177 fn route_prom<S>(state: PromStoreState) -> Router<S> {
1183 Router::new()
1184 .route("/read", routing::post(prom_store::remote_read))
1185 .route("/write", routing::post(prom_store::remote_write))
1186 .with_state(state)
1187 }
1188
1189 fn route_influxdb<S>(influxdb_handler: InfluxdbLineProtocolHandlerRef) -> Router<S> {
1190 Router::new()
1191 .route("/write", routing::post(influxdb_write_v1))
1192 .route("/api/v2/write", routing::post(influxdb_write_v2))
1193 .layer(
1194 ServiceBuilder::new()
1195 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1196 )
1197 .route("/ping", routing::get(influxdb_ping))
1198 .route("/health", routing::get(influxdb_health))
1199 .with_state(influxdb_handler)
1200 }
1201
1202 fn route_opentsdb<S>(opentsdb_handler: OpentsdbProtocolHandlerRef) -> Router<S> {
1203 Router::new()
1204 .route("/api/put", routing::post(opentsdb::put))
1205 .with_state(opentsdb_handler)
1206 }
1207
1208 fn route_otlp<S>(
1209 otlp_handler: OpenTelemetryProtocolHandlerRef,
1210 with_metric_engine: bool,
1211 ) -> Router<S> {
1212 Router::new()
1213 .route("/v1/metrics", routing::post(otlp::metrics))
1214 .route("/v1/traces", routing::post(otlp::traces))
1215 .route("/v1/logs", routing::post(otlp::logs))
1216 .layer(
1217 ServiceBuilder::new()
1218 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1219 )
1220 .with_state(OtlpState {
1221 with_metric_engine,
1222 handler: otlp_handler,
1223 })
1224 }
1225
1226 fn route_config<S>(state: GreptimeOptionsConfigState) -> Router<S> {
1227 Router::new()
1228 .route("/config", routing::get(handler::config))
1229 .with_state(state)
1230 }
1231
1232 fn route_jaeger<S>(handler: JaegerQueryHandlerRef) -> Router<S> {
1233 Router::new()
1234 .route("/api/services", routing::get(jaeger::handle_get_services))
1235 .route(
1236 "/api/services/{service_name}/operations",
1237 routing::get(jaeger::handle_get_operations_by_service),
1238 )
1239 .route(
1240 "/api/operations",
1241 routing::get(jaeger::handle_get_operations),
1242 )
1243 .route("/api/traces", routing::get(jaeger::handle_find_traces))
1244 .route(
1245 "/api/traces/{trace_id}",
1246 routing::get(jaeger::handle_get_trace),
1247 )
1248 .with_state(handler)
1249 }
1250
1251 #[cfg(feature = "dashboard")]
1252 fn route_dashboard<S>(handler: DashboardHandlerRef) -> Router<S> {
1253 use crate::http::dashboard::{add_dashboard, delete_dashboard, list_dashboards};
1254
1255 Router::new()
1256 .route("/", routing::get(list_dashboards))
1257 .route("/{dashboard_name}", routing::post(add_dashboard))
1258 .route("/{dashboard_name}", routing::delete(delete_dashboard))
1259 .layer(
1260 ServiceBuilder::new()
1261 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1262 )
1263 .with_state(DashboardState { handler })
1264 }
1265
1266 #[cfg(not(feature = "dashboard"))]
1267 fn route_dashboard<S>(handler: DashboardHandlerRef) -> Router<S> {
1268 Router::new().with_state(DashboardState { handler })
1269 }
1270}
1271
1272pub const HTTP_SERVER: &str = "HTTP_SERVER";
1273
1274#[async_trait]
1275impl Server for HttpServer {
1276 async fn shutdown(&self) -> Result<()> {
1277 let mut shutdown_tx = self.shutdown_tx.lock().await;
1278 if let Some(tx) = shutdown_tx.take()
1279 && tx.send(()).is_err()
1280 {
1281 info!("Receiver dropped, the HTTP server has already exited");
1282 }
1283 info!("Shutdown HTTP server");
1284
1285 Ok(())
1286 }
1287
1288 async fn start(&mut self, listening: SocketAddr) -> Result<()> {
1289 let (tx, rx) = oneshot::channel();
1290 let serve = {
1291 let mut shutdown_tx = self.shutdown_tx.lock().await;
1292 ensure!(
1293 shutdown_tx.is_none(),
1294 AlreadyStartedSnafu { server: "HTTP" }
1295 );
1296
1297 let app = self.build(self.make_app())?;
1298 let listener = tokio::net::TcpListener::bind(listening)
1299 .await
1300 .context(AddressBindSnafu { addr: listening })?
1301 .tap_io(|tcp_stream| {
1302 if let Err(e) = tcp_stream.set_nodelay(true) {
1303 error!(e; "Failed to set TCP_NODELAY on incoming connection");
1304 }
1305 });
1306 let serve = axum::serve(
1307 listener,
1308 app.into_make_service_with_connect_info::<SocketAddr>(),
1309 );
1310
1311 *shutdown_tx = Some(tx);
1328
1329 serve
1330 };
1331 let listening = serve.local_addr().context(InternalIoSnafu)?;
1332 info!("HTTP server is bound to {}", listening);
1333
1334 common_runtime::spawn_global(async move {
1335 if let Err(e) = serve
1336 .with_graceful_shutdown(rx.map(drop))
1337 .await
1338 .context(InternalIoSnafu)
1339 {
1340 error!(e; "Failed to shutdown http server");
1341 }
1342 });
1343
1344 self.bind_addr = Some(listening);
1345 Ok(())
1346 }
1347
1348 fn name(&self) -> &str {
1349 HTTP_SERVER
1350 }
1351
1352 fn bind_addr(&self) -> Option<SocketAddr> {
1353 self.bind_addr
1354 }
1355
1356 fn as_any(&self) -> &dyn std::any::Any {
1357 self
1358 }
1359}
1360
1361#[cfg(test)]
1362mod test {
1363 use std::future::pending;
1364 use std::io::Cursor;
1365 use std::sync::Arc;
1366
1367 use arrow_ipc::reader::StreamReader;
1368 use arrow_schema::DataType;
1369 use axum::handler::Handler;
1370 use axum::http::StatusCode;
1371 use axum::routing::get;
1372 use common_query::{Output, OutputData};
1373 use common_recordbatch::RecordBatches;
1374 use datafusion_expr::LogicalPlan;
1375 use datatypes::prelude::*;
1376 use datatypes::schema::{ColumnSchema, Schema};
1377 use datatypes::vectors::{StringVector, UInt32Vector};
1378 use header::constants::GREPTIME_DB_HEADER_TIMEOUT;
1379 use query::parser::PromQuery;
1380 use query::query_engine::DescribeResult;
1381 use session::context::QueryContextRef;
1382 use sql::statements::statement::Statement;
1383 use tokio::sync::mpsc;
1384 use tokio::time::Instant;
1385
1386 use super::*;
1387 use crate::http::test_helpers::TestClient;
1388 use crate::prom_remote_write::validation::validate_label_name;
1389 use crate::query_handler::sql::SqlQueryHandler;
1390
1391 struct DummyInstance {
1392 _tx: mpsc::Sender<(String, Vec<u8>)>,
1393 }
1394
1395 #[async_trait]
1396 impl SqlQueryHandler for DummyInstance {
1397 async fn do_query(&self, _: &str, _: QueryContextRef) -> Vec<Result<Output>> {
1398 unimplemented!()
1399 }
1400
1401 async fn do_analyze_stream_query(&self, _: &str, _: QueryContextRef) -> Result<Output> {
1402 let stream = common_recordbatch::RecordBatches::empty().as_stream();
1403 Ok(Output::new(OutputData::Stream(stream), Default::default()))
1404 }
1405
1406 async fn do_promql_query(&self, _: &PromQuery, _: QueryContextRef) -> Vec<Result<Output>> {
1407 unimplemented!()
1408 }
1409
1410 async fn do_exec_plan(
1411 &self,
1412 _plan: LogicalPlan,
1413 _stmt: Option<Statement>,
1414 _query_ctx: QueryContextRef,
1415 ) -> Result<Output> {
1416 unimplemented!()
1417 }
1418
1419 async fn do_describe(
1420 &self,
1421 _stmt: sql::statements::statement::Statement,
1422 _query_ctx: QueryContextRef,
1423 ) -> Result<Option<DescribeResult>> {
1424 unimplemented!()
1425 }
1426
1427 async fn is_valid_schema(&self, _catalog: &str, _schema: &str) -> Result<bool> {
1428 Ok(true)
1429 }
1430 }
1431
1432 fn timeout() -> DynamicTimeoutLayer {
1433 DynamicTimeoutLayer::new(Duration::from_millis(10))
1434 }
1435
1436 async fn forever() {
1437 pending().await
1438 }
1439
1440 fn make_test_app(tx: mpsc::Sender<(String, Vec<u8>)>) -> Router {
1441 make_test_app_custom(tx, HttpOptions::default())
1442 }
1443
1444 fn make_test_app_custom(tx: mpsc::Sender<(String, Vec<u8>)>, options: HttpOptions) -> Router {
1445 let instance = Arc::new(DummyInstance { _tx: tx });
1446 let server = HttpServerBuilder::new(options)
1447 .with_sql_handler(instance.clone())
1448 .build();
1449 server.build(server.make_app()).unwrap().route(
1450 "/test/timeout",
1451 get(forever.layer(ServiceBuilder::new().layer(timeout()))),
1452 )
1453 }
1454
1455 #[tokio::test]
1456 pub async fn test_analyze_stream_route_config_gate() {
1457 let (tx, _rx) = mpsc::channel(100);
1458 let options = HttpOptions {
1459 experimental_enable_explain_analyze_stream: false,
1460 ..Default::default()
1461 };
1462 let app = make_test_app_custom(tx, options);
1463 let client = TestClient::new(app).await;
1464 let res = client
1465 .post("/v1/sql/analyze/stream?sql=EXPLAIN%20ANALYZE%20VERBOSE%20SELECT%201")
1466 .send()
1467 .await;
1468 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1469
1470 let (tx, _rx) = mpsc::channel(100);
1471 let app = make_test_app_custom(tx, HttpOptions::default());
1472 let client = TestClient::new(app).await;
1473 let res = client
1474 .post("/v1/sql/analyze/stream?sql=EXPLAIN%20ANALYZE%20VERBOSE%20SELECT%201")
1475 .send()
1476 .await;
1477 assert_ne!(res.status(), StatusCode::NOT_FOUND);
1478 }
1479
1480 #[tokio::test]
1481 pub async fn test_cors() {
1482 let (tx, _rx) = mpsc::channel(100);
1484 let app = make_test_app(tx);
1485 let client = TestClient::new(app).await;
1486
1487 let res = client.get("/health").send().await;
1488
1489 assert_eq!(res.status(), StatusCode::OK);
1490 assert_eq!(
1491 res.headers()
1492 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1493 .expect("expect cors header origin"),
1494 "*"
1495 );
1496
1497 let res = client.get("/v1/health").send().await;
1498
1499 assert_eq!(res.status(), StatusCode::OK);
1500 assert_eq!(
1501 res.headers()
1502 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1503 .expect("expect cors header origin"),
1504 "*"
1505 );
1506
1507 let res = client
1508 .options("/health")
1509 .header("Access-Control-Request-Headers", "x-greptime-auth")
1510 .header("Access-Control-Request-Method", "DELETE")
1511 .header("Origin", "https://example.com")
1512 .send()
1513 .await;
1514 assert_eq!(res.status(), StatusCode::OK);
1515 assert_eq!(
1516 res.headers()
1517 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1518 .expect("expect cors header origin"),
1519 "*"
1520 );
1521 assert_eq!(
1522 res.headers()
1523 .get(http::header::ACCESS_CONTROL_ALLOW_HEADERS)
1524 .expect("expect cors header headers"),
1525 "*"
1526 );
1527 assert_eq!(
1528 res.headers()
1529 .get(http::header::ACCESS_CONTROL_ALLOW_METHODS)
1530 .expect("expect cors header methods"),
1531 "GET,POST,PUT,DELETE,HEAD"
1532 );
1533 }
1534
1535 #[tokio::test]
1536 pub async fn test_cors_custom_origins() {
1537 let (tx, _rx) = mpsc::channel(100);
1539 let origin = "https://example.com";
1540
1541 let options = HttpOptions {
1542 cors_allowed_origins: vec![origin.to_string()],
1543 ..Default::default()
1544 };
1545
1546 let app = make_test_app_custom(tx, options);
1547 let client = TestClient::new(app).await;
1548
1549 let res = client.get("/health").header("Origin", origin).send().await;
1550
1551 assert_eq!(res.status(), StatusCode::OK);
1552 assert_eq!(
1553 res.headers()
1554 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1555 .expect("expect cors header origin"),
1556 origin
1557 );
1558
1559 let res = client
1560 .get("/health")
1561 .header("Origin", "https://notallowed.com")
1562 .send()
1563 .await;
1564
1565 assert_eq!(res.status(), StatusCode::OK);
1566 assert!(
1567 !res.headers()
1568 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1569 );
1570 }
1571
1572 #[tokio::test]
1573 pub async fn test_cors_disabled() {
1574 let (tx, _rx) = mpsc::channel(100);
1576
1577 let options = HttpOptions {
1578 enable_cors: false,
1579 ..Default::default()
1580 };
1581
1582 let app = make_test_app_custom(tx, options);
1583 let client = TestClient::new(app).await;
1584
1585 let res = client.get("/health").send().await;
1586
1587 assert_eq!(res.status(), StatusCode::OK);
1588 assert!(
1589 !res.headers()
1590 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
1591 );
1592 }
1593
1594 #[test]
1595 fn test_http_options_default() {
1596 let default = HttpOptions::default();
1597 assert_eq!("127.0.0.1:4000".to_string(), default.addr);
1598 assert_eq!(Duration::from_secs(0), default.timeout)
1599 }
1600
1601 #[tokio::test]
1602 async fn test_http_server_request_timeout() {
1603 common_telemetry::init_default_ut_logging();
1604
1605 let (tx, _rx) = mpsc::channel(100);
1606 let app = make_test_app(tx);
1607 let client = TestClient::new(app).await;
1608 let res = client.get("/test/timeout").send().await;
1609 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
1610
1611 let now = Instant::now();
1612 let res = client
1613 .get("/test/timeout")
1614 .header(GREPTIME_DB_HEADER_TIMEOUT, "20ms")
1615 .send()
1616 .await;
1617 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
1618 let elapsed = now.elapsed();
1619 assert!(elapsed > Duration::from_millis(15));
1620
1621 tokio::time::timeout(
1622 Duration::from_millis(15),
1623 client
1624 .get("/test/timeout")
1625 .header(GREPTIME_DB_HEADER_TIMEOUT, "0s")
1626 .send(),
1627 )
1628 .await
1629 .unwrap_err();
1630
1631 tokio::time::timeout(
1632 Duration::from_millis(15),
1633 client
1634 .get("/test/timeout")
1635 .header(
1636 GREPTIME_DB_HEADER_TIMEOUT,
1637 humantime::format_duration(Duration::default()).to_string(),
1638 )
1639 .send(),
1640 )
1641 .await
1642 .unwrap_err();
1643 }
1644
1645 #[tokio::test]
1646 async fn test_schema_for_empty_response() {
1647 let column_schemas = vec![
1648 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
1649 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
1650 ];
1651 let schema = Arc::new(Schema::new(column_schemas));
1652
1653 let recordbatches = RecordBatches::try_new(schema.clone(), vec![]).unwrap();
1654 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
1655
1656 let json_resp = GreptimedbV1Response::from_output(outputs).await;
1657 if let HttpResponse::GreptimedbV1(json_resp) = json_resp {
1658 let json_output = &json_resp.output[0];
1659 if let GreptimeQueryOutput::Records(r) = json_output {
1660 assert_eq!(r.num_rows(), 0);
1661 assert_eq!(r.num_cols(), 2);
1662 assert_eq!(r.schema.column_schemas[0].name, "numbers");
1663 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
1664 } else {
1665 panic!("invalid output type");
1666 }
1667 } else {
1668 panic!("invalid format")
1669 }
1670 }
1671
1672 #[tokio::test]
1673 async fn test_recordbatches_conversion() {
1674 let column_schemas = vec![
1675 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
1676 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
1677 ];
1678 let schema = Arc::new(Schema::new(column_schemas));
1679 let columns: Vec<VectorRef> = vec![
1680 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
1681 Arc::new(StringVector::from(vec![
1682 None,
1683 Some("hello"),
1684 Some("greptime"),
1685 None,
1686 ])),
1687 ];
1688 let recordbatch = RecordBatch::new(schema.clone(), columns).unwrap();
1689
1690 for format in [
1691 ResponseFormat::GreptimedbV1,
1692 ResponseFormat::InfluxdbV1,
1693 ResponseFormat::Csv(true, true),
1694 ResponseFormat::Table,
1695 ResponseFormat::Arrow,
1696 ResponseFormat::Json,
1697 ResponseFormat::Null,
1698 ] {
1699 let recordbatches =
1700 RecordBatches::try_new(schema.clone(), vec![recordbatch.clone()]).unwrap();
1701 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
1702 let json_resp = match format {
1703 ResponseFormat::Arrow => ArrowResponse::from_output(outputs, None).await,
1704 ResponseFormat::Csv(with_names, with_types) => {
1705 CsvResponse::from_output(outputs, with_names, with_types).await
1706 }
1707 ResponseFormat::Table => TableResponse::from_output(outputs).await,
1708 ResponseFormat::GreptimedbV1 => GreptimedbV1Response::from_output(outputs).await,
1709 ResponseFormat::InfluxdbV1 => InfluxdbV1Response::from_output(outputs, None).await,
1710 ResponseFormat::Json => JsonResponse::from_output(outputs).await,
1711 ResponseFormat::Null => NullResponse::from_output(outputs).await,
1712 };
1713
1714 match json_resp {
1715 HttpResponse::GreptimedbV1(resp) => {
1716 let json_output = &resp.output[0];
1717 if let GreptimeQueryOutput::Records(r) = json_output {
1718 assert_eq!(r.num_rows(), 4);
1719 assert_eq!(r.num_cols(), 2);
1720 assert_eq!(r.schema.column_schemas[0].name, "numbers");
1721 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
1722 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
1723 assert_eq!(r.rows[0][1], serde_json::Value::Null);
1724 } else {
1725 panic!("invalid output type");
1726 }
1727 }
1728 HttpResponse::InfluxdbV1(resp) => {
1729 let json_output = &resp.results()[0];
1730 assert_eq!(json_output.num_rows(), 4);
1731 assert_eq!(json_output.num_cols(), 2);
1732 assert_eq!(json_output.series[0].columns.clone()[0], "numbers");
1733 assert_eq!(
1734 json_output.series[0].values[0][0],
1735 serde_json::Value::from(1)
1736 );
1737 assert_eq!(json_output.series[0].values[0][1], serde_json::Value::Null);
1738 }
1739 HttpResponse::Csv(resp) => {
1740 let output = &resp.output()[0];
1741 if let GreptimeQueryOutput::Records(r) = output {
1742 assert_eq!(r.num_rows(), 4);
1743 assert_eq!(r.num_cols(), 2);
1744 assert_eq!(r.schema.column_schemas[0].name, "numbers");
1745 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
1746 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
1747 assert_eq!(r.rows[0][1], serde_json::Value::Null);
1748 } else {
1749 panic!("invalid output type");
1750 }
1751 }
1752
1753 HttpResponse::Table(resp) => {
1754 let output = &resp.output()[0];
1755 if let GreptimeQueryOutput::Records(r) = output {
1756 assert_eq!(r.num_rows(), 4);
1757 assert_eq!(r.num_cols(), 2);
1758 assert_eq!(r.schema.column_schemas[0].name, "numbers");
1759 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
1760 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
1761 assert_eq!(r.rows[0][1], serde_json::Value::Null);
1762 } else {
1763 panic!("invalid output type");
1764 }
1765 }
1766
1767 HttpResponse::Arrow(resp) => {
1768 let output = resp.data;
1769 let mut reader = StreamReader::try_new(Cursor::new(output), None)
1770 .expect("Arrow reader error");
1771 let schema = reader.schema();
1772 assert_eq!(schema.fields[0].name(), "numbers");
1773 assert_eq!(schema.fields[0].data_type(), &DataType::UInt32);
1774 assert_eq!(schema.fields[1].name(), "strings");
1775 assert_eq!(schema.fields[1].data_type(), &DataType::Utf8);
1776
1777 let rb = reader.next().unwrap().expect("read record batch failed");
1778 assert_eq!(rb.num_columns(), 2);
1779 assert_eq!(rb.num_rows(), 4);
1780 }
1781
1782 HttpResponse::Json(resp) => {
1783 let output = &resp.output()[0];
1784 if let GreptimeQueryOutput::Records(r) = output {
1785 assert_eq!(r.num_rows(), 4);
1786 assert_eq!(r.num_cols(), 2);
1787 assert_eq!(r.schema.column_schemas[0].name, "numbers");
1788 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
1789 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
1790 assert_eq!(r.rows[0][1], serde_json::Value::Null);
1791 } else {
1792 panic!("invalid output type");
1793 }
1794 }
1795
1796 HttpResponse::Null(resp) => {
1797 assert_eq!(resp.rows(), 4);
1798 }
1799
1800 HttpResponse::Error(err) => unreachable!("{err:?}"),
1801 }
1802 }
1803 }
1804
1805 #[test]
1806 fn test_response_format_misc() {
1807 assert_eq!(ResponseFormat::default(), ResponseFormat::GreptimedbV1);
1808 assert_eq!(ResponseFormat::parse("arrow"), Some(ResponseFormat::Arrow));
1809 assert_eq!(
1810 ResponseFormat::parse("csv"),
1811 Some(ResponseFormat::Csv(false, false))
1812 );
1813 assert_eq!(
1814 ResponseFormat::parse("csvwithnames"),
1815 Some(ResponseFormat::Csv(true, false))
1816 );
1817 assert_eq!(
1818 ResponseFormat::parse("csvwithnamesandtypes"),
1819 Some(ResponseFormat::Csv(true, true))
1820 );
1821 assert_eq!(ResponseFormat::parse("table"), Some(ResponseFormat::Table));
1822 assert_eq!(
1823 ResponseFormat::parse("greptimedb_v1"),
1824 Some(ResponseFormat::GreptimedbV1)
1825 );
1826 assert_eq!(
1827 ResponseFormat::parse("influxdb_v1"),
1828 Some(ResponseFormat::InfluxdbV1)
1829 );
1830 assert_eq!(ResponseFormat::parse("json"), Some(ResponseFormat::Json));
1831 assert_eq!(ResponseFormat::parse("null"), Some(ResponseFormat::Null));
1832
1833 assert_eq!(ResponseFormat::parse("invalid"), None);
1835 assert_eq!(ResponseFormat::parse(""), None);
1836 assert_eq!(ResponseFormat::parse("CSV"), None); assert_eq!(ResponseFormat::Arrow.as_str(), "arrow");
1840 assert_eq!(ResponseFormat::Csv(false, false).as_str(), "csv");
1841 assert_eq!(ResponseFormat::Csv(true, true).as_str(), "csv");
1842 assert_eq!(ResponseFormat::Table.as_str(), "table");
1843 assert_eq!(ResponseFormat::GreptimedbV1.as_str(), "greptimedb_v1");
1844 assert_eq!(ResponseFormat::InfluxdbV1.as_str(), "influxdb_v1");
1845 assert_eq!(ResponseFormat::Json.as_str(), "json");
1846 assert_eq!(ResponseFormat::Null.as_str(), "null");
1847 assert_eq!(ResponseFormat::default().as_str(), "greptimedb_v1");
1848 }
1849
1850 #[test]
1851 fn test_decode_label_name_strict() {
1852 let strict = PromValidationMode::Strict;
1853
1854 assert!(strict.decode_label_name(b"__name__").is_ok());
1856 assert!(strict.decode_label_name(b"job").is_ok());
1857 assert!(strict.decode_label_name(b"instance").is_ok());
1858 assert!(strict.decode_label_name(b"_private").is_ok());
1859 assert!(strict.decode_label_name(b"label_with_underscores").is_ok());
1860 assert!(strict.decode_label_name(b"abc123").is_ok());
1861 assert!(strict.decode_label_name(b"A").is_ok());
1862 assert!(strict.decode_label_name(b"_").is_ok());
1863
1864 assert!(strict.decode_label_name(b"0abc").is_err());
1866 assert!(strict.decode_label_name(b"123").is_err());
1867
1868 assert!(strict.decode_label_name(b"label-name").is_err());
1870 assert!(strict.decode_label_name(b"label.name").is_err());
1871 assert!(strict.decode_label_name(b"label name").is_err());
1872 assert!(strict.decode_label_name(b"label/name").is_err());
1873
1874 assert!(strict.decode_label_name(b"").is_err());
1876
1877 assert!(strict.decode_label_name("ラベル".as_bytes()).is_err());
1879
1880 assert!(strict.decode_label_name(&[0xff, 0xfe]).is_err());
1882 }
1883
1884 #[test]
1885 fn test_decode_label_name_lossy() {
1886 let lossy = PromValidationMode::Lossy;
1887
1888 assert!(lossy.decode_label_name(b"__name__").is_ok());
1890 assert!(lossy.decode_label_name(b"label-name").is_err());
1891 assert!(lossy.decode_label_name(b"0abc").is_err());
1892
1893 assert!(lossy.decode_label_name(&[0xff, 0xfe]).is_err());
1895 }
1896
1897 #[test]
1898 fn test_decode_label_name_unchecked() {
1899 let unchecked = PromValidationMode::Unchecked;
1900
1901 assert!(unchecked.decode_label_name(b"__name__").is_ok());
1903 assert!(unchecked.decode_label_name(b"label-name").is_err());
1904 assert!(unchecked.decode_label_name(b"0abc").is_err());
1905 }
1906
1907 #[test]
1908 fn test_is_valid_prom_label_name_bytes() {
1909 assert!(validate_label_name(b"__name__"));
1910 assert!(validate_label_name(b"job"));
1911 assert!(validate_label_name(b"_"));
1912 assert!(validate_label_name(b"A"));
1913 assert!(validate_label_name(b"abc123"));
1914 assert!(validate_label_name(b"_leading_underscore"));
1915
1916 assert!(!validate_label_name(b""));
1917 assert!(!validate_label_name(b"0starts_with_digit"));
1918 assert!(!validate_label_name(b"has-dash"));
1919 assert!(!validate_label_name(b"has.dot"));
1920 assert!(!validate_label_name(b"has space"));
1921 assert!(!validate_label_name(&[0xff, 0xfe]));
1922 }
1923}