servers/
error.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::any::Any;
16use std::net::SocketAddr;
17use std::string::FromUtf8Error;
18
19use axum::http::StatusCode as HttpStatusCode;
20use axum::response::{IntoResponse, Response};
21use axum::{Json, http};
22use base64::DecodeError;
23use common_base::readable_size::ReadableSize;
24use common_error::define_into_tonic_status;
25use common_error::ext::{BoxedError, ErrorExt};
26use common_error::status_code::StatusCode;
27use common_macro::stack_trace_debug;
28use common_telemetry::{error, warn};
29use common_time::Duration;
30use datafusion::error::DataFusionError;
31use datatypes::prelude::ConcreteDataType;
32use headers::ContentType;
33use http::header::InvalidHeaderValue;
34use query::parser::PromQuery;
35use serde_json::json;
36use snafu::{Location, Snafu};
37
38#[derive(Snafu)]
39#[snafu(visibility(pub))]
40#[stack_trace_debug]
41pub enum Error {
42    #[snafu(display("Failed to bind address: {}", addr))]
43    AddressBind {
44        addr: SocketAddr,
45        #[snafu(source)]
46        error: std::io::Error,
47        #[snafu(implicit)]
48        location: Location,
49    },
50
51    #[snafu(display("Arrow error"))]
52    Arrow {
53        #[snafu(source)]
54        error: arrow_schema::ArrowError,
55    },
56
57    #[snafu(display("Internal error: {}", err_msg))]
58    Internal { err_msg: String },
59
60    #[snafu(display("Unsupported data type: {}, reason: {}", data_type, reason))]
61    UnsupportedDataType {
62        data_type: ConcreteDataType,
63        reason: String,
64    },
65
66    #[snafu(display("Internal IO error"))]
67    InternalIo {
68        #[snafu(source)]
69        error: std::io::Error,
70    },
71
72    #[snafu(display("Tokio IO error: {}", err_msg))]
73    TokioIo {
74        err_msg: String,
75        #[snafu(source)]
76        error: std::io::Error,
77    },
78
79    #[snafu(display("Failed to collect recordbatch"))]
80    CollectRecordbatch {
81        #[snafu(implicit)]
82        location: Location,
83        source: common_recordbatch::error::Error,
84    },
85
86    #[snafu(display("Failed to start HTTP server"))]
87    StartHttp {
88        #[snafu(source)]
89        error: hyper::Error,
90    },
91
92    #[snafu(display("Failed to start gRPC server"))]
93    StartGrpc {
94        #[snafu(source)]
95        error: tonic::transport::Error,
96    },
97
98    #[snafu(display("{} server is already started", server))]
99    AlreadyStarted {
100        server: String,
101        #[snafu(implicit)]
102        location: Location,
103    },
104
105    #[snafu(display("Failed to bind address {}", addr))]
106    TcpBind {
107        addr: SocketAddr,
108        #[snafu(source)]
109        error: std::io::Error,
110    },
111
112    #[snafu(display("Failed to execute query"))]
113    ExecuteQuery {
114        #[snafu(implicit)]
115        location: Location,
116        source: BoxedError,
117    },
118
119    #[snafu(display("Failed to execute plan"))]
120    ExecutePlan {
121        #[snafu(implicit)]
122        location: Location,
123        source: BoxedError,
124    },
125
126    #[snafu(display("Execute gRPC query error"))]
127    ExecuteGrpcQuery {
128        #[snafu(implicit)]
129        location: Location,
130        source: BoxedError,
131    },
132
133    #[snafu(display("Execute gRPC request error"))]
134    ExecuteGrpcRequest {
135        #[snafu(implicit)]
136        location: Location,
137        source: BoxedError,
138    },
139
140    #[snafu(display("Failed to check database validity"))]
141    CheckDatabaseValidity {
142        #[snafu(implicit)]
143        location: Location,
144        source: BoxedError,
145    },
146
147    #[snafu(display("Failed to describe statement"))]
148    DescribeStatement { source: BoxedError },
149
150    #[snafu(display("Pipeline error"))]
151    Pipeline {
152        #[snafu(source)]
153        source: pipeline::error::Error,
154        #[snafu(implicit)]
155        location: Location,
156    },
157
158    #[snafu(display("Not supported: {}", feat))]
159    NotSupported { feat: String },
160
161    #[snafu(display("Invalid request parameter: {}", reason))]
162    InvalidParameter {
163        reason: String,
164        #[snafu(implicit)]
165        location: Location,
166    },
167
168    #[snafu(display(
169        "Too many concurrent large requests, limit: {}, request size: {}",
170        ReadableSize(*limit as u64),
171        ReadableSize(*request_size as u64)
172    ))]
173    TooManyConcurrentRequests {
174        limit: usize,
175        request_size: usize,
176        #[snafu(implicit)]
177        location: Location,
178    },
179
180    #[snafu(display("Invalid query: {}", reason))]
181    InvalidQuery {
182        reason: String,
183        #[snafu(implicit)]
184        location: Location,
185    },
186
187    #[snafu(display("Failed to parse query"))]
188    FailedToParseQuery {
189        #[snafu(implicit)]
190        location: Location,
191        source: sql::error::Error,
192    },
193
194    #[snafu(display("Failed to parse InfluxDB line protocol"))]
195    InfluxdbLineProtocol {
196        #[snafu(implicit)]
197        location: Location,
198        #[snafu(source)]
199        error: influxdb_line_protocol::Error,
200    },
201
202    #[snafu(display("Failed to write row"))]
203    RowWriter {
204        #[snafu(implicit)]
205        location: Location,
206        source: common_grpc::error::Error,
207    },
208
209    #[snafu(display("Failed to convert time precision, name: {}", name))]
210    TimePrecision {
211        name: String,
212        #[snafu(implicit)]
213        location: Location,
214    },
215
216    #[snafu(display("Invalid OpenTSDB Json request"))]
217    InvalidOpentsdbJsonRequest {
218        #[snafu(source)]
219        error: serde_json::error::Error,
220        #[snafu(implicit)]
221        location: Location,
222    },
223
224    #[snafu(display("Failed to decode prometheus remote request"))]
225    DecodePromRemoteRequest {
226        #[snafu(implicit)]
227        location: Location,
228        #[snafu(source)]
229        error: prost::DecodeError,
230    },
231
232    #[snafu(display(
233        "Failed to decode OTLP request (content-type: {content_type}): {error}. The endpoint only accepts 'application/x-protobuf' format."
234    ))]
235    DecodeOtlpRequest {
236        content_type: String,
237        #[snafu(implicit)]
238        location: Location,
239        #[snafu(source)]
240        error: prost::DecodeError,
241    },
242
243    #[snafu(display("Failed to decode Loki request: {error}"))]
244    DecodeLokiRequest {
245        #[snafu(implicit)]
246        location: Location,
247        #[snafu(source)]
248        error: prost::DecodeError,
249    },
250
251    #[snafu(display(
252        "Unsupported content type 'application/json'. OTLP endpoint only supports 'application/x-protobuf'. Please configure your OTLP exporter to use protobuf encoding."
253    ))]
254    UnsupportedJsonContentType {
255        #[snafu(implicit)]
256        location: Location,
257    },
258
259    #[snafu(display(
260        "OTLP metric input have incompatible existing tables, please refer to docs for details"
261    ))]
262    OtlpMetricModeIncompatible {
263        #[snafu(implicit)]
264        location: Location,
265    },
266
267    #[snafu(display("Common Meta error"))]
268    CommonMeta {
269        #[snafu(implicit)]
270        location: Location,
271        #[snafu(source)]
272        source: common_meta::error::Error,
273    },
274
275    #[snafu(display("Failed to decompress snappy prometheus remote request"))]
276    DecompressSnappyPromRemoteRequest {
277        #[snafu(implicit)]
278        location: Location,
279        #[snafu(source)]
280        error: snap::Error,
281    },
282
283    #[snafu(display("Failed to decompress zstd prometheus remote request"))]
284    DecompressZstdPromRemoteRequest {
285        #[snafu(implicit)]
286        location: Location,
287        #[snafu(source)]
288        error: std::io::Error,
289    },
290
291    #[snafu(display("Failed to compress prometheus remote request"))]
292    CompressPromRemoteRequest {
293        #[snafu(implicit)]
294        location: Location,
295        #[snafu(source)]
296        error: snap::Error,
297    },
298
299    #[snafu(display("Invalid prometheus remote request, msg: {}", msg))]
300    InvalidPromRemoteRequest {
301        msg: String,
302        #[snafu(implicit)]
303        location: Location,
304    },
305
306    #[snafu(display("Invalid prometheus remote read query result, msg: {}", msg))]
307    InvalidPromRemoteReadQueryResult {
308        msg: String,
309        #[snafu(implicit)]
310        location: Location,
311    },
312
313    #[snafu(display("Invalid Flight ticket"))]
314    InvalidFlightTicket {
315        #[snafu(source)]
316        error: api::DecodeError,
317        #[snafu(implicit)]
318        location: Location,
319    },
320
321    #[snafu(display("Tls is required for {}, plain connection is rejected", server))]
322    TlsRequired { server: String },
323
324    #[snafu(display("Failed to get user info"))]
325    Auth {
326        #[snafu(implicit)]
327        location: Location,
328        source: auth::error::Error,
329    },
330
331    #[snafu(display("Not found http or grpc authorization header"))]
332    NotFoundAuthHeader {},
333
334    #[snafu(display("Not found influx http authorization info"))]
335    NotFoundInfluxAuth {},
336
337    #[snafu(display("Unsupported http auth scheme, name: {}", name))]
338    UnsupportedAuthScheme { name: String },
339
340    #[snafu(display("Invalid visibility ASCII chars"))]
341    InvalidAuthHeaderInvisibleASCII {
342        #[snafu(source)]
343        error: hyper::header::ToStrError,
344        #[snafu(implicit)]
345        location: Location,
346    },
347
348    #[snafu(display("Invalid utf-8 value"))]
349    InvalidAuthHeaderInvalidUtf8Value {
350        #[snafu(source)]
351        error: FromUtf8Error,
352        #[snafu(implicit)]
353        location: Location,
354    },
355
356    #[snafu(display("Invalid http authorization header"))]
357    InvalidAuthHeader {
358        #[snafu(implicit)]
359        location: Location,
360    },
361
362    #[snafu(display("Invalid base64 value"))]
363    InvalidBase64Value {
364        #[snafu(source)]
365        error: DecodeError,
366        #[snafu(implicit)]
367        location: Location,
368    },
369
370    #[snafu(display("Invalid utf-8 value"))]
371    InvalidUtf8Value {
372        #[snafu(source)]
373        error: FromUtf8Error,
374        #[snafu(implicit)]
375        location: Location,
376    },
377
378    #[snafu(display("Invalid http header value"))]
379    InvalidHeaderValue {
380        #[snafu(source)]
381        error: InvalidHeaderValue,
382        #[snafu(implicit)]
383        location: Location,
384    },
385
386    #[snafu(display("Error accessing catalog"))]
387    Catalog {
388        source: catalog::error::Error,
389        #[snafu(implicit)]
390        location: Location,
391    },
392
393    #[snafu(display("Cannot find requested table: {}.{}.{}", catalog, schema, table))]
394    TableNotFound {
395        catalog: String,
396        schema: String,
397        table: String,
398        #[snafu(implicit)]
399        location: Location,
400    },
401
402    #[cfg(feature = "mem-prof")]
403    #[snafu(display("Failed to dump profile data"))]
404    DumpProfileData {
405        #[snafu(implicit)]
406        location: Location,
407        source: common_mem_prof::error::Error,
408    },
409
410    #[snafu(display("Invalid prepare statement: {}", err_msg))]
411    InvalidPrepareStatement {
412        err_msg: String,
413        #[snafu(implicit)]
414        location: Location,
415    },
416
417    #[snafu(display("Failed to build HTTP response"))]
418    BuildHttpResponse {
419        #[snafu(source)]
420        error: http::Error,
421        #[snafu(implicit)]
422        location: Location,
423    },
424
425    #[snafu(display("Failed to parse PromQL: {query:?}"))]
426    ParsePromQL {
427        query: Box<PromQuery>,
428        #[snafu(implicit)]
429        location: Location,
430        source: query::error::Error,
431    },
432
433    #[snafu(display("Failed to parse timestamp: {}", timestamp))]
434    ParseTimestamp {
435        timestamp: String,
436        #[snafu(implicit)]
437        location: Location,
438        #[snafu(source)]
439        error: query::error::Error,
440    },
441
442    #[snafu(display("{}", reason))]
443    UnexpectedResult {
444        reason: String,
445        #[snafu(implicit)]
446        location: Location,
447    },
448
449    // this error is used for custom error mapping
450    // please do not delete it
451    #[snafu(display("Other error"))]
452    Other {
453        source: BoxedError,
454        #[snafu(implicit)]
455        location: Location,
456    },
457
458    #[snafu(display("Failed to join task"))]
459    JoinTask {
460        #[snafu(source)]
461        error: tokio::task::JoinError,
462        #[snafu(implicit)]
463        location: Location,
464    },
465
466    #[cfg(feature = "pprof")]
467    #[snafu(display("Failed to dump pprof data"))]
468    DumpPprof { source: common_pprof::error::Error },
469
470    #[cfg(not(windows))]
471    #[snafu(display("Failed to update jemalloc metrics"))]
472    UpdateJemallocMetrics {
473        #[snafu(source)]
474        error: tikv_jemalloc_ctl::Error,
475        #[snafu(implicit)]
476        location: Location,
477    },
478
479    #[snafu(display("DataFrame operation error"))]
480    DataFrame {
481        #[snafu(source)]
482        error: datafusion::error::DataFusionError,
483        #[snafu(implicit)]
484        location: Location,
485    },
486
487    #[snafu(display("Failed to convert scalar value"))]
488    ConvertScalarValue {
489        source: datatypes::error::Error,
490        #[snafu(implicit)]
491        location: Location,
492    },
493
494    #[snafu(display("Expected type: {:?}, actual: {:?}", expected, actual))]
495    PreparedStmtTypeMismatch {
496        expected: ConcreteDataType,
497        actual: opensrv_mysql::ColumnType,
498        #[snafu(implicit)]
499        location: Location,
500    },
501
502    #[snafu(display(
503        "Column: {}, {} incompatible, expected: {}, actual: {}",
504        column_name,
505        datatype,
506        expected,
507        actual
508    ))]
509    IncompatibleSchema {
510        column_name: String,
511        datatype: String,
512        expected: i32,
513        actual: i32,
514        #[snafu(implicit)]
515        location: Location,
516    },
517
518    #[snafu(display("Failed to convert to json"))]
519    ToJson {
520        #[snafu(source)]
521        error: serde_json::error::Error,
522        #[snafu(implicit)]
523        location: Location,
524    },
525
526    #[snafu(display("Failed to parse payload as json"))]
527    ParseJson {
528        #[snafu(source)]
529        error: serde_json::error::Error,
530        #[snafu(implicit)]
531        location: Location,
532    },
533
534    #[snafu(display("Invalid Loki labels: {}", msg))]
535    InvalidLokiLabels {
536        msg: String,
537        #[snafu(implicit)]
538        location: Location,
539    },
540
541    #[snafu(display("Invalid Loki JSON request: {}", msg))]
542    InvalidLokiPayload {
543        msg: String,
544        #[snafu(implicit)]
545        location: Location,
546    },
547
548    #[snafu(display("Unsupported content type: {:?}", content_type))]
549    UnsupportedContentType {
550        content_type: ContentType,
551        #[snafu(implicit)]
552        location: Location,
553    },
554
555    #[snafu(display("Failed to decode url"))]
556    UrlDecode {
557        #[snafu(source)]
558        error: FromUtf8Error,
559        #[snafu(implicit)]
560        location: Location,
561    },
562
563    #[snafu(display("Failed to convert Mysql value, error: {}", err_msg))]
564    MysqlValueConversion {
565        err_msg: String,
566        #[snafu(implicit)]
567        location: Location,
568    },
569
570    #[snafu(display("Invalid table name"))]
571    InvalidTableName {
572        #[snafu(source)]
573        error: tonic::metadata::errors::ToStrError,
574        #[snafu(implicit)]
575        location: Location,
576    },
577
578    #[snafu(display("Failed to initialize a watcher for file {}", path))]
579    FileWatch {
580        path: String,
581        #[snafu(source)]
582        error: notify::Error,
583    },
584
585    #[snafu(display("Timestamp overflow: {}", error))]
586    TimestampOverflow {
587        error: String,
588        #[snafu(implicit)]
589        location: Location,
590    },
591
592    #[snafu(display("Unsupported json data type for tag: {} {}", key, ty))]
593    UnsupportedJsonDataTypeForTag {
594        key: String,
595        ty: String,
596        #[snafu(implicit)]
597        location: Location,
598    },
599
600    #[snafu(display("Convert SQL value error"))]
601    ConvertSqlValue {
602        source: datatypes::error::Error,
603        #[snafu(implicit)]
604        location: Location,
605    },
606
607    #[snafu(display("Prepare statement not found: {}", name))]
608    PrepareStatementNotFound {
609        name: String,
610        #[snafu(implicit)]
611        location: Location,
612    },
613
614    #[snafu(display("Invalid elasticsearch input, reason: {}", reason))]
615    InvalidElasticsearchInput {
616        reason: String,
617        #[snafu(implicit)]
618        location: Location,
619    },
620
621    #[snafu(display("Invalid Jaeger query, reason: {}", reason))]
622    InvalidJaegerQuery {
623        reason: String,
624        #[snafu(implicit)]
625        location: Location,
626    },
627
628    #[snafu(display("DataFusion error"))]
629    DataFusion {
630        #[snafu(source)]
631        error: DataFusionError,
632        #[snafu(implicit)]
633        location: Location,
634    },
635
636    #[snafu(display("Overflow while casting `{:?}` to Interval", val))]
637    DurationOverflow { val: Duration },
638
639    #[snafu(display("Failed to handle otel-arrow request, error message: {}", err_msg))]
640    HandleOtelArrowRequest {
641        err_msg: String,
642        #[snafu(implicit)]
643        location: Location,
644    },
645
646    #[snafu(display("Unknown hint: {}", hint))]
647    UnknownHint { hint: String },
648
649    #[snafu(display("Query has been cancelled"))]
650    Cancelled {
651        #[snafu(implicit)]
652        location: Location,
653    },
654
655    #[snafu(display("Service suspended"))]
656    Suspended {
657        #[snafu(implicit)]
658        location: Location,
659    },
660}
661
662pub type Result<T, E = Error> = std::result::Result<T, E>;
663
664impl ErrorExt for Error {
665    fn status_code(&self) -> StatusCode {
666        use Error::*;
667        match self {
668            Internal { .. }
669            | InternalIo { .. }
670            | TokioIo { .. }
671            | StartHttp { .. }
672            | StartGrpc { .. }
673            | TcpBind { .. }
674            | BuildHttpResponse { .. }
675            | Arrow { .. }
676            | FileWatch { .. } => StatusCode::Internal,
677
678            AddressBind { .. }
679            | AlreadyStarted { .. }
680            | InvalidPromRemoteReadQueryResult { .. }
681            | OtlpMetricModeIncompatible { .. } => StatusCode::IllegalState,
682
683            UnsupportedDataType { .. } => StatusCode::Unsupported,
684
685            #[cfg(not(windows))]
686            UpdateJemallocMetrics { .. } => StatusCode::Internal,
687
688            CollectRecordbatch { .. } => StatusCode::EngineExecuteQuery,
689
690            ExecuteQuery { source, .. }
691            | ExecutePlan { source, .. }
692            | ExecuteGrpcQuery { source, .. }
693            | ExecuteGrpcRequest { source, .. }
694            | CheckDatabaseValidity { source, .. } => source.status_code(),
695
696            Pipeline { source, .. } => source.status_code(),
697            CommonMeta { source, .. } => source.status_code(),
698
699            NotSupported { .. }
700            | InvalidParameter { .. }
701            | InvalidQuery { .. }
702            | InfluxdbLineProtocol { .. }
703            | InvalidOpentsdbJsonRequest { .. }
704            | DecodePromRemoteRequest { .. }
705            | DecodeOtlpRequest { .. }
706            | DecodeLokiRequest { .. }
707            | UnsupportedJsonContentType { .. }
708            | CompressPromRemoteRequest { .. }
709            | DecompressSnappyPromRemoteRequest { .. }
710            | DecompressZstdPromRemoteRequest { .. }
711            | InvalidPromRemoteRequest { .. }
712            | InvalidFlightTicket { .. }
713            | InvalidPrepareStatement { .. }
714            | DataFrame { .. }
715            | PreparedStmtTypeMismatch { .. }
716            | TimePrecision { .. }
717            | UrlDecode { .. }
718            | IncompatibleSchema { .. }
719            | MysqlValueConversion { .. }
720            | ParseJson { .. }
721            | InvalidLokiLabels { .. }
722            | InvalidLokiPayload { .. }
723            | UnsupportedContentType { .. }
724            | TimestampOverflow { .. }
725            | UnsupportedJsonDataTypeForTag { .. }
726            | InvalidTableName { .. }
727            | PrepareStatementNotFound { .. }
728            | FailedToParseQuery { .. }
729            | InvalidElasticsearchInput { .. }
730            | InvalidJaegerQuery { .. }
731            | ParseTimestamp { .. }
732            | UnknownHint { .. } => StatusCode::InvalidArguments,
733
734            Catalog { source, .. } => source.status_code(),
735            RowWriter { source, .. } => source.status_code(),
736
737            TlsRequired { .. } => StatusCode::Unknown,
738            Auth { source, .. } => source.status_code(),
739            DescribeStatement { source } => source.status_code(),
740
741            NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
742            InvalidAuthHeaderInvisibleASCII { .. }
743            | UnsupportedAuthScheme { .. }
744            | InvalidAuthHeader { .. }
745            | InvalidBase64Value { .. }
746            | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
747
748            TableNotFound { .. } => StatusCode::TableNotFound,
749
750            #[cfg(feature = "mem-prof")]
751            DumpProfileData { source, .. } => source.status_code(),
752
753            InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
754
755            TooManyConcurrentRequests { .. } => StatusCode::RuntimeResourcesExhausted,
756
757            ParsePromQL { source, .. } => source.status_code(),
758            Other { source, .. } => source.status_code(),
759
760            UnexpectedResult { .. } => StatusCode::Unexpected,
761
762            JoinTask { error, .. } => {
763                if error.is_cancelled() {
764                    StatusCode::Cancelled
765                } else if error.is_panic() {
766                    StatusCode::Unexpected
767                } else {
768                    StatusCode::Unknown
769                }
770            }
771
772            #[cfg(feature = "pprof")]
773            DumpPprof { source, .. } => source.status_code(),
774
775            ConvertScalarValue { source, .. } => source.status_code(),
776
777            ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
778
779            ConvertSqlValue { source, .. } => source.status_code(),
780
781            DurationOverflow { .. } => StatusCode::InvalidArguments,
782
783            HandleOtelArrowRequest { .. } => StatusCode::Internal,
784
785            Cancelled { .. } => StatusCode::Cancelled,
786
787            Suspended { .. } => StatusCode::Suspended,
788        }
789    }
790
791    fn as_any(&self) -> &dyn Any {
792        self
793    }
794}
795
796define_into_tonic_status!(Error);
797
798impl From<std::io::Error> for Error {
799    fn from(e: std::io::Error) -> Self {
800        Error::InternalIo { error: e }
801    }
802}
803
804fn log_error_if_necessary(error: &Error) {
805    if error.status_code().should_log_error() {
806        error!(error; "Failed to handle HTTP request ");
807    } else {
808        warn!(error; "Failed to handle HTTP request ");
809    }
810}
811
812impl IntoResponse for Error {
813    fn into_response(self) -> Response {
814        let error_msg = self.output_msg();
815        let status = status_code_to_http_status(&self.status_code());
816
817        log_error_if_necessary(&self);
818
819        let body = Json(json!({
820            "error": error_msg,
821        }));
822        (status, body).into_response()
823    }
824}
825
826/// Converts [StatusCode] to [HttpStatusCode].
827pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
828    match status_code {
829        StatusCode::Success => HttpStatusCode::OK,
830
831        // When a request is cancelled by the client (e.g., by a client side timeout),
832        // we should return a gateway timeout status code to the external client.
833        StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
834
835        StatusCode::Unsupported
836        | StatusCode::InvalidArguments
837        | StatusCode::InvalidSyntax
838        | StatusCode::RequestOutdated
839        | StatusCode::RegionAlreadyExists
840        | StatusCode::TableColumnExists
841        | StatusCode::TableAlreadyExists
842        | StatusCode::RegionNotFound
843        | StatusCode::DatabaseNotFound
844        | StatusCode::TableNotFound
845        | StatusCode::TableColumnNotFound
846        | StatusCode::PlanQuery
847        | StatusCode::DatabaseAlreadyExists
848        | StatusCode::TriggerAlreadyExists
849        | StatusCode::TriggerNotFound
850        | StatusCode::FlowNotFound
851        | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
852
853        StatusCode::AuthHeaderNotFound
854        | StatusCode::InvalidAuthHeader
855        | StatusCode::UserNotFound
856        | StatusCode::UnsupportedPasswordType
857        | StatusCode::UserPasswordMismatch
858        | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
859
860        StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
861
862        StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
863
864        StatusCode::RegionNotReady
865        | StatusCode::TableUnavailable
866        | StatusCode::RegionBusy
867        | StatusCode::StorageUnavailable
868        | StatusCode::External
869        | StatusCode::Suspended => HttpStatusCode::SERVICE_UNAVAILABLE,
870
871        StatusCode::Internal
872        | StatusCode::Unexpected
873        | StatusCode::IllegalState
874        | StatusCode::Unknown
875        | StatusCode::RuntimeResourcesExhausted
876        | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
877    }
878}