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, State};
25use axum::http::StatusCode as HttpStatusCode;
26use axum::middleware::Next;
27use axum::response::{IntoResponse, Response};
28use axum::routing::Route;
29use axum::serve::ListenerExt;
30use axum::{Extension, Router, middleware, routing};
31use common_base::readable_size::ReadableSize;
32use common_recordbatch::RecordBatch;
33use common_telemetry::{error, info};
34use common_time::Timestamp;
35use common_time::timestamp::TimeUnit;
36use datatypes::data_type::DataType;
37use datatypes::schema::SchemaRef;
38use event::{LogState, LogValidatorRef};
39use futures::FutureExt;
40use http::{HeaderValue, Method};
41use serde::{Deserialize, Serialize};
42use serde_json::Value;
43use session::context::QueryContext;
44use snafu::{ResultExt, ensure};
45use tokio::sync::Mutex;
46use tokio::sync::oneshot::{self, Sender};
47use tonic::codegen::Service;
48use tower::{Layer, ServiceBuilder};
49use tower_http::compression::CompressionLayer;
50use tower_http::cors::{AllowOrigin, Any, CorsLayer};
51use tower_http::decompression::RequestDecompressionLayer;
52use tower_http::trace::TraceLayer;
53
54use self::authorize::AuthState;
55use self::result::table_result::TableResponse;
56use crate::batcher::BatchingProtocol;
57use crate::batcher::logical_table::LogicalTablePendingRowsBatcher;
58use crate::elasticsearch;
59use crate::error::{
60 AddressBindSnafu, AlreadyStartedSnafu, Error, InternalIoSnafu, InvalidHeaderValueSnafu, Result,
61};
62use crate::http::influxdb::{influxdb_health, influxdb_ping, influxdb_write_v1, influxdb_write_v2};
63use crate::http::otlp::OtlpState;
64use crate::http::prom_store::PromStoreState;
65use crate::http::prometheus::{
66 build_info_query, format_query, instant_query, label_values_query, labels_query,
67 metadata_query, parse_query, range_query, series_query,
68};
69use crate::http::result::arrow_result::ArrowResponse;
70use crate::http::result::csv_result::CsvResponse;
71use crate::http::result::error_result::ErrorResponse;
72use crate::http::result::greptime_result_v1::GreptimedbV1Response;
73use crate::http::result::influxdb_result_v1::InfluxdbV1Response;
74use crate::http::result::json_result::JsonResponse;
75use crate::http::result::null_result::NullResponse;
76use crate::interceptor::LogIngestInterceptorRef;
77use crate::metrics::http_metrics_layer;
78use crate::metrics_handler::MetricsHandler;
79use crate::prometheus_handler::PrometheusHandlerRef;
80use crate::query_handler::sql::ServerSqlQueryHandlerRef;
81use crate::query_handler::{
82 DashboardHandlerRef, InfluxdbLineProtocolHandlerRef, JaegerQueryHandlerRef, LogQueryHandlerRef,
83 OpenTelemetryProtocolHandlerRef, OpentsdbProtocolHandlerRef, PipelineHandlerRef,
84 PromStoreProtocolHandlerRef,
85};
86use crate::request_memory_limiter::ServerMemoryLimiter;
87use crate::server::Server;
88
89pub mod authorize;
90#[cfg(feature = "dashboard")]
91mod dashboard;
92pub mod dyn_log;
93pub mod dyn_trace;
94pub mod event;
95pub mod extractor;
96pub mod handler;
97pub mod header;
98pub mod influxdb;
99pub mod jaeger;
100pub mod logs;
101pub mod loki;
102pub mod mem_prof;
103mod memory_limit;
104pub mod opentsdb;
105pub mod otlp;
106pub mod pprof;
107pub mod prom_store;
108pub mod prometheus;
109pub mod result;
110pub mod splunk;
111mod timeout;
112pub mod utils;
113mod workload_scheduler;
114
115use result::HttpOutputWriter;
116pub(crate) use timeout::DynamicTimeoutLayer;
117
118mod client_ip;
119use crate::prom_remote_write::validation::PromValidationMode;
120mod hints;
121mod read_preference;
122mod skip_wal;
123#[cfg(any(test, feature = "testing"))]
124pub mod test_helpers;
125
126pub const HTTP_API_VERSION: &str = "v1";
127pub const HTTP_API_PREFIX: &str = "/v1/";
128pub const HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH: &str = "/v1";
129
130pub trait ExtraHttpRouterProvider: Send + Sync {
132 fn router(&self) -> Router;
134}
135
136pub type ExtraHttpRouterProviderRef = Arc<dyn ExtraHttpRouterProvider>;
137
138#[derive(Clone, Default)]
140pub struct ExtraHttpRouterProviders {
141 providers: Vec<ExtraHttpRouterProviderRef>,
142}
143
144impl ExtraHttpRouterProviders {
145 pub fn new() -> Self {
147 Self::default()
148 }
149
150 pub fn add(&mut self, provider: ExtraHttpRouterProviderRef) {
152 self.providers.push(provider);
153 }
154
155 pub fn iter(&self) -> impl Iterator<Item = &dyn ExtraHttpRouterProvider> {
157 self.providers.iter().map(|x| x.as_ref())
158 }
159}
160
161const DEFAULT_BODY_LIMIT: ReadableSize = ReadableSize::mb(64);
163const DEFAULT_HTTP_API_ADDR_PORT: u16 = 4006;
165
166pub const AUTHORIZATION_HEADER: &str = "x-greptime-auth";
168
169pub static PUBLIC_API_PREFIX: [&str; 4] = [
172 "/v1/influxdb/ping",
173 "/v1/influxdb/health",
174 "/v1/health",
175 "/v1/splunk/services/collector/health",
176];
177
178#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
184pub(crate) enum HttpServerKind {
185 #[default]
188 Full,
189 Api,
191}
192
193#[derive(Default)]
194pub struct HttpServer {
195 router: StdMutex<Router>,
196 shutdown_tx: Mutex<Option<Sender<()>>>,
197 user_provider: Option<UserProviderRef>,
198 memory_limiter: ServerMemoryLimiter,
199
200 options: HttpOptions,
202 batching_protocols: Vec<BatchingProtocol>,
203 logical_batching_protocols: Vec<BatchingProtocol>,
204 bind_addr: Option<SocketAddr>,
205 kind: HttpServerKind,
207}
208
209pub(crate) fn is_namespace(path: &str, root: &str) -> bool {
213 path == root
214 || path
215 .strip_prefix(root)
216 .is_some_and(|suffix| suffix.starts_with('/'))
217}
218
219pub fn is_api_listener_path(path: &str) -> bool {
221 is_namespace(path, HTTP_API_PREFIX_WITHOUT_TRAILING_SLASH) || is_namespace(path, "/dashboard")
222}
223
224#[derive(Clone)]
225struct LogicalBatchingProtocols(Vec<BatchingProtocol>);
226
227pub async fn set_http_write_batching(
229 State(protocol): State<BatchingProtocol>,
230 mut req: Request,
231 next: Next,
232) -> Response {
233 let enabled = req
234 .extensions()
235 .get::<Arc<Vec<BatchingProtocol>>>()
236 .is_some_and(|protocols| protocols.contains(&protocol));
237 let logical_enabled = req
238 .extensions()
239 .get::<LogicalBatchingProtocols>()
240 .is_some_and(|protocols| protocols.0.contains(&protocol));
241 if let Some(ctx) = req.extensions_mut().get_mut::<QueryContext>() {
242 ctx.set_batching_enabled(enabled);
243 ctx.set_logical_batching_enabled(logical_enabled);
244 }
245 next.run(req).await
246}
247
248async fn enforce_api_surface(req: Request, next: Next) -> Response {
252 if !is_api_listener_path(req.uri().path()) {
253 return HttpStatusCode::NOT_FOUND.into_response();
254 }
255 next.run(req).await
256}
257
258impl HttpServer {
259 fn kind(&self) -> &'static str {
261 match self.kind {
262 HttpServerKind::Api => "HTTP API",
263 HttpServerKind::Full => "HTTP",
264 }
265 }
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
269#[serde(default)]
270pub struct HttpOptions {
271 pub addr: String,
272
273 #[serde(with = "humantime_serde")]
276 pub timeout: Duration,
277
278 #[serde(skip)]
279 pub disable_dashboard: bool,
280
281 pub body_limit: ReadableSize,
282
283 pub cors_allowed_origins: Vec<String>,
284
285 pub enable_cors: bool,
286
287 pub enable_api_server: bool,
292 pub api_server_addr: String,
295}
296
297impl Default for HttpOptions {
298 fn default() -> Self {
299 Self {
300 addr: "127.0.0.1:4000".to_string(),
301 timeout: Duration::from_secs(0),
302 disable_dashboard: false,
303 body_limit: DEFAULT_BODY_LIMIT,
304 cors_allowed_origins: Vec::new(),
305 enable_cors: true,
306 enable_api_server: false,
307 api_server_addr: format!("127.0.0.1:{}", DEFAULT_HTTP_API_ADDR_PORT),
308 }
309 }
310}
311
312#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
313pub struct ColumnSchema {
314 name: String,
315 data_type: String,
316}
317
318impl ColumnSchema {
319 pub fn new(name: String, data_type: String) -> ColumnSchema {
320 ColumnSchema { name, data_type }
321 }
322}
323
324#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
325pub struct OutputSchema {
326 column_schemas: Vec<ColumnSchema>,
327}
328
329impl OutputSchema {
330 pub fn new(columns: Vec<ColumnSchema>) -> OutputSchema {
331 OutputSchema {
332 column_schemas: columns,
333 }
334 }
335}
336
337impl From<SchemaRef> for OutputSchema {
338 fn from(schema: SchemaRef) -> OutputSchema {
339 OutputSchema {
340 column_schemas: schema
341 .column_schemas()
342 .iter()
343 .map(|cs| ColumnSchema {
344 name: cs.name.clone(),
345 data_type: cs.data_type.name(),
346 })
347 .collect(),
348 }
349 }
350}
351
352#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
353pub struct HttpRecordsOutput {
354 schema: OutputSchema,
355 rows: Vec<Vec<Value>>,
356 #[serde(default)]
359 total_rows: usize,
360
361 #[serde(skip_serializing_if = "HashMap::is_empty")]
363 #[serde(default)]
364 metrics: HashMap<String, Value>,
365}
366
367impl HttpRecordsOutput {
368 pub fn num_rows(&self) -> usize {
369 self.rows.len()
370 }
371
372 pub fn num_cols(&self) -> usize {
373 self.schema.column_schemas.len()
374 }
375
376 pub fn schema(&self) -> &OutputSchema {
377 &self.schema
378 }
379
380 pub fn rows(&self) -> &Vec<Vec<Value>> {
381 &self.rows
382 }
383}
384
385impl HttpRecordsOutput {
386 pub fn try_new(
387 schema: SchemaRef,
388 recordbatches: Vec<RecordBatch>,
389 ) -> std::result::Result<HttpRecordsOutput, Error> {
390 if recordbatches.is_empty() {
391 Ok(HttpRecordsOutput {
392 schema: OutputSchema::from(schema),
393 rows: vec![],
394 total_rows: 0,
395 metrics: Default::default(),
396 })
397 } else {
398 let num_rows = recordbatches.iter().map(|r| r.num_rows()).sum::<usize>();
399 let mut rows = Vec::with_capacity(num_rows);
400
401 for recordbatch in recordbatches {
402 let mut writer = HttpOutputWriter::new(schema.num_columns(), None);
403 writer.write(recordbatch, &mut rows)?;
404 }
405
406 Ok(HttpRecordsOutput {
407 schema: OutputSchema::from(schema),
408 total_rows: rows.len(),
409 rows,
410 metrics: Default::default(),
411 })
412 }
413 }
414}
415
416#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
417#[serde(rename_all = "lowercase")]
418pub enum GreptimeQueryOutput {
419 AffectedRows(usize),
420 Records(HttpRecordsOutput),
421}
422
423#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
425pub enum ResponseFormat {
426 Arrow,
427 Csv(bool, bool),
429 Table,
430 #[default]
431 GreptimedbV1,
432 InfluxdbV1,
433 Json,
434 Null,
435}
436
437impl ResponseFormat {
438 pub fn parse(s: &str) -> Option<Self> {
439 match s {
440 "arrow" => Some(ResponseFormat::Arrow),
441 "csv" => Some(ResponseFormat::Csv(false, false)),
442 "csvwithnames" => Some(ResponseFormat::Csv(true, false)),
443 "csvwithnamesandtypes" => Some(ResponseFormat::Csv(true, true)),
444 "table" => Some(ResponseFormat::Table),
445 "greptimedb_v1" => Some(ResponseFormat::GreptimedbV1),
446 "influxdb_v1" => Some(ResponseFormat::InfluxdbV1),
447 "json" => Some(ResponseFormat::Json),
448 "null" => Some(ResponseFormat::Null),
449 _ => None,
450 }
451 }
452
453 pub fn as_str(&self) -> &'static str {
454 match self {
455 ResponseFormat::Arrow => "arrow",
456 ResponseFormat::Csv(_, _) => "csv",
457 ResponseFormat::Table => "table",
458 ResponseFormat::GreptimedbV1 => "greptimedb_v1",
459 ResponseFormat::InfluxdbV1 => "influxdb_v1",
460 ResponseFormat::Json => "json",
461 ResponseFormat::Null => "null",
462 }
463 }
464}
465
466#[derive(Debug, Clone, Copy, PartialEq, Eq)]
467pub enum Epoch {
468 Nanosecond,
469 Microsecond,
470 Millisecond,
471 Second,
472}
473
474impl Epoch {
475 pub fn parse(s: &str) -> Option<Epoch> {
476 match s {
481 "ns" => Some(Epoch::Nanosecond),
482 "u" | "µ" => Some(Epoch::Microsecond),
483 "ms" => Some(Epoch::Millisecond),
484 "s" => Some(Epoch::Second),
485 _ => None, }
487 }
488
489 pub fn convert_timestamp(&self, ts: Timestamp) -> Option<Timestamp> {
490 match self {
491 Epoch::Nanosecond => ts.convert_to(TimeUnit::Nanosecond),
492 Epoch::Microsecond => ts.convert_to(TimeUnit::Microsecond),
493 Epoch::Millisecond => ts.convert_to(TimeUnit::Millisecond),
494 Epoch::Second => ts.convert_to(TimeUnit::Second),
495 }
496 }
497}
498
499impl Display for Epoch {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 match self {
502 Epoch::Nanosecond => write!(f, "Epoch::Nanosecond"),
503 Epoch::Microsecond => write!(f, "Epoch::Microsecond"),
504 Epoch::Millisecond => write!(f, "Epoch::Millisecond"),
505 Epoch::Second => write!(f, "Epoch::Second"),
506 }
507 }
508}
509
510#[derive(Serialize, Deserialize, Debug)]
511pub enum HttpResponse {
512 Arrow(ArrowResponse),
513 Csv(CsvResponse),
514 Table(TableResponse),
515 Error(ErrorResponse),
516 GreptimedbV1(GreptimedbV1Response),
517 InfluxdbV1(InfluxdbV1Response),
518 Json(JsonResponse),
519 Null(NullResponse),
520}
521
522impl HttpResponse {
523 pub fn with_execution_time(self, execution_time: u64) -> Self {
524 match self {
525 HttpResponse::Arrow(resp) => resp.with_execution_time(execution_time).into(),
526 HttpResponse::Csv(resp) => resp.with_execution_time(execution_time).into(),
527 HttpResponse::Table(resp) => resp.with_execution_time(execution_time).into(),
528 HttpResponse::GreptimedbV1(resp) => resp.with_execution_time(execution_time).into(),
529 HttpResponse::InfluxdbV1(resp) => resp.with_execution_time(execution_time).into(),
530 HttpResponse::Json(resp) => resp.with_execution_time(execution_time).into(),
531 HttpResponse::Null(resp) => resp.with_execution_time(execution_time).into(),
532 HttpResponse::Error(resp) => resp.with_execution_time(execution_time).into(),
533 }
534 }
535
536 pub fn with_limit(self, limit: usize) -> Self {
537 match self {
538 HttpResponse::Csv(resp) => resp.with_limit(limit).into(),
539 HttpResponse::Table(resp) => resp.with_limit(limit).into(),
540 HttpResponse::GreptimedbV1(resp) => resp.with_limit(limit).into(),
541 HttpResponse::Json(resp) => resp.with_limit(limit).into(),
542 _ => self,
543 }
544 }
545}
546
547pub fn process_with_limit(
548 mut outputs: Vec<GreptimeQueryOutput>,
549 limit: usize,
550) -> Vec<GreptimeQueryOutput> {
551 outputs
552 .drain(..)
553 .map(|data| match data {
554 GreptimeQueryOutput::Records(mut records) => {
555 if records.rows.len() > limit {
556 records.rows.truncate(limit);
557 records.total_rows = limit;
558 }
559 GreptimeQueryOutput::Records(records)
560 }
561 _ => data,
562 })
563 .collect()
564}
565
566impl IntoResponse for HttpResponse {
567 fn into_response(self) -> Response {
568 match self {
569 HttpResponse::Arrow(resp) => resp.into_response(),
570 HttpResponse::Csv(resp) => resp.into_response(),
571 HttpResponse::Table(resp) => resp.into_response(),
572 HttpResponse::GreptimedbV1(resp) => resp.into_response(),
573 HttpResponse::InfluxdbV1(resp) => resp.into_response(),
574 HttpResponse::Json(resp) => resp.into_response(),
575 HttpResponse::Null(resp) => resp.into_response(),
576 HttpResponse::Error(resp) => resp.into_response(),
577 }
578 }
579}
580
581impl From<ArrowResponse> for HttpResponse {
582 fn from(value: ArrowResponse) -> Self {
583 HttpResponse::Arrow(value)
584 }
585}
586
587impl From<CsvResponse> for HttpResponse {
588 fn from(value: CsvResponse) -> Self {
589 HttpResponse::Csv(value)
590 }
591}
592
593impl From<TableResponse> for HttpResponse {
594 fn from(value: TableResponse) -> Self {
595 HttpResponse::Table(value)
596 }
597}
598
599impl From<ErrorResponse> for HttpResponse {
600 fn from(value: ErrorResponse) -> Self {
601 HttpResponse::Error(value)
602 }
603}
604
605impl From<GreptimedbV1Response> for HttpResponse {
606 fn from(value: GreptimedbV1Response) -> Self {
607 HttpResponse::GreptimedbV1(value)
608 }
609}
610
611impl From<InfluxdbV1Response> for HttpResponse {
612 fn from(value: InfluxdbV1Response) -> Self {
613 HttpResponse::InfluxdbV1(value)
614 }
615}
616
617impl From<JsonResponse> for HttpResponse {
618 fn from(value: JsonResponse) -> Self {
619 HttpResponse::Json(value)
620 }
621}
622
623impl From<NullResponse> for HttpResponse {
624 fn from(value: NullResponse) -> Self {
625 HttpResponse::Null(value)
626 }
627}
628
629#[derive(Clone)]
630pub struct ApiState {
631 pub sql_handler: ServerSqlQueryHandlerRef,
632}
633
634#[derive(Clone)]
635pub struct GreptimeOptionsConfigState {
636 pub greptime_config_options: String,
637}
638
639#[derive(Clone)]
640pub struct DashboardState {
641 pub handler: DashboardHandlerRef,
642}
643
644pub struct HttpServerBuilder {
645 options: HttpOptions,
646 batching_protocols: Vec<BatchingProtocol>,
647 logical_batching_protocols: Vec<BatchingProtocol>,
648 user_provider: Option<UserProviderRef>,
649 router: Router,
650 memory_limiter: ServerMemoryLimiter,
651}
652
653impl HttpServerBuilder {
654 pub fn new(options: HttpOptions) -> Self {
655 Self {
656 options,
657 batching_protocols: Vec::new(),
658 logical_batching_protocols: Vec::new(),
659 user_provider: None,
660 router: Router::new(),
661 memory_limiter: ServerMemoryLimiter::default(),
662 }
663 }
664
665 pub fn with_logical_batching_protocols(mut self, protocols: Vec<BatchingProtocol>) -> Self {
667 self.logical_batching_protocols = protocols;
668 self
669 }
670
671 pub fn with_batching_protocols(mut self, protocols: Vec<BatchingProtocol>) -> Self {
673 self.batching_protocols = protocols;
674 self
675 }
676
677 pub fn with_memory_limiter(mut self, limiter: ServerMemoryLimiter) -> Self {
679 self.memory_limiter = limiter;
680 self
681 }
682
683 pub fn with_sql_handler(self, sql_handler: ServerSqlQueryHandlerRef) -> Self {
684 let batch_body_limit = if self.options.body_limit.0 == 0 {
685 4 * 1024 * 1024
686 } else {
687 (self.options.body_limit.0 as usize).min(4 * 1024 * 1024)
688 };
689 let sql_router = HttpServer::route_sql(ApiState { sql_handler }, batch_body_limit);
690
691 Self {
692 router: self
693 .router
694 .nest(&format!("/{HTTP_API_VERSION}"), sql_router),
695 ..self
696 }
697 }
698
699 pub fn with_logs_handler(self, logs_handler: LogQueryHandlerRef) -> Self {
700 let logs_router = HttpServer::route_logs(logs_handler);
701
702 Self {
703 router: self
704 .router
705 .nest(&format!("/{HTTP_API_VERSION}"), logs_router),
706 ..self
707 }
708 }
709
710 pub fn with_opentsdb_handler(self, handler: OpentsdbProtocolHandlerRef) -> Self {
711 Self {
712 router: self.router.nest(
713 &format!("/{HTTP_API_VERSION}/opentsdb"),
714 HttpServer::route_opentsdb(handler),
715 ),
716 ..self
717 }
718 }
719
720 pub fn with_influxdb_handler(self, handler: InfluxdbLineProtocolHandlerRef) -> Self {
721 Self {
722 router: self.router.nest(
723 &format!("/{HTTP_API_VERSION}/influxdb"),
724 HttpServer::route_influxdb(handler, self.memory_limiter.clone()),
725 ),
726 ..self
727 }
728 }
729
730 pub fn with_prom_handler(
731 self,
732 handler: PromStoreProtocolHandlerRef,
733 pipeline_handler: Option<PipelineHandlerRef>,
734 prom_store_with_metric_engine: bool,
735 prom_validation_mode: PromValidationMode,
736 pending_rows_batcher: Option<Arc<LogicalTablePendingRowsBatcher>>,
737 ) -> Self {
738 let state = PromStoreState {
739 prom_store_handler: handler,
740 pipeline_handler,
741 prom_store_with_metric_engine,
742 prom_validation_mode,
743 pending_rows_batcher,
744 memory_limiter: self.memory_limiter.clone(),
745 };
746
747 Self {
748 router: self.router.nest(
749 &format!("/{HTTP_API_VERSION}/prometheus"),
750 HttpServer::route_prom(state),
751 ),
752 ..self
753 }
754 }
755
756 pub fn with_prometheus_handler(self, handler: PrometheusHandlerRef) -> Self {
757 Self {
758 router: self.router.nest(
759 &format!("/{HTTP_API_VERSION}/prometheus/api/v1"),
760 HttpServer::route_prometheus(handler),
761 ),
762 ..self
763 }
764 }
765
766 pub fn with_otlp_handler(
767 self,
768 handler: OpenTelemetryProtocolHandlerRef,
769 with_metric_engine: bool,
770 ) -> Self {
771 Self {
772 router: self.router.nest(
773 &format!("/{HTTP_API_VERSION}/otlp"),
774 HttpServer::route_otlp(handler, with_metric_engine, self.memory_limiter.clone()),
775 ),
776 ..self
777 }
778 }
779
780 pub fn with_user_provider(self, user_provider: UserProviderRef) -> Self {
781 Self {
782 user_provider: Some(user_provider),
783 ..self
784 }
785 }
786
787 pub fn with_metrics_handler(self, handler: MetricsHandler) -> Self {
788 Self {
789 router: self.router.merge(HttpServer::route_metrics(handler)),
790 ..self
791 }
792 }
793
794 pub fn with_log_ingest_handler(
795 self,
796 handler: PipelineHandlerRef,
797 validator: Option<LogValidatorRef>,
798 ingest_interceptor: Option<LogIngestInterceptorRef<Error>>,
799 ) -> Self {
800 let log_state = LogState {
801 log_handler: handler,
802 log_validator: validator,
803 ingest_interceptor,
804 };
805
806 let router = self.router.nest(
807 &format!("/{HTTP_API_VERSION}"),
808 HttpServer::route_pipelines(log_state.clone(), self.memory_limiter.clone()),
809 );
810 let router = router.nest(
812 &format!("/{HTTP_API_VERSION}/events"),
813 #[allow(deprecated)]
814 HttpServer::route_log_deprecated(log_state.clone(), self.memory_limiter.clone()),
815 );
816
817 let router = router.nest(
818 &format!("/{HTTP_API_VERSION}/loki"),
819 HttpServer::route_loki(log_state.clone(), self.memory_limiter.clone()),
820 );
821
822 let router = router.nest(
823 &format!("/{HTTP_API_VERSION}/elasticsearch"),
824 HttpServer::route_elasticsearch(log_state.clone(), self.memory_limiter.clone()),
825 );
826
827 let router = router.nest(
828 &format!("/{HTTP_API_VERSION}/elasticsearch/"),
829 Router::new()
830 .route("/", routing::get(elasticsearch::handle_get_version))
831 .with_state(log_state.clone()),
832 );
833
834 let router = router.nest(
835 &format!("/{HTTP_API_VERSION}/splunk"),
836 HttpServer::route_splunk(log_state, self.memory_limiter.clone()),
837 );
838
839 Self { router, ..self }
840 }
841
842 pub fn with_greptime_config_options(self, opts: String) -> Self {
843 let config_router = HttpServer::route_config(GreptimeOptionsConfigState {
844 greptime_config_options: opts,
845 });
846
847 Self {
848 router: self.router.merge(config_router),
849 ..self
850 }
851 }
852
853 pub fn with_jaeger_handler(self, handler: JaegerQueryHandlerRef) -> Self {
854 Self {
855 router: self.router.nest(
856 &format!("/{HTTP_API_VERSION}/jaeger"),
857 HttpServer::route_jaeger(handler),
858 ),
859 ..self
860 }
861 }
862
863 pub fn with_dashboard_handler(self, handler: DashboardHandlerRef) -> Self {
864 Self {
865 router: self.router.nest(
866 &format!("/{HTTP_API_VERSION}/dashboards"),
867 HttpServer::route_dashboard(handler, self.memory_limiter.clone()),
868 ),
869 ..self
870 }
871 }
872
873 pub fn with_extra_router(self, router: Router) -> Self {
874 Self {
875 router: self.router.merge(router),
876 ..self
877 }
878 }
879
880 pub fn add_layer<L>(self, layer: L) -> Self
881 where
882 L: Layer<Route> + Clone + Send + Sync + 'static,
883 L::Service: Service<Request> + Clone + Send + Sync + 'static,
884 <L::Service as Service<Request>>::Response: IntoResponse + 'static,
885 <L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
886 <L::Service as Service<Request>>::Future: Send + 'static,
887 {
888 Self {
889 router: self.router.layer(layer),
890 ..self
891 }
892 }
893
894 pub fn build(self) -> HttpServer {
895 HttpServer {
896 options: self.options,
897 batching_protocols: self.batching_protocols.clone(),
898 logical_batching_protocols: self.logical_batching_protocols.clone(),
899 user_provider: self.user_provider,
900 shutdown_tx: Mutex::new(None),
901 router: StdMutex::new(self.router),
902 bind_addr: None,
903 memory_limiter: self.memory_limiter,
904 kind: HttpServerKind::Full,
905 }
906 }
907
908 pub fn build_servers(self) -> (HttpServer, Option<HttpServer>) {
927 let api_enabled = self.options.enable_api_server;
928 let api_addr = self.options.api_server_addr.clone();
929
930 let internal = HttpServer {
931 options: self.options,
932 batching_protocols: self.batching_protocols.clone(),
933 logical_batching_protocols: self.logical_batching_protocols.clone(),
934 user_provider: self.user_provider.clone(),
935 shutdown_tx: Mutex::new(None),
936 router: StdMutex::new(self.router.clone()),
937 bind_addr: None,
938 memory_limiter: self.memory_limiter.clone(),
939 kind: HttpServerKind::Full,
940 };
941
942 let api = if api_enabled {
943 let api_options = HttpOptions {
946 addr: api_addr,
947 ..internal.options.clone()
948 };
949 Some(HttpServer {
950 options: api_options,
951 batching_protocols: self.batching_protocols,
952 logical_batching_protocols: self.logical_batching_protocols,
953 user_provider: self.user_provider.clone(),
954 shutdown_tx: Mutex::new(None),
955 router: StdMutex::new(self.router),
956 bind_addr: None,
957 memory_limiter: self.memory_limiter,
958 kind: HttpServerKind::Api,
959 })
960 } else {
961 None
962 };
963
964 (internal, api)
965 }
966}
967
968impl HttpServer {
969 pub fn make_app(&self) -> Router {
979 let mut router = self.router.lock().unwrap().clone();
980
981 router = router
982 .route(
983 &format!("/{HTTP_API_VERSION}/health"),
984 routing::get(handler::health).post(handler::health),
985 )
986 .route("/", routing::get(handler::index))
987 .route(
988 "/health",
989 routing::get(handler::health).post(handler::health),
990 )
991 .route(
992 "/ready",
993 routing::get(handler::health).post(handler::health),
994 )
995 .route("/status", routing::get(handler::status));
996
997 #[cfg(feature = "dashboard")]
1000 {
1001 if !self.options.disable_dashboard {
1002 info!("Enable dashboard service at '/dashboard'");
1003 router = router.route(
1005 "/dashboard",
1006 routing::get(|uri: axum::http::uri::Uri| async move {
1007 let path = uri.path();
1008 let query = uri.query().map(|q| format!("?{}", q)).unwrap_or_default();
1009
1010 let new_uri = format!("{}/{}", path, query);
1011 axum::response::Redirect::permanent(&new_uri)
1012 }),
1013 );
1014
1015 router = router
1019 .route(
1020 "/dashboard/",
1021 routing::get(dashboard::static_handler).post(dashboard::static_handler),
1022 )
1023 .route(
1024 "/dashboard/{*x}",
1025 routing::get(dashboard::static_handler).post(dashboard::static_handler),
1026 );
1027 }
1028 }
1029
1030 router = router.route_layer(middleware::from_fn(http_metrics_layer));
1032
1033 router
1034 }
1035
1036 pub fn build(&self, router: Router) -> Result<Router> {
1039 let timeout_layer = if self.options.timeout != Duration::default() {
1040 Some(
1041 ServiceBuilder::new().layer(
1042 DynamicTimeoutLayer::new(self.options.timeout)
1043 .with_status_code_fn(Self::request_timeout_status_code),
1044 ),
1045 )
1046 } else {
1047 info!("HTTP server timeout is disabled");
1048 None
1049 };
1050 let body_limit_layer = if self.options.body_limit != ReadableSize(0) {
1051 Some(
1052 ServiceBuilder::new()
1053 .layer(DefaultBodyLimit::max(self.options.body_limit.0 as usize)),
1054 )
1055 } else {
1056 info!("HTTP server body limit is disabled");
1057 None
1058 };
1059 let cors_layer = if self.options.enable_cors {
1060 Some(
1061 CorsLayer::new()
1062 .allow_methods([
1063 Method::GET,
1064 Method::POST,
1065 Method::PUT,
1066 Method::DELETE,
1067 Method::HEAD,
1068 ])
1069 .allow_origin(cors_allow_origin(&self.options.cors_allowed_origins)?)
1070 .allow_headers(Any),
1071 )
1072 } else {
1073 info!("HTTP server cross-origin is disabled");
1074 None
1075 };
1076
1077 let router = router
1078 .layer(
1080 ServiceBuilder::new()
1081 .layer(TraceLayer::new_for_http().on_failure(()))
1084 .option_layer(cors_layer)
1085 .option_layer(timeout_layer)
1086 .option_layer(body_limit_layer)
1087 .layer(middleware::from_fn_with_state(
1089 self.memory_limiter.clone(),
1090 memory_limit::memory_limit_middleware,
1091 ))
1092 .layer(middleware::from_fn_with_state(
1094 AuthState::new(self.user_provider.clone()),
1095 authorize::check_http_auth,
1096 ))
1097 .layer(Extension(Arc::new(self.batching_protocols.clone())))
1098 .layer(Extension(LogicalBatchingProtocols(
1099 self.logical_batching_protocols.clone(),
1100 )))
1101 .layer(middleware::from_fn(hints::extract_hints))
1102 .layer(middleware::from_fn(client_ip::log_error_with_client_ip))
1103 .layer(middleware::from_fn(
1104 read_preference::extract_read_preference,
1105 ))
1106 .layer(middleware::from_fn(skip_wal::extract_skip_wal)),
1107 );
1108
1109 let router = router.nest(
1112 "/debug",
1113 Router::new()
1114 .route("/log_level", routing::post(dyn_log::dyn_log_handler))
1116 .route(
1117 "/workload_scheduler",
1118 routing::get(workload_scheduler::get_status_handler),
1119 )
1120 .route(
1121 "/workload_scheduler/enabled",
1122 routing::post(workload_scheduler::set_enabled_handler),
1123 )
1124 .route(
1125 "/workload_scheduler/weights",
1126 routing::post(workload_scheduler::set_weights_handler),
1127 )
1128 .route("/enable_trace", routing::post(dyn_trace::dyn_trace_handler))
1129 .nest(
1130 "/prof",
1131 Router::new()
1132 .route("/cpu", routing::post(pprof::pprof_handler))
1133 .route("/mem", routing::post(mem_prof::mem_prof_handler))
1134 .route("/mem/symbol", routing::post(mem_prof::symbolicate_handler))
1135 .route(
1136 "/mem/activate",
1137 routing::post(mem_prof::activate_heap_prof_handler),
1138 )
1139 .route(
1140 "/mem/deactivate",
1141 routing::post(mem_prof::deactivate_heap_prof_handler),
1142 )
1143 .route(
1144 "/mem/status",
1145 routing::get(mem_prof::heap_prof_status_handler),
1146 ) .route(
1148 "/mem/gdump",
1149 routing::get(mem_prof::gdump_status_handler)
1150 .post(mem_prof::gdump_toggle_handler),
1151 ),
1152 ),
1153 );
1154
1155 if self.kind == HttpServerKind::Api {
1160 Ok(router.layer(middleware::from_fn(enforce_api_surface)))
1161 } else {
1162 Ok(router)
1163 }
1164 }
1165
1166 fn request_timeout_status_code(request: &Request) -> HttpStatusCode {
1167 if request.uri().path() == "/v1/prometheus/write" {
1168 HttpStatusCode::GATEWAY_TIMEOUT
1169 } else {
1170 HttpStatusCode::REQUEST_TIMEOUT
1171 }
1172 }
1173
1174 fn route_metrics<S>(metrics_handler: MetricsHandler) -> Router<S> {
1175 Router::new()
1176 .route("/metrics", routing::get(handler::metrics))
1177 .with_state(metrics_handler)
1178 }
1179
1180 fn route_loki<S>(log_state: LogState, memory_limiter: ServerMemoryLimiter) -> Router<S> {
1181 Router::new()
1182 .route("/api/v1/push", routing::post(loki::loki_ingest))
1183 .layer(middleware::from_fn_with_state(
1184 memory_limiter,
1185 memory_limit::decoded_body_accounting_middleware,
1186 ))
1187 .layer(
1188 ServiceBuilder::new()
1189 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1190 )
1191 .layer(middleware::from_fn_with_state(
1192 BatchingProtocol::Loki,
1193 set_http_write_batching,
1194 ))
1195 .with_state(log_state)
1196 }
1197
1198 fn route_splunk<S>(log_state: LogState, memory_limiter: ServerMemoryLimiter) -> Router<S> {
1199 Router::new()
1200 .route(
1201 "/services/collector/health",
1202 routing::get(splunk::handle_health),
1203 )
1204 .route(
1205 "/services/collector/health/1.0",
1206 routing::get(splunk::handle_health),
1207 )
1208 .route(
1211 "/services/collector/event",
1212 routing::post(splunk::handle_event),
1213 )
1214 .route("/services/collector", routing::post(splunk::handle_event))
1215 .route(
1216 "/services/collector/event/1.0",
1217 routing::post(splunk::handle_event),
1218 )
1219 .route("/services/collector/raw", routing::post(splunk::handle_raw))
1222 .route(
1223 "/services/collector/raw/1.0",
1224 routing::post(splunk::handle_raw),
1225 )
1226 .layer(middleware::from_fn_with_state(
1227 memory_limiter,
1228 memory_limit::decoded_body_accounting_middleware,
1229 ))
1230 .layer(
1231 ServiceBuilder::new()
1232 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1233 )
1234 .layer(middleware::from_fn_with_state(
1235 BatchingProtocol::Splunk,
1236 set_http_write_batching,
1237 ))
1238 .with_state(log_state)
1239 }
1240
1241 fn route_elasticsearch<S>(
1242 log_state: LogState,
1243 memory_limiter: ServerMemoryLimiter,
1244 ) -> Router<S> {
1245 Router::new()
1246 .route(
1248 "/",
1249 routing::head((HttpStatusCode::OK, elasticsearch::elasticsearch_headers())),
1250 )
1251 .route("/", routing::get(elasticsearch::handle_get_version))
1253 .route("/_license", routing::get(elasticsearch::handle_get_license))
1255 .route("/_bulk", routing::post(elasticsearch::handle_bulk_api))
1256 .route(
1257 "/{index}/_bulk",
1258 routing::post(elasticsearch::handle_bulk_api_with_index),
1259 )
1260 .route(
1262 "/_ilm/policy/{*path}",
1263 routing::any((
1264 HttpStatusCode::OK,
1265 elasticsearch::elasticsearch_headers(),
1266 axum::Json(serde_json::json!({})),
1267 )),
1268 )
1269 .route(
1271 "/_index_template/{*path}",
1272 routing::any((
1273 HttpStatusCode::OK,
1274 elasticsearch::elasticsearch_headers(),
1275 axum::Json(serde_json::json!({})),
1276 )),
1277 )
1278 .route(
1281 "/_ingest/{*path}",
1282 routing::any((
1283 HttpStatusCode::OK,
1284 elasticsearch::elasticsearch_headers(),
1285 axum::Json(serde_json::json!({})),
1286 )),
1287 )
1288 .route(
1291 "/_nodes/{*path}",
1292 routing::any((
1293 HttpStatusCode::OK,
1294 elasticsearch::elasticsearch_headers(),
1295 axum::Json(serde_json::json!({})),
1296 )),
1297 )
1298 .route(
1301 "/logstash/{*path}",
1302 routing::any((
1303 HttpStatusCode::OK,
1304 elasticsearch::elasticsearch_headers(),
1305 axum::Json(serde_json::json!({})),
1306 )),
1307 )
1308 .route(
1309 "/_logstash/{*path}",
1310 routing::any((
1311 HttpStatusCode::OK,
1312 elasticsearch::elasticsearch_headers(),
1313 axum::Json(serde_json::json!({})),
1314 )),
1315 )
1316 .layer(middleware::from_fn_with_state(
1317 memory_limiter,
1318 memory_limit::decoded_body_accounting_middleware,
1319 ))
1320 .layer(ServiceBuilder::new().layer(RequestDecompressionLayer::new()))
1321 .layer(middleware::from_fn_with_state(
1322 BatchingProtocol::Elasticsearch,
1323 set_http_write_batching,
1324 ))
1325 .with_state(log_state)
1326 }
1327
1328 #[deprecated(since = "0.11.0", note = "Use `route_pipelines()` instead.")]
1329 fn route_log_deprecated<S>(
1330 log_state: LogState,
1331 memory_limiter: ServerMemoryLimiter,
1332 ) -> Router<S> {
1333 Router::new()
1334 .route("/logs", routing::post(event::log_ingester))
1335 .route(
1336 "/pipelines/{pipeline_name}",
1337 routing::get(event::query_pipeline),
1338 )
1339 .route(
1340 "/pipelines/{pipeline_name}",
1341 routing::post(event::add_pipeline),
1342 )
1343 .route(
1344 "/pipelines/{pipeline_name}",
1345 routing::delete(event::delete_pipeline),
1346 )
1347 .route("/pipelines/dryrun", routing::post(event::pipeline_dryrun))
1348 .layer(middleware::from_fn_with_state(
1349 memory_limiter,
1350 memory_limit::decoded_body_accounting_middleware,
1351 ))
1352 .layer(
1353 ServiceBuilder::new()
1354 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1355 )
1356 .layer(middleware::from_fn_with_state(
1357 BatchingProtocol::Logs,
1358 set_http_write_batching,
1359 ))
1360 .with_state(log_state)
1361 }
1362
1363 fn route_pipelines<S>(log_state: LogState, memory_limiter: ServerMemoryLimiter) -> Router<S> {
1364 Router::new()
1365 .route("/ingest", routing::post(event::log_ingester))
1366 .route(
1367 "/pipelines/{pipeline_name}",
1368 routing::get(event::query_pipeline),
1369 )
1370 .route(
1371 "/pipelines/{pipeline_name}/ddl",
1372 routing::get(event::query_pipeline_ddl),
1373 )
1374 .route(
1375 "/pipelines/{pipeline_name}",
1376 routing::post(event::add_pipeline),
1377 )
1378 .route(
1379 "/pipelines/{pipeline_name}",
1380 routing::delete(event::delete_pipeline),
1381 )
1382 .route("/pipelines/_dryrun", routing::post(event::pipeline_dryrun))
1383 .layer(middleware::from_fn_with_state(
1384 memory_limiter,
1385 memory_limit::decoded_body_accounting_middleware,
1386 ))
1387 .layer(
1388 ServiceBuilder::new()
1389 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1390 )
1391 .layer(middleware::from_fn_with_state(
1392 BatchingProtocol::Logs,
1393 set_http_write_batching,
1394 ))
1395 .with_state(log_state)
1396 }
1397
1398 fn route_sql<S>(api_state: ApiState, batch_body_limit: usize) -> Router<S> {
1399 Router::new()
1400 .route(
1401 "/capabilities",
1402 routing::get(|State(state): State<ApiState>| async move {
1403 let mut capabilities =
1404 serde_json::json!({"metric_packed_import": 1, "metric_packed_export": 1});
1405 if state.sql_handler.supports_metric_batch_ddl() {
1406 capabilities["metric_batch_ddl"] = serde_json::json!(1);
1407 }
1408 axum::Json(capabilities)
1409 }),
1410 )
1411 .route(
1412 "/ddl/logical-tables",
1413 routing::post(handler::create_logical_tables)
1414 .layer(DefaultBodyLimit::max(batch_body_limit)),
1415 )
1416 .route(
1417 "/sql",
1418 routing::get(handler::sql).post(handler::sql).layer(
1419 middleware::from_fn_with_state(
1420 BatchingProtocol::HttpSql,
1421 set_http_write_batching,
1422 ),
1423 ),
1424 )
1425 .route(
1426 "/sql/parse",
1427 routing::get(handler::sql_parse).post(handler::sql_parse),
1428 )
1429 .route(
1430 "/sql/format",
1431 routing::get(handler::sql_format).post(handler::sql_format),
1432 )
1433 .route(
1434 "/promql",
1435 routing::get(handler::promql).post(handler::promql),
1436 )
1437 .route(
1438 "/sql/analyze/stream",
1439 routing::post(handler::sql_analyze_stream),
1440 )
1441 .with_state(api_state)
1442 }
1443
1444 fn route_logs<S>(log_handler: LogQueryHandlerRef) -> Router<S> {
1445 Router::new()
1446 .route("/logs", routing::get(logs::logs).post(logs::logs))
1447 .with_state(log_handler)
1448 }
1449
1450 pub fn route_prometheus<S>(prometheus_handler: PrometheusHandlerRef) -> Router<S> {
1454 Router::new()
1455 .route(
1456 "/format_query",
1457 routing::post(format_query).get(format_query),
1458 )
1459 .route("/status/buildinfo", routing::get(build_info_query))
1460 .route("/query", routing::post(instant_query).get(instant_query))
1461 .route("/query_range", routing::post(range_query).get(range_query))
1462 .route("/labels", routing::post(labels_query).get(labels_query))
1463 .route("/metadata", routing::get(metadata_query))
1464 .route("/series", routing::post(series_query).get(series_query))
1465 .route("/parse_query", routing::post(parse_query).get(parse_query))
1466 .route(
1467 "/label/{label_name}/values",
1468 routing::get(label_values_query),
1469 )
1470 .layer(ServiceBuilder::new().layer(CompressionLayer::new()))
1471 .with_state(prometheus_handler)
1472 }
1473
1474 fn route_prom<S>(state: PromStoreState) -> Router<S> {
1480 let write = routing::post(prom_store::remote_write);
1481 let write = if state.prom_store_with_metric_engine {
1482 write
1483 } else {
1484 write.layer(middleware::from_fn_with_state(
1485 BatchingProtocol::Prom,
1486 set_http_write_batching,
1487 ))
1488 };
1489 Router::new()
1490 .route("/read", routing::post(prom_store::remote_read))
1491 .route("/write", write)
1492 .with_state(state)
1493 }
1494
1495 fn route_influxdb<S>(
1496 influxdb_handler: InfluxdbLineProtocolHandlerRef,
1497 memory_limiter: ServerMemoryLimiter,
1498 ) -> Router<S> {
1499 Router::new()
1500 .route("/write", routing::post(influxdb_write_v1))
1501 .route("/api/v2/write", routing::post(influxdb_write_v2))
1502 .layer(middleware::from_fn_with_state(
1503 memory_limiter,
1504 memory_limit::decoded_body_accounting_middleware,
1505 ))
1506 .layer(
1507 ServiceBuilder::new()
1508 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1509 )
1510 .route("/ping", routing::get(influxdb_ping))
1511 .route("/health", routing::get(influxdb_health))
1512 .layer(middleware::from_fn_with_state(
1513 BatchingProtocol::Influxdb,
1514 set_http_write_batching,
1515 ))
1516 .with_state(influxdb_handler)
1517 }
1518
1519 fn route_opentsdb<S>(opentsdb_handler: OpentsdbProtocolHandlerRef) -> Router<S> {
1520 Router::new()
1521 .route("/api/put", routing::post(opentsdb::put))
1522 .layer(middleware::from_fn_with_state(
1523 BatchingProtocol::Opentsdb,
1524 set_http_write_batching,
1525 ))
1526 .with_state(opentsdb_handler)
1527 }
1528
1529 fn route_otlp<S>(
1530 otlp_handler: OpenTelemetryProtocolHandlerRef,
1531 with_metric_engine: bool,
1532 memory_limiter: ServerMemoryLimiter,
1533 ) -> Router<S> {
1534 Router::new()
1535 .route("/v1/metrics", routing::post(otlp::metrics))
1536 .route("/v1/traces", routing::post(otlp::traces))
1537 .route("/v1/logs", routing::post(otlp::logs))
1538 .layer(middleware::from_fn_with_state(
1539 memory_limiter,
1540 memory_limit::decoded_body_accounting_middleware,
1541 ))
1542 .layer(
1543 ServiceBuilder::new()
1544 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1545 )
1546 .layer(middleware::from_fn_with_state(
1547 BatchingProtocol::Otlp,
1548 set_http_write_batching,
1549 ))
1550 .with_state(OtlpState {
1551 with_metric_engine,
1552 handler: otlp_handler,
1553 })
1554 }
1555
1556 fn route_config<S>(state: GreptimeOptionsConfigState) -> Router<S> {
1557 Router::new()
1558 .route("/config", routing::get(handler::config))
1559 .with_state(state)
1560 }
1561
1562 fn route_jaeger<S>(handler: JaegerQueryHandlerRef) -> Router<S> {
1563 Router::new()
1564 .route("/api/services", routing::get(jaeger::handle_get_services))
1565 .route(
1566 "/api/services/{service_name}/operations",
1567 routing::get(jaeger::handle_get_operations_by_service),
1568 )
1569 .route(
1570 "/api/operations",
1571 routing::get(jaeger::handle_get_operations),
1572 )
1573 .route("/api/traces", routing::get(jaeger::handle_find_traces))
1574 .route(
1575 "/api/traces/{trace_id}",
1576 routing::get(jaeger::handle_get_trace),
1577 )
1578 .with_state(handler)
1579 }
1580
1581 #[cfg(feature = "dashboard")]
1582 fn route_dashboard<S>(
1583 handler: DashboardHandlerRef,
1584 memory_limiter: ServerMemoryLimiter,
1585 ) -> Router<S> {
1586 use crate::http::dashboard::{add_dashboard, delete_dashboard, list_dashboards};
1587
1588 Router::new()
1589 .route("/", routing::get(list_dashboards))
1590 .route("/{dashboard_name}", routing::post(add_dashboard))
1591 .route("/{dashboard_name}", routing::delete(delete_dashboard))
1592 .layer(middleware::from_fn_with_state(
1593 memory_limiter,
1594 memory_limit::decoded_body_accounting_middleware,
1595 ))
1596 .layer(
1597 ServiceBuilder::new()
1598 .layer(RequestDecompressionLayer::new().pass_through_unaccepted(true)),
1599 )
1600 .with_state(DashboardState { handler })
1601 }
1602
1603 #[cfg(not(feature = "dashboard"))]
1604 fn route_dashboard<S>(
1605 handler: DashboardHandlerRef,
1606 _memory_limiter: ServerMemoryLimiter,
1607 ) -> Router<S> {
1608 Router::new().with_state(DashboardState { handler })
1609 }
1610}
1611
1612pub const HTTP_SERVER: &str = "HTTP_SERVER";
1613pub const HTTP_API_SERVER: &str = "HTTP_API_SERVER";
1614
1615pub(crate) fn cors_allow_origin(origins: &[String]) -> Result<AllowOrigin> {
1617 if origins.is_empty() {
1618 return Ok(AllowOrigin::any());
1619 }
1620 let origins = origins
1621 .iter()
1622 .map(|origin| HeaderValue::from_str(origin).context(InvalidHeaderValueSnafu))
1623 .collect::<Result<Vec<_>>>()?;
1624 Ok(AllowOrigin::list(origins))
1625}
1626
1627#[async_trait]
1628impl Server for HttpServer {
1629 async fn shutdown(&self) -> Result<()> {
1630 let mut shutdown_tx = self.shutdown_tx.lock().await;
1631 if let Some(tx) = shutdown_tx.take()
1632 && tx.send(()).is_err()
1633 {
1634 info!("Receiver dropped, the HTTP server has already exited");
1635 }
1636 info!("Shutdown {}", self.kind());
1637
1638 Ok(())
1639 }
1640
1641 async fn start(&mut self, listening: SocketAddr) -> Result<()> {
1642 let (tx, rx) = oneshot::channel();
1643 let serve = {
1644 let mut shutdown_tx = self.shutdown_tx.lock().await;
1645 ensure!(
1646 shutdown_tx.is_none(),
1647 AlreadyStartedSnafu {
1648 server: self.kind()
1649 }
1650 );
1651
1652 let app = self.build(self.make_app())?;
1653 let listener = tokio::net::TcpListener::bind(listening)
1654 .await
1655 .context(AddressBindSnafu { addr: listening })?
1656 .tap_io(|tcp_stream| {
1657 if let Err(e) = tcp_stream.set_nodelay(true) {
1658 error!(e; "Failed to set TCP_NODELAY on incoming connection");
1659 }
1660 });
1661 let serve = axum::serve(
1662 listener,
1663 app.into_make_service_with_connect_info::<SocketAddr>(),
1664 );
1665
1666 *shutdown_tx = Some(tx);
1683
1684 serve
1685 };
1686 let listening = serve.local_addr().context(InternalIoSnafu)?;
1687 info!("{} server is bound to {}", self.kind(), listening);
1688
1689 common_runtime::spawn_global(async move {
1690 if let Err(e) = serve
1691 .with_graceful_shutdown(rx.map(drop))
1692 .await
1693 .context(InternalIoSnafu)
1694 {
1695 error!(e; "Failed to shutdown http server");
1696 }
1697 });
1698
1699 self.bind_addr = Some(listening);
1700 Ok(())
1701 }
1702
1703 fn name(&self) -> &str {
1704 match self.kind {
1705 HttpServerKind::Api => HTTP_API_SERVER,
1706 HttpServerKind::Full => HTTP_SERVER,
1707 }
1708 }
1709
1710 fn bind_addr(&self) -> Option<SocketAddr> {
1711 self.bind_addr
1712 }
1713
1714 fn as_any(&self) -> &dyn std::any::Any {
1715 self
1716 }
1717}
1718
1719#[cfg(test)]
1720mod test {
1721 use std::future::pending;
1722 use std::io::Cursor;
1723 use std::sync::Arc;
1724
1725 use arrow_ipc::reader::StreamReader;
1726 use arrow_schema::DataType;
1727 use axum::http::StatusCode;
1728 use axum::routing::{get, post};
1729 use common_query::{Output, OutputData};
1730 use common_recordbatch::RecordBatches;
1731 use datafusion_expr::LogicalPlan;
1732 use datatypes::prelude::*;
1733 use datatypes::schema::{ColumnSchema, Schema};
1734 use datatypes::vectors::{StringVector, UInt32Vector};
1735 use header::constants::GREPTIME_DB_HEADER_TIMEOUT;
1736 use query::parser::PromQuery;
1737 use query::query_engine::DescribeResult;
1738 use session::context::QueryContextRef;
1739 use sql::statements::statement::Statement;
1740 use tokio::sync::mpsc;
1741 use tokio::time::Instant;
1742
1743 use crate::http::test_helpers::TestClient;
1744 use crate::http::*;
1745 use crate::prom_remote_write::validation::validate_label_name;
1746 use crate::query_handler::sql::SqlQueryHandler;
1747
1748 struct DummyInstance {
1749 _tx: mpsc::Sender<(String, Vec<u8>)>,
1750 }
1751
1752 #[async_trait]
1753 impl SqlQueryHandler for DummyInstance {
1754 async fn do_query(&self, _: &str, _: QueryContextRef) -> Vec<Result<Output>> {
1755 unimplemented!()
1756 }
1757
1758 async fn do_analyze_stream_query(&self, _: &str, _: QueryContextRef) -> Result<Output> {
1759 let stream = common_recordbatch::RecordBatches::empty().as_stream();
1760 Ok(Output::new(OutputData::Stream(stream), Default::default()))
1761 }
1762
1763 async fn do_promql_query(&self, _: &PromQuery, _: QueryContextRef) -> Vec<Result<Output>> {
1764 unimplemented!()
1765 }
1766
1767 async fn do_exec_plan(
1768 &self,
1769 _plan: LogicalPlan,
1770 _stmt: Option<Statement>,
1771 _query_ctx: QueryContextRef,
1772 ) -> Result<Output> {
1773 unimplemented!()
1774 }
1775
1776 async fn do_describe(
1777 &self,
1778 _stmt: sql::statements::statement::Statement,
1779 _query_ctx: QueryContextRef,
1780 ) -> Result<Option<DescribeResult>> {
1781 unimplemented!()
1782 }
1783
1784 async fn is_valid_schema(&self, _catalog: &str, _schema: &str) -> Result<bool> {
1785 Ok(true)
1786 }
1787 }
1788
1789 async fn forever() {
1790 pending().await
1791 }
1792
1793 fn make_test_app(tx: mpsc::Sender<(String, Vec<u8>)>) -> Router {
1794 make_test_app_custom(tx, HttpOptions::default())
1795 }
1796
1797 fn make_test_app_custom(tx: mpsc::Sender<(String, Vec<u8>)>, options: HttpOptions) -> Router {
1798 let instance = Arc::new(DummyInstance { _tx: tx });
1799 let server = HttpServerBuilder::new(options)
1800 .with_sql_handler(instance.clone())
1801 .build();
1802 let app = server
1803 .make_app()
1804 .route("/test/timeout", get(forever))
1805 .route("/v1/prometheus/write", post(forever));
1806 server.build(app).unwrap()
1807 }
1808
1809 fn make_split_builder() -> HttpServerBuilder {
1810 let (tx, _rx) = mpsc::channel(100);
1811 let instance = Arc::new(DummyInstance { _tx: tx });
1812 let options = HttpOptions {
1814 enable_api_server: true,
1815 ..HttpOptions::default()
1816 };
1817 HttpServerBuilder::new(options)
1818 .with_sql_handler(instance)
1819 .with_metrics_handler(MetricsHandler)
1820 .with_greptime_config_options("dummy = \"value\"".to_string())
1821 }
1822
1823 #[tokio::test]
1824 pub async fn test_http_api_options_defaults() {
1825 let opts = HttpOptions::default();
1826 assert!(!opts.enable_api_server);
1828 assert_eq!(opts.api_server_addr, "127.0.0.1:4006");
1829 }
1830
1831 #[tokio::test]
1832 pub async fn test_build_serves_all_routes() {
1833 let server = make_split_builder().build();
1837 assert_eq!(server.name(), HTTP_SERVER);
1838
1839 let app = server.build(server.make_app()).unwrap();
1840 let client = TestClient::new(app).await;
1841
1842 for path in ["/v1/health", "/health", "/status", "/metrics", "/config"] {
1843 let res = client.get(path).send().await;
1844 assert_eq!(
1845 res.status(),
1846 StatusCode::OK,
1847 "internal/full server should serve {path}"
1848 );
1849 }
1850 }
1851
1852 #[tokio::test]
1853 pub async fn test_build_servers_separates_api_and_internal_routes() {
1854 let (internal, api) = make_split_builder().build_servers();
1857 let api = api.expect("API server is explicitly enabled in make_split_builder");
1858 assert_eq!(internal.name(), HTTP_SERVER);
1859 assert_eq!(api.name(), HTTP_API_SERVER);
1860
1861 let internal_app = internal.build(internal.make_app()).unwrap();
1862 let api_app = api.build(api.make_app()).unwrap();
1863
1864 let internal_client = TestClient::new(internal_app).await;
1865 let api_client = TestClient::new(api_app).await;
1866
1867 assert_eq!(
1869 internal_client.get("/v1/health").send().await.status(),
1870 StatusCode::OK
1871 );
1872 assert_eq!(
1873 api_client.get("/v1/health").send().await.status(),
1874 StatusCode::OK
1875 );
1876
1877 for path in ["/health", "/status", "/metrics", "/config"] {
1879 assert_eq!(
1880 internal_client.get(path).send().await.status(),
1881 StatusCode::OK,
1882 "internal/full server should serve {path}"
1883 );
1884 assert_eq!(
1886 api_client.get(path).send().await.status(),
1887 StatusCode::NOT_FOUND,
1888 "API server should NOT serve {path}"
1889 );
1890 }
1891 }
1892
1893 #[test]
1894 fn test_build_servers_api_inherits_http_options() {
1895 let http_opts = HttpOptions {
1897 timeout: Duration::from_secs(42),
1898 body_limit: ReadableSize::mb(128),
1899 cors_allowed_origins: vec!["https://example.com".to_string()],
1900 enable_api_server: true,
1901 ..HttpOptions::default()
1902 };
1903 let (internal, api) = HttpServerBuilder::new(http_opts.clone()).build_servers();
1904 let api = api.expect("API server is explicitly enabled");
1905
1906 assert_eq!(internal.options.addr, http_opts.addr);
1908 assert_eq!(api.options.addr, "127.0.0.1:4006");
1909 assert_eq!(api.options.timeout, http_opts.timeout);
1911 assert_eq!(api.options.body_limit, http_opts.body_limit);
1912 assert_eq!(
1913 api.options.cors_allowed_origins,
1914 http_opts.cors_allowed_origins
1915 );
1916 }
1917
1918 #[test]
1919 fn test_is_api_listener_path() {
1920 assert!(is_api_listener_path("/v1"));
1922 assert!(is_api_listener_path("/v1/"));
1923 assert!(is_api_listener_path("/v1/sql"));
1924 assert!(!is_api_listener_path("/v10/sql"));
1925 assert!(!is_api_listener_path("/v1-internal"));
1926 assert!(is_api_listener_path("/dashboard"));
1928 assert!(is_api_listener_path("/dashboard/app.js"));
1929 assert!(!is_api_listener_path("/dashboard-admin"));
1930 assert!(!is_api_listener_path("/metrics"));
1932 assert!(!is_api_listener_path("/status/plugin"));
1933 assert!(!is_api_listener_path("/health"));
1934 }
1935
1936 #[tokio::test]
1937 async fn packed_capability_requires_auth_on_full_and_api_listeners() {
1938 let (tx, _rx) = mpsc::channel(1);
1939 let provider =
1940 auth::static_user_provider_from_option("static_user_provider:cmd:user=password")
1941 .unwrap();
1942 let (full, api) = HttpServerBuilder::new(HttpOptions {
1943 enable_api_server: true,
1944 ..Default::default()
1945 })
1946 .with_sql_handler(Arc::new(DummyInstance { _tx: tx }))
1947 .with_user_provider(Arc::new(provider))
1948 .build_servers();
1949 for server in [full, api.unwrap()] {
1950 let client = TestClient::new(server.build(server.make_app()).unwrap()).await;
1951 assert_eq!(
1952 client.get("/v1/capabilities").send().await.status(),
1953 StatusCode::UNAUTHORIZED
1954 );
1955 assert_eq!(
1956 client
1957 .post("/v1/ddl/logical-tables")
1958 .form(&[("sql", "SELECT 1")])
1959 .send()
1960 .await
1961 .status(),
1962 StatusCode::UNAUTHORIZED
1963 );
1964 let response = client
1965 .get("/v1/capabilities")
1966 .header("Authorization", "Basic dXNlcjpwYXNzd29yZA==")
1967 .send()
1968 .await;
1969 assert_eq!(response.status(), StatusCode::OK);
1970 assert_eq!(
1971 response.json::<serde_json::Value>().await,
1972 serde_json::json!({"metric_packed_import": 1, "metric_packed_export": 1})
1973 );
1974 }
1975 }
1976
1977 #[tokio::test]
1978 async fn logical_ddl_form_limits_and_unsupported_handler() {
1979 use axum::body::{Body, to_bytes};
1980 use tower::ServiceExt;
1981
1982 async fn post_form(app: Router, body: String) -> (StatusCode, serde_json::Value) {
1983 let response = app
1984 .oneshot(
1985 Request::builder()
1986 .method(Method::POST)
1987 .uri("/v1/ddl/logical-tables")
1988 .header("Content-Type", "application/x-www-form-urlencoded")
1989 .body(Body::from(body))
1990 .unwrap(),
1991 )
1992 .await
1993 .unwrap();
1994 let status = response.status();
1995 let body = to_bytes(response.into_body(), 4096).await.unwrap();
1996 (status, serde_json::from_slice(&body).unwrap())
1997 }
1998
1999 for limit in [0, 64] {
2000 let (tx, _rx) = mpsc::channel(1);
2001 let server = HttpServerBuilder::new(HttpOptions {
2002 body_limit: ReadableSize(limit),
2003 ..Default::default()
2004 })
2005 .with_sql_handler(Arc::new(DummyInstance { _tx: tx }))
2006 .build();
2007 let app = server.build(server.make_app()).unwrap();
2008 let client = TestClient::new(app.clone()).await;
2009 assert_eq!(
2010 client.get("/v1/ddl/logical-tables").send().await.status(),
2011 StatusCode::METHOD_NOT_ALLOWED
2012 );
2013 let small = client
2014 .post("/v1/ddl/logical-tables")
2015 .form(&[("sql", "CREATE TABLE t")])
2016 .send()
2017 .await;
2018 assert_eq!(
2019 small.json::<serde_json::Value>().await["code"],
2020 common_error::status_code::StatusCode::Unsupported as u32
2021 );
2022 let (_, body) = post_form(
2023 app.clone(),
2024 if limit == 0 {
2025 format!("sql={}", "%20".repeat(1024 * 1024))
2026 } else {
2027 "sql=".to_string() + &" ".repeat(65)
2028 },
2029 )
2030 .await;
2031 let code = body["code"].clone();
2032 assert_eq!(
2033 code,
2034 if limit == 0 {
2035 common_error::status_code::StatusCode::Unsupported as u32
2036 } else {
2037 common_error::status_code::StatusCode::InvalidArguments as u32
2038 }
2039 );
2040 for body in [
2041 format!("sql={}", "x".repeat(1024 * 1024 + 1)),
2042 format!("sql={}", "%C3%A9".repeat(512 * 1024 + 1)),
2043 format!("sql=CREATE+TABLE+t&padding={}", "x".repeat(4 * 1024 * 1024)),
2044 ] {
2045 let (status, body) = post_form(app.clone(), body).await;
2046 assert_eq!(status, StatusCode::BAD_REQUEST);
2047 assert_eq!(
2048 body["code"],
2049 common_error::status_code::StatusCode::InvalidArguments as u32
2050 );
2051 }
2052 }
2053 }
2054
2055 #[tokio::test]
2056 pub async fn test_extra_router_respects_api_surface() {
2057 let (tx, _rx) = mpsc::channel(100);
2063 let instance = Arc::new(DummyInstance { _tx: tx });
2064 let options = HttpOptions {
2065 enable_api_server: true,
2066 ..HttpOptions::default()
2067 };
2068 let builder = HttpServerBuilder::new(options)
2069 .with_sql_handler(instance)
2070 .with_extra_router(
2071 Router::new()
2072 .route("/status/plugin", routing::get(|| async { "ok" }))
2073 .route("/v1/plugin", routing::get(|| async { "ok" })),
2074 );
2075 let (full, api) = builder.build_servers();
2076 let api = api.expect("API server is explicitly enabled");
2077
2078 let full_client = TestClient::new(full.build(full.make_app()).unwrap()).await;
2079 let api_client = TestClient::new(api.build(api.make_app()).unwrap()).await;
2080
2081 assert_eq!(
2083 full_client.get("/status/plugin").send().await.status(),
2084 StatusCode::OK
2085 );
2086 assert_eq!(
2088 api_client.get("/status/plugin").send().await.status(),
2089 StatusCode::NOT_FOUND
2090 );
2091
2092 assert_eq!(
2094 full_client.get("/v1/plugin").send().await.status(),
2095 StatusCode::OK
2096 );
2097 assert_eq!(
2098 api_client.get("/v1/plugin").send().await.status(),
2099 StatusCode::OK
2100 );
2101
2102 assert_eq!(
2104 api_client.get("/v10/plugin").send().await.status(),
2105 StatusCode::NOT_FOUND
2106 );
2107 }
2108
2109 #[test]
2110 fn test_build_servers_api_disabled_by_default() {
2111 let (_internal, api) = HttpServerBuilder::new(HttpOptions::default()).build_servers();
2114 assert!(api.is_none());
2115 }
2116
2117 #[tokio::test]
2118 pub async fn test_cors() {
2119 let (tx, _rx) = mpsc::channel(100);
2121 let app = make_test_app(tx);
2122 let client = TestClient::new(app).await;
2123
2124 let res = client.get("/health").send().await;
2125
2126 assert_eq!(res.status(), StatusCode::OK);
2127 assert_eq!(
2128 res.headers()
2129 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2130 .expect("expect cors header origin"),
2131 "*"
2132 );
2133
2134 let res = client.get("/v1/health").send().await;
2135
2136 assert_eq!(res.status(), StatusCode::OK);
2137 assert_eq!(
2138 res.headers()
2139 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2140 .expect("expect cors header origin"),
2141 "*"
2142 );
2143
2144 let res = client
2145 .options("/health")
2146 .header("Access-Control-Request-Headers", "x-greptime-auth")
2147 .header("Access-Control-Request-Method", "DELETE")
2148 .header("Origin", "https://example.com")
2149 .send()
2150 .await;
2151 assert_eq!(res.status(), StatusCode::OK);
2152 assert_eq!(
2153 res.headers()
2154 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2155 .expect("expect cors header origin"),
2156 "*"
2157 );
2158 assert_eq!(
2159 res.headers()
2160 .get(http::header::ACCESS_CONTROL_ALLOW_HEADERS)
2161 .expect("expect cors header headers"),
2162 "*"
2163 );
2164 assert_eq!(
2165 res.headers()
2166 .get(http::header::ACCESS_CONTROL_ALLOW_METHODS)
2167 .expect("expect cors header methods"),
2168 "GET,POST,PUT,DELETE,HEAD"
2169 );
2170 }
2171
2172 #[tokio::test]
2173 pub async fn test_cors_custom_origins() {
2174 let (tx, _rx) = mpsc::channel(100);
2176 let origin = "https://example.com";
2177
2178 let options = HttpOptions {
2179 cors_allowed_origins: vec![origin.to_string()],
2180 ..Default::default()
2181 };
2182
2183 let app = make_test_app_custom(tx, options);
2184 let client = TestClient::new(app).await;
2185
2186 let res = client.get("/health").header("Origin", origin).send().await;
2187
2188 assert_eq!(res.status(), StatusCode::OK);
2189 assert_eq!(
2190 res.headers()
2191 .get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2192 .expect("expect cors header origin"),
2193 origin
2194 );
2195
2196 let res = client
2197 .get("/health")
2198 .header("Origin", "https://notallowed.com")
2199 .send()
2200 .await;
2201
2202 assert_eq!(res.status(), StatusCode::OK);
2203 assert!(
2204 !res.headers()
2205 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2206 );
2207 }
2208
2209 #[tokio::test]
2210 pub async fn test_cors_disabled() {
2211 let (tx, _rx) = mpsc::channel(100);
2213
2214 let options = HttpOptions {
2215 enable_cors: false,
2216 ..Default::default()
2217 };
2218
2219 let app = make_test_app_custom(tx, options);
2220 let client = TestClient::new(app).await;
2221
2222 let res = client.get("/health").send().await;
2223
2224 assert_eq!(res.status(), StatusCode::OK);
2225 assert!(
2226 !res.headers()
2227 .contains_key(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
2228 );
2229 }
2230
2231 #[test]
2232 fn test_http_options_default() {
2233 let default = HttpOptions::default();
2234 assert_eq!("127.0.0.1:4000".to_string(), default.addr);
2235 assert_eq!(Duration::from_secs(0), default.timeout)
2236 }
2237
2238 #[tokio::test]
2239 async fn test_http_options_legacy_analyze_stream_config_is_ignored() {
2240 let options: HttpOptions = serde_json::from_value(serde_json::json!({
2241 "addr": "127.0.0.1:4000",
2242 "timeout": "0s",
2243 "body_limit": "64MiB",
2244 "cors_allowed_origins": [],
2245 "enable_cors": true,
2246 "experimental_enable_explain_analyze_stream": false,
2247 "enable_api_server": false,
2248 "api_server_addr": "127.0.0.1:4006"
2249 }))
2250 .unwrap();
2251 let serialized = serde_json::to_string(&options).unwrap();
2252 assert!(!serialized.contains("experimental_enable_explain_analyze_stream"));
2253
2254 let (tx, _rx) = mpsc::channel(100);
2255 let app = make_test_app_custom(tx, options);
2256 let client = TestClient::new(app).await;
2257 let response = client
2258 .post("/v1/sql/analyze/stream")
2259 .form(&handler::SqlQuery {
2260 sql: Some("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
2261 ..Default::default()
2262 })
2263 .send()
2264 .await;
2265 assert_ne!(response.status(), StatusCode::NOT_FOUND);
2266 }
2267
2268 #[tokio::test]
2269 async fn test_http_server_request_timeout() {
2270 common_telemetry::init_default_ut_logging();
2271
2272 let (tx, _rx) = mpsc::channel(100);
2273 let options = HttpOptions {
2274 timeout: Duration::from_millis(10),
2275 ..Default::default()
2276 };
2277 let app = make_test_app_custom(tx, options);
2278 let client = TestClient::new(app).await;
2279 let res = client.get("/test/timeout").send().await;
2280 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
2281
2282 let res = client.post("/v1/prometheus/write").send().await;
2283 assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
2284
2285 let now = Instant::now();
2286 let res = client
2287 .get("/test/timeout")
2288 .header(GREPTIME_DB_HEADER_TIMEOUT, "20ms")
2289 .send()
2290 .await;
2291 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
2292 let elapsed = now.elapsed();
2293 assert!(elapsed > Duration::from_millis(15));
2294
2295 tokio::time::timeout(
2296 Duration::from_millis(15),
2297 client
2298 .get("/test/timeout")
2299 .header(GREPTIME_DB_HEADER_TIMEOUT, "0s")
2300 .send(),
2301 )
2302 .await
2303 .unwrap_err();
2304
2305 tokio::time::timeout(
2306 Duration::from_millis(15),
2307 client
2308 .get("/test/timeout")
2309 .header(
2310 GREPTIME_DB_HEADER_TIMEOUT,
2311 humantime::format_duration(Duration::default()).to_string(),
2312 )
2313 .send(),
2314 )
2315 .await
2316 .unwrap_err();
2317 }
2318
2319 #[tokio::test]
2320 async fn test_schema_for_empty_response() {
2321 let column_schemas = vec![
2322 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2323 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2324 ];
2325 let schema = Arc::new(Schema::new(column_schemas));
2326
2327 let recordbatches = RecordBatches::try_new(schema.clone(), vec![]).unwrap();
2328 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2329
2330 let json_resp = GreptimedbV1Response::from_output(outputs).await;
2331 if let HttpResponse::GreptimedbV1(json_resp) = json_resp {
2332 let json_output = &json_resp.output[0];
2333 if let GreptimeQueryOutput::Records(r) = json_output {
2334 assert_eq!(r.num_rows(), 0);
2335 assert_eq!(r.num_cols(), 2);
2336 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2337 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2338 } else {
2339 panic!("invalid output type");
2340 }
2341 } else {
2342 panic!("invalid format")
2343 }
2344 }
2345
2346 #[tokio::test]
2347 async fn test_recordbatches_conversion() {
2348 let column_schemas = vec![
2349 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
2350 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
2351 ];
2352 let schema = Arc::new(Schema::new(column_schemas));
2353 let columns: Vec<VectorRef> = vec![
2354 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
2355 Arc::new(StringVector::from(vec![
2356 None,
2357 Some("hello"),
2358 Some("greptime"),
2359 None,
2360 ])),
2361 ];
2362 let recordbatch = RecordBatch::new(schema.clone(), columns).unwrap();
2363
2364 for format in [
2365 ResponseFormat::GreptimedbV1,
2366 ResponseFormat::InfluxdbV1,
2367 ResponseFormat::Csv(true, true),
2368 ResponseFormat::Table,
2369 ResponseFormat::Arrow,
2370 ResponseFormat::Json,
2371 ResponseFormat::Null,
2372 ] {
2373 let recordbatches =
2374 RecordBatches::try_new(schema.clone(), vec![recordbatch.clone()]).unwrap();
2375 let outputs = vec![Ok(Output::new_with_record_batches(recordbatches))];
2376 let json_resp = match format {
2377 ResponseFormat::Arrow => ArrowResponse::from_output(outputs, None).await,
2378 ResponseFormat::Csv(with_names, with_types) => {
2379 CsvResponse::from_output(outputs, with_names, with_types).await
2380 }
2381 ResponseFormat::Table => TableResponse::from_output(outputs).await,
2382 ResponseFormat::GreptimedbV1 => GreptimedbV1Response::from_output(outputs).await,
2383 ResponseFormat::InfluxdbV1 => InfluxdbV1Response::from_output(outputs, None).await,
2384 ResponseFormat::Json => JsonResponse::from_output(outputs).await,
2385 ResponseFormat::Null => NullResponse::from_output(outputs).await,
2386 };
2387
2388 match json_resp {
2389 HttpResponse::GreptimedbV1(resp) => {
2390 let json_output = &resp.output[0];
2391 if let GreptimeQueryOutput::Records(r) = json_output {
2392 assert_eq!(r.num_rows(), 4);
2393 assert_eq!(r.num_cols(), 2);
2394 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2395 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2396 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2397 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2398 } else {
2399 panic!("invalid output type");
2400 }
2401 }
2402 HttpResponse::InfluxdbV1(resp) => {
2403 let json_output = &resp.results()[0];
2404 assert_eq!(json_output.num_rows(), 4);
2405 assert_eq!(json_output.num_cols(), 2);
2406 assert_eq!(json_output.series[0].columns.clone()[0], "numbers");
2407 assert_eq!(
2408 json_output.series[0].values[0][0],
2409 serde_json::Value::from(1)
2410 );
2411 assert_eq!(json_output.series[0].values[0][1], serde_json::Value::Null);
2412 }
2413 HttpResponse::Csv(resp) => {
2414 let output = &resp.output()[0];
2415 if let GreptimeQueryOutput::Records(r) = output {
2416 assert_eq!(r.num_rows(), 4);
2417 assert_eq!(r.num_cols(), 2);
2418 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2419 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2420 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2421 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2422 } else {
2423 panic!("invalid output type");
2424 }
2425 }
2426
2427 HttpResponse::Table(resp) => {
2428 let output = &resp.output()[0];
2429 if let GreptimeQueryOutput::Records(r) = output {
2430 assert_eq!(r.num_rows(), 4);
2431 assert_eq!(r.num_cols(), 2);
2432 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2433 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2434 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2435 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2436 } else {
2437 panic!("invalid output type");
2438 }
2439 }
2440
2441 HttpResponse::Arrow(resp) => {
2442 let output = resp.data;
2443 let mut reader = StreamReader::try_new(Cursor::new(output), None)
2444 .expect("Arrow reader error");
2445 let schema = reader.schema();
2446 assert_eq!(schema.fields[0].name(), "numbers");
2447 assert_eq!(schema.fields[0].data_type(), &DataType::UInt32);
2448 assert_eq!(schema.fields[1].name(), "strings");
2449 assert_eq!(schema.fields[1].data_type(), &DataType::Utf8);
2450
2451 let rb = reader.next().unwrap().expect("read record batch failed");
2452 assert_eq!(rb.num_columns(), 2);
2453 assert_eq!(rb.num_rows(), 4);
2454 }
2455
2456 HttpResponse::Json(resp) => {
2457 let output = &resp.output()[0];
2458 if let GreptimeQueryOutput::Records(r) = output {
2459 assert_eq!(r.num_rows(), 4);
2460 assert_eq!(r.num_cols(), 2);
2461 assert_eq!(r.schema.column_schemas[0].name, "numbers");
2462 assert_eq!(r.schema.column_schemas[0].data_type, "UInt32");
2463 assert_eq!(r.rows[0][0], serde_json::Value::from(1));
2464 assert_eq!(r.rows[0][1], serde_json::Value::Null);
2465 } else {
2466 panic!("invalid output type");
2467 }
2468 }
2469
2470 HttpResponse::Null(resp) => {
2471 assert_eq!(resp.rows(), 4);
2472 }
2473
2474 HttpResponse::Error(err) => unreachable!("{err:?}"),
2475 }
2476 }
2477 }
2478
2479 #[test]
2480 fn test_response_format_misc() {
2481 assert_eq!(ResponseFormat::default(), ResponseFormat::GreptimedbV1);
2482 assert_eq!(ResponseFormat::parse("arrow"), Some(ResponseFormat::Arrow));
2483 assert_eq!(
2484 ResponseFormat::parse("csv"),
2485 Some(ResponseFormat::Csv(false, false))
2486 );
2487 assert_eq!(
2488 ResponseFormat::parse("csvwithnames"),
2489 Some(ResponseFormat::Csv(true, false))
2490 );
2491 assert_eq!(
2492 ResponseFormat::parse("csvwithnamesandtypes"),
2493 Some(ResponseFormat::Csv(true, true))
2494 );
2495 assert_eq!(ResponseFormat::parse("table"), Some(ResponseFormat::Table));
2496 assert_eq!(
2497 ResponseFormat::parse("greptimedb_v1"),
2498 Some(ResponseFormat::GreptimedbV1)
2499 );
2500 assert_eq!(
2501 ResponseFormat::parse("influxdb_v1"),
2502 Some(ResponseFormat::InfluxdbV1)
2503 );
2504 assert_eq!(ResponseFormat::parse("json"), Some(ResponseFormat::Json));
2505 assert_eq!(ResponseFormat::parse("null"), Some(ResponseFormat::Null));
2506
2507 assert_eq!(ResponseFormat::parse("invalid"), None);
2509 assert_eq!(ResponseFormat::parse(""), None);
2510 assert_eq!(ResponseFormat::parse("CSV"), None); assert_eq!(ResponseFormat::Arrow.as_str(), "arrow");
2514 assert_eq!(ResponseFormat::Csv(false, false).as_str(), "csv");
2515 assert_eq!(ResponseFormat::Csv(true, true).as_str(), "csv");
2516 assert_eq!(ResponseFormat::Table.as_str(), "table");
2517 assert_eq!(ResponseFormat::GreptimedbV1.as_str(), "greptimedb_v1");
2518 assert_eq!(ResponseFormat::InfluxdbV1.as_str(), "influxdb_v1");
2519 assert_eq!(ResponseFormat::Json.as_str(), "json");
2520 assert_eq!(ResponseFormat::Null.as_str(), "null");
2521 assert_eq!(ResponseFormat::default().as_str(), "greptimedb_v1");
2522 }
2523
2524 #[test]
2525 fn test_decode_label_name_strict() {
2526 let strict = PromValidationMode::Strict;
2527
2528 assert!(strict.decode_label_name(b"__name__").is_ok());
2530 assert!(strict.decode_label_name(b"job").is_ok());
2531 assert!(strict.decode_label_name(b"instance").is_ok());
2532 assert!(strict.decode_label_name(b"_private").is_ok());
2533 assert!(strict.decode_label_name(b"label_with_underscores").is_ok());
2534 assert!(strict.decode_label_name(b"abc123").is_ok());
2535 assert!(strict.decode_label_name(b"A").is_ok());
2536 assert!(strict.decode_label_name(b"_").is_ok());
2537
2538 assert!(strict.decode_label_name(b"0abc").is_err());
2540 assert!(strict.decode_label_name(b"123").is_err());
2541
2542 assert!(strict.decode_label_name(b"label-name").is_err());
2544 assert!(strict.decode_label_name(b"label.name").is_err());
2545 assert!(strict.decode_label_name(b"label name").is_err());
2546 assert!(strict.decode_label_name(b"label/name").is_err());
2547
2548 assert!(strict.decode_label_name(b"").is_err());
2550
2551 assert!(strict.decode_label_name("ラベル".as_bytes()).is_err());
2553
2554 assert!(strict.decode_label_name(&[0xff, 0xfe]).is_err());
2556 }
2557
2558 #[test]
2559 fn test_decode_label_name_lossy() {
2560 let lossy = PromValidationMode::Lossy;
2561
2562 assert!(lossy.decode_label_name(b"__name__").is_ok());
2564 assert!(lossy.decode_label_name(b"label-name").is_err());
2565 assert!(lossy.decode_label_name(b"0abc").is_err());
2566
2567 assert!(lossy.decode_label_name(&[0xff, 0xfe]).is_err());
2569 }
2570
2571 #[test]
2572 fn test_decode_label_name_unchecked() {
2573 let unchecked = PromValidationMode::Unchecked;
2574
2575 assert!(unchecked.decode_label_name(b"__name__").is_ok());
2577 assert!(unchecked.decode_label_name(b"label-name").is_err());
2578 assert!(unchecked.decode_label_name(b"0abc").is_err());
2579 }
2580
2581 #[test]
2582 fn test_is_valid_prom_label_name_bytes() {
2583 assert!(validate_label_name(b"__name__"));
2584 assert!(validate_label_name(b"job"));
2585 assert!(validate_label_name(b"_"));
2586 assert!(validate_label_name(b"A"));
2587 assert!(validate_label_name(b"abc123"));
2588 assert!(validate_label_name(b"_leading_underscore"));
2589
2590 assert!(!validate_label_name(b""));
2591 assert!(!validate_label_name(b"0starts_with_digit"));
2592 assert!(!validate_label_name(b"has-dash"));
2593 assert!(!validate_label_name(b"has.dot"));
2594 assert!(!validate_label_name(b"has space"));
2595 assert!(!validate_label_name(&[0xff, 0xfe]));
2596 }
2597}
2598
2599#[cfg(test)]
2600mod batching_tests {
2601 use std::sync::{Arc, Mutex};
2602
2603 use axum::http::StatusCode;
2604 use common_query::Output;
2605 use session::context::QueryContextRef;
2606
2607 use crate::batcher::BatchingProtocol;
2608 use crate::error::Result as ServerResult;
2609 use crate::http::test_helpers::TestClient;
2610 use crate::http::{HttpOptions, HttpServerBuilder};
2611 use crate::influxdb::InfluxdbRequest;
2612 use crate::opentsdb::codec::DataPoint;
2613 use crate::query_handler::{InfluxdbLineProtocolHandler, OpentsdbProtocolHandler};
2614
2615 #[tokio::test]
2616 async fn test_batching_selectors_are_independent() {
2617 use axum::routing::post;
2618 use axum::{Extension, Json, Router, middleware};
2619 use session::context::{QueryContext, QueryContextBuilder};
2620
2621 use crate::http::{LogicalBatchingProtocols, set_http_write_batching};
2622 for table in [false, true] {
2623 for logical in [false, true] {
2624 let app = Router::new()
2625 .route(
2626 "/",
2627 post(|Extension(ctx): Extension<QueryContext>| async move {
2628 Json([ctx.batching_enabled(), ctx.logical_batching_enabled()])
2629 }),
2630 )
2631 .route_layer(middleware::from_fn_with_state(
2632 BatchingProtocol::Otlp,
2633 set_http_write_batching,
2634 ))
2635 .layer(Extension(QueryContextBuilder::default().build()))
2636 .layer(Extension(Arc::new(if table {
2637 vec![BatchingProtocol::Otlp]
2638 } else {
2639 vec![]
2640 })))
2641 .layer(Extension(LogicalBatchingProtocols(if logical {
2642 vec![BatchingProtocol::Otlp]
2643 } else {
2644 vec![]
2645 })));
2646 let client = TestClient::new(app).await;
2647 let actual: [bool; 2] = client.post("/").send().await.json().await;
2648 assert_eq!(actual, [table, logical]);
2649 }
2650 }
2651 }
2652
2653 #[derive(Default)]
2654 struct RecordingWriteHandler {
2655 selections: Mutex<Vec<bool>>,
2656 }
2657
2658 #[async_trait::async_trait]
2659 impl OpentsdbProtocolHandler for RecordingWriteHandler {
2660 async fn preflight(&self, _: &[DataPoint], _: QueryContextRef) -> ServerResult<()> {
2661 Ok(())
2662 }
2663
2664 async fn exec(&self, points: Vec<DataPoint>, ctx: QueryContextRef) -> ServerResult<usize> {
2665 self.selections.lock().unwrap().push(ctx.batching_enabled());
2666 Ok(points.len())
2667 }
2668 }
2669
2670 #[async_trait::async_trait]
2671 impl InfluxdbLineProtocolHandler for RecordingWriteHandler {
2672 async fn exec(&self, _: InfluxdbRequest, ctx: QueryContextRef) -> ServerResult<Output> {
2673 self.selections.lock().unwrap().push(ctx.batching_enabled());
2674 Ok(Output::new_with_affected_rows(1))
2675 }
2676 }
2677
2678 #[tokio::test]
2679 async fn test_real_influx_and_opentsdb_routes_keep_selection() {
2680 for protocols in [
2681 vec![],
2682 vec![BatchingProtocol::Influxdb],
2683 vec![BatchingProtocol::Opentsdb],
2684 vec![BatchingProtocol::Influxdb, BatchingProtocol::Opentsdb],
2685 ] {
2686 let handler = Arc::new(RecordingWriteHandler::default());
2687 let influx_enabled = protocols.contains(&BatchingProtocol::Influxdb);
2688 let opentsdb_enabled = protocols.contains(&BatchingProtocol::Opentsdb);
2689 let server = HttpServerBuilder::new(HttpOptions::default())
2690 .with_batching_protocols(protocols)
2691 .with_influxdb_handler(handler.clone())
2692 .with_opentsdb_handler(handler.clone())
2693 .build();
2694 let client = TestClient::new(server.build(server.make_app()).unwrap()).await;
2695 for path in [
2696 "/v1/influxdb/write",
2697 "/v1/influxdb/api/v2/write?bucket=public",
2698 ] {
2699 assert_eq!(
2700 client
2701 .post(path)
2702 .body("cpu value=1 42")
2703 .send()
2704 .await
2705 .status(),
2706 StatusCode::NO_CONTENT
2707 );
2708 }
2709 let point =
2710 serde_json::json!({"metric":"cpu", "timestamp":42, "value":1, "tags":{"host":"a"}});
2711 for query in ["", "?summary", "?details"] {
2712 let response = client
2713 .post(&format!("/v1/opentsdb/api/put{query}"))
2714 .json(&point)
2715 .send()
2716 .await;
2717 assert_eq!(
2718 response.status(),
2719 if query.is_empty() {
2720 StatusCode::NO_CONTENT
2721 } else {
2722 StatusCode::OK
2723 }
2724 );
2725 }
2726 assert_eq!(
2727 *handler.selections.lock().unwrap(),
2728 vec![
2729 influx_enabled,
2730 influx_enabled,
2731 opentsdb_enabled,
2732 opentsdb_enabled,
2733 opentsdb_enabled
2734 ]
2735 );
2736 }
2737 }
2738}