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