Skip to main content

databend_client/
client.rs

1// Copyright 2021 Datafuse Labs
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 crate::auth::{AccessTokenAuth, AccessTokenFileAuth, Auth, BasicAuth, KeyPairAuth};
16use crate::capability::Capability;
17use crate::client_mgr::{GLOBAL_CLIENT_MANAGER, GLOBAL_RUNTIME};
18use crate::error_code::{need_refresh_token, ResponseWithErrorCode};
19use crate::global_cookie_store::GlobalCookieStore;
20use crate::login::{
21    LoginRequest, LoginResponseResult, RefreshResponse, RefreshSessionTokenRequest,
22    SessionTokenInfo,
23};
24use crate::presign::{presign_upload_to_stage, PresignMode, PresignedResponse, Reader};
25use crate::response::LoadResponse;
26use crate::stage::StageLocation;
27use crate::{
28    error::{Error, RequestKind, Result},
29    request::{PaginationConfig, QueryRequest, StageAttachmentConfig},
30    response::QueryResponse,
31    session::SessionState,
32    QueryStats,
33};
34use crate::{Page, Pages};
35use arrow_array::RecordBatch;
36use arrow_ipc::reader::StreamReader;
37use base64::engine::general_purpose::URL_SAFE;
38use base64::Engine;
39use bytes::Bytes;
40use log::{debug, error, info, warn};
41use once_cell::sync::Lazy;
42use parking_lot::Mutex;
43use percent_encoding::percent_decode_str;
44use reqwest::cookie::CookieStore;
45use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
46use reqwest::multipart::{Form, Part};
47use reqwest::{
48    Body, Client as HttpClient, Error as ReqwestError, Request, RequestBuilder, StatusCode,
49};
50use semver::Version;
51use serde::{de, Deserialize};
52use serde_json::{json, Value};
53use std::collections::{BTreeMap, HashMap};
54use std::error::Error as StdError;
55use std::io::ErrorKind;
56use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
57use std::sync::Arc;
58use std::time::{Duration, Instant};
59use tokio::time::sleep;
60use tokio_retry::strategy::jitter;
61use tokio_stream::StreamExt;
62use tokio_util::io::ReaderStream;
63use url::Url;
64
65const HEADER_QUERY_ID: &str = "X-DATABEND-QUERY-ID";
66const HEADER_TENANT: &str = "X-DATABEND-TENANT";
67const HEADER_STICKY_NODE: &str = "X-DATABEND-STICKY-NODE";
68const HEADER_WAREHOUSE: &str = "X-DATABEND-WAREHOUSE";
69const HEADER_STAGE_NAME: &str = "X-DATABEND-STAGE-NAME";
70const HEADER_ROUTE_HINT: &str = "X-DATABEND-ROUTE-HINT";
71const TXN_STATE_ACTIVE: &str = "Active";
72const HEADER_SQL: &str = "X-DATABEND-SQL";
73const HEADER_QUERY_CONTEXT: &str = "X-DATABEND-QUERY-CONTEXT";
74const HEADER_SESSION_ID: &str = "X-DATABEND-SESSION-ID";
75const CONTENT_TYPE_ARROW: &str = "application/vnd.apache.arrow.stream";
76const CONTENT_TYPE_ARROW_OR_JSON: &str = "application/vnd.apache.arrow.stream";
77const DEFAULT_USERNAME: &str = "root";
78
79static VERSION: Lazy<String> = Lazy::new(|| {
80    let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
81    version.to_string()
82});
83
84#[derive(Clone)]
85pub(crate) struct QueryState {
86    pub node_id: String,
87    pub last_access_time: Arc<Mutex<Instant>>,
88    pub timeout_secs: u64,
89}
90
91impl QueryState {
92    pub fn need_heartbeat(&self, now: Instant) -> bool {
93        let t = *self.last_access_time.lock();
94        now.duration_since(t).as_secs() > self.timeout_secs / 2
95    }
96}
97
98struct HttpResponseData {
99    status: StatusCode,
100    headers: HeaderMap,
101    body: Bytes,
102}
103
104pub struct APIClient {
105    pub(crate) session_id: String,
106    cli: HttpClient,
107    scheme: String,
108    host: String,
109    port: u16,
110
111    endpoint: Url,
112
113    auth: Arc<dyn Auth>,
114
115    /// Tenant ID used only in Databend Cloud management mode.
116    /// This is NOT for specifying tenant in normal usage.
117    tenant: Option<String>,
118    warehouse: Mutex<Option<String>>,
119    session_state: Mutex<SessionState>,
120    route_hint: RouteHintGenerator,
121
122    disable_login: bool,
123    query_result_format: String,
124    disable_session_token: bool,
125    session_token_info: Option<Arc<Mutex<(SessionTokenInfo, Instant)>>>,
126
127    closed: AtomicBool,
128
129    server_version: Option<Version>,
130
131    wait_time_secs: Option<i64>,
132    max_rows_in_buffer: Option<i64>,
133    max_rows_per_page: Option<i64>,
134
135    connect_timeout: Duration,
136    page_request_timeout: Duration,
137
138    tls_ca_file: Option<String>,
139
140    presign: Mutex<PresignMode>,
141    last_node_id: Mutex<Option<String>>,
142    last_query_id: Mutex<Option<String>>,
143
144    capability: Capability,
145
146    queries_need_heartbeat: Mutex<HashMap<String, QueryState>>,
147
148    retry_count: u32,
149    retry_delay_secs: u64,
150}
151
152impl Drop for APIClient {
153    fn drop(&mut self) {
154        self.close_with_spawn()
155    }
156}
157
158impl APIClient {
159    fn has_transient_io_source(mut source: Option<&(dyn StdError + 'static)>) -> bool {
160        while let Some(err) = source {
161            if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
162                match io_err.kind() {
163                    ErrorKind::TimedOut
164                    | ErrorKind::UnexpectedEof
165                    | ErrorKind::ConnectionReset
166                    | ErrorKind::ConnectionAborted
167                    | ErrorKind::BrokenPipe
168                    | ErrorKind::NotConnected
169                    | ErrorKind::WouldBlock
170                    | ErrorKind::Interrupted => return true,
171                    _ => {}
172                }
173            }
174            source = err.source();
175        }
176        false
177    }
178
179    fn retry_reason_for_reqwest(err: &ReqwestError) -> Option<&'static str> {
180        if err.is_timeout() {
181            Some("request timeout")
182        } else if err.is_connect() {
183            Some("connection error")
184        } else if err.is_request() {
185            Some("request error")
186        } else if err.is_body() || err.is_decode() {
187            if Self::has_transient_io_source(err.source()) {
188                Some("response read transient I/O error")
189            } else {
190                None
191            }
192        } else {
193            None
194        }
195    }
196
197    fn request_query_id(request: &Request) -> Option<String> {
198        request
199            .headers()
200            .get(HEADER_QUERY_ID)
201            .and_then(|v| v.to_str().ok())
202            .map(|v| v.to_string())
203    }
204
205    fn with_request_context(
206        reqwest_error: ReqwestError,
207        request_kind: &RequestKind,
208        request: &Request,
209        retry_times: Option<u32>,
210    ) -> Error {
211        let error: Error = reqwest_error.into();
212        let error = error.with_context(request_kind.clone());
213        let error = if let Some(retry_times) = retry_times {
214            error.with_retry_times(retry_times)
215        } else {
216            error
217        };
218        if let Some(query_id) = Self::request_query_id(request) {
219            error.with_query_id(query_id)
220        } else {
221            error
222        }
223    }
224
225    fn status_body_to_error(status: StatusCode, body: &[u8]) -> Error {
226        match serde_json::from_slice::<ResponseWithErrorCode>(body) {
227            Ok(resp) if status == StatusCode::UNAUTHORIZED => Error::AuthFailure(resp.error),
228            Ok(resp) => Error::Logic(status, resp.error),
229            Err(_) => Error::response_error(status, body),
230        }
231    }
232
233    pub async fn new(dsn: &str, name: Option<String>) -> Result<Arc<Self>> {
234        let mut client = Self::from_dsn(dsn).await?;
235        client.build_client(name).await?;
236        if !client.disable_login {
237            client.login().await?;
238        }
239        if client.session_id.is_empty() {
240            client.session_id = format!("no_login_{}", uuid::Uuid::new_v4());
241        }
242        let client = Arc::new(client);
243        client.check_presign().await?;
244        GLOBAL_CLIENT_MANAGER.register_client(client.clone()).await;
245        Ok(client)
246    }
247
248    pub fn capability(&self) -> &Capability {
249        &self.capability
250    }
251
252    fn set_presign_mode(&self, mode: PresignMode) {
253        *self.presign.lock() = mode
254    }
255    fn get_presign_mode(&self) -> PresignMode {
256        *self.presign.lock()
257    }
258
259    async fn from_dsn(dsn: &str) -> Result<Self> {
260        let u = Url::parse(dsn)?;
261        let mut client = Self::default();
262        if let Some(host) = u.host_str() {
263            client.host = host.to_string();
264        }
265
266        let username = u.username().to_string();
267        if !username.is_empty() {
268            let password = u.password().unwrap_or_default();
269            let password = percent_decode_str(password).decode_utf8()?;
270            client.auth = Arc::new(BasicAuth::new(&username, password));
271        }
272
273        let mut session_state = SessionState::default();
274
275        let database = u.path().trim_start_matches('/');
276        if !database.is_empty() {
277            session_state.set_database(database);
278        }
279
280        let mut private_key_file: Option<String> = None;
281        let mut private_key_passphrase_file: Option<String> = None;
282
283        let mut scheme = "https";
284        for (k, v) in u.query_pairs() {
285            match k.as_ref() {
286                "wait_time_secs" => {
287                    client.wait_time_secs = Some(v.parse()?);
288                }
289                "max_rows_in_buffer" => {
290                    client.max_rows_in_buffer = Some(v.parse()?);
291                }
292                "max_rows_per_page" => {
293                    client.max_rows_per_page = Some(v.parse()?);
294                }
295                "connect_timeout" => client.connect_timeout = Duration::from_secs(v.parse()?),
296                "page_request_timeout_secs" => {
297                    client.page_request_timeout = {
298                        let secs: u64 = v.parse()?;
299                        Duration::from_secs(secs)
300                    };
301                }
302                "presign" => {
303                    let presign_mode = match v.as_ref() {
304                        "auto" => PresignMode::Auto,
305                        "detect" => PresignMode::Detect,
306                        "on" => PresignMode::On,
307                        "off" => PresignMode::Off,
308                        _ => {
309                            return Err(Error::BadArgument(format!(
310                            "Invalid value for presign: {v}, should be one of auto/detect/on/off"
311                        )))
312                        }
313                    };
314                    client.set_presign_mode(presign_mode);
315                }
316                // NOTE: `tenant` is only used in Databend Cloud management mode.
317                // It is not intended for daily use to specify a tenant.
318                "tenant" => {
319                    client.tenant = Some(v.to_string());
320                }
321                "warehouse" => {
322                    client.warehouse = Mutex::new(Some(v.to_string()));
323                }
324                "role" => session_state.set_role(v),
325                "sslmode" => match v.as_ref() {
326                    "disable" => scheme = "http",
327                    "require" | "enable" => scheme = "https",
328                    _ => {
329                        return Err(Error::BadArgument(format!(
330                            "Invalid value for sslmode: {v}"
331                        )))
332                    }
333                },
334                "tls_ca_file" => {
335                    client.tls_ca_file = Some(v.to_string());
336                }
337                "access_token" => {
338                    client.auth = Arc::new(AccessTokenAuth::new(v));
339                }
340                "access_token_file" => {
341                    client.auth = Arc::new(AccessTokenFileAuth::new(v));
342                }
343                "private_key_file" => {
344                    private_key_file = Some(v.to_string());
345                }
346                "private_key_passphrase_file" => {
347                    private_key_passphrase_file = Some(v.to_string());
348                }
349                "login" => {
350                    client.disable_login = match v.as_ref() {
351                        "disable" => true,
352                        "enable" => false,
353                        _ => {
354                            return Err(Error::BadArgument(format!("Invalid value for login: {v}")))
355                        }
356                    }
357                }
358                "session_token" => {
359                    client.disable_session_token = match v.as_ref() {
360                        "disable" => true,
361                        "enable" => false,
362                        _ => {
363                            return Err(Error::BadArgument(format!(
364                                "Invalid value for session_token: {v}"
365                            )))
366                        }
367                    }
368                }
369                "body_format" | "query_result_format" => {
370                    let v = v.to_string().to_lowercase();
371                    match v.as_str() {
372                        "json" | "arrow" => client.query_result_format = v.to_string(),
373                        _ => {
374                            return Err(Error::BadArgument(format!(
375                                "Invalid value for query_result_format: {v}"
376                            )))
377                        }
378                    }
379                }
380                "retry_count" => {
381                    client.retry_count = v.parse()?;
382                }
383                "retry_delay_secs" => {
384                    client.retry_delay_secs = v.parse()?;
385                }
386                _ => {
387                    session_state.set(k, v);
388                }
389            }
390        }
391        // If private_key_file is specified, use KeyPairAuth.
392        if let Some(key_file) = private_key_file {
393            if username.is_empty() {
394                return Err(Error::BadArgument(
395                    "username is required for key-pair authentication".to_string(),
396                ));
397            }
398            client.auth = Arc::new(KeyPairAuth::new(
399                &username,
400                &key_file,
401                private_key_passphrase_file.as_deref(),
402            )?);
403        }
404        client.port = match u.port() {
405            Some(p) => p,
406            None => match scheme {
407                "http" => 80,
408                "https" => 443,
409                _ => unreachable!(),
410            },
411        };
412        client.scheme = scheme.to_string();
413        client.endpoint = Url::parse(&format!("{}://{}:{}", scheme, client.host, client.port))?;
414        client.session_state = Mutex::new(session_state);
415
416        Ok(client)
417    }
418
419    pub fn host(&self) -> &str {
420        self.host.as_str()
421    }
422
423    pub fn port(&self) -> u16 {
424        self.port
425    }
426
427    pub fn scheme(&self) -> &str {
428        self.scheme.as_str()
429    }
430
431    async fn build_client(&mut self, name: Option<String>) -> Result<()> {
432        let ua = match name {
433            Some(n) => n,
434            None => format!("databend-client-rust/{}", VERSION.as_str()),
435        };
436        let cookie_provider = GlobalCookieStore::new();
437        let cookie = HeaderValue::from_str("cookie_enabled=true").unwrap();
438        let mut initial_cookies = [&cookie].into_iter();
439        cookie_provider.set_cookies(&mut initial_cookies, &Url::parse("https://a.com").unwrap());
440        let mut cli_builder = HttpClient::builder()
441            .user_agent(ua)
442            .connect_timeout(self.connect_timeout)
443            .cookie_provider(Arc::new(cookie_provider))
444            .pool_idle_timeout(Duration::from_secs(1));
445        #[cfg(any(feature = "rustls", feature = "native-tls"))]
446        if self.scheme == "https" {
447            if let Some(ref ca_file) = self.tls_ca_file {
448                let cert_pem = tokio::fs::read(ca_file).await?;
449                let cert = reqwest::Certificate::from_pem(&cert_pem)?;
450                cli_builder = cli_builder.add_root_certificate(cert);
451            }
452        }
453        self.cli = cli_builder.build()?;
454        Ok(())
455    }
456
457    async fn check_presign(self: &Arc<Self>) -> Result<()> {
458        let mode = match self.get_presign_mode() {
459            PresignMode::Auto => {
460                if self.host.ends_with(".databend.com")
461                    || self.host.ends_with(".databend.cn")
462                    || self.host.ends_with(".tidbcloud.com")
463                {
464                    PresignMode::On
465                } else {
466                    PresignMode::Off
467                }
468            }
469            PresignMode::Detect => match self.get_presigned_upload_url("@~/.bendsql/check").await {
470                Ok(_) => PresignMode::On,
471                Err(e) => {
472                    warn!("presign mode off with error detected: {e}");
473                    PresignMode::Off
474                }
475            },
476            mode => mode,
477        };
478        self.set_presign_mode(mode);
479        Ok(())
480    }
481
482    pub fn current_warehouse(&self) -> Option<String> {
483        let guard = self.warehouse.lock();
484        guard.clone()
485    }
486
487    pub fn current_catalog(&self) -> Option<String> {
488        let guard = self.session_state.lock();
489        guard.catalog.clone()
490    }
491
492    pub fn current_database(&self) -> Option<String> {
493        let guard = self.session_state.lock();
494        guard.database.clone()
495    }
496
497    pub fn set_warehouse(&self, warehouse: impl Into<String>) {
498        let mut guard = self.warehouse.lock();
499        *guard = Some(warehouse.into());
500    }
501
502    pub fn set_database(&self, database: impl Into<String>) {
503        let mut guard = self.session_state.lock();
504        guard.set_database(database);
505    }
506
507    pub fn set_role(&self, role: impl Into<String>) {
508        let mut guard = self.session_state.lock();
509        guard.set_role(role);
510    }
511
512    pub fn set_session(&self, key: impl Into<String>, value: impl Into<String>) {
513        let mut guard = self.session_state.lock();
514        guard.set(key, value);
515    }
516
517    pub async fn current_role(&self) -> Option<String> {
518        let guard = self.session_state.lock();
519        guard.role.clone()
520    }
521
522    fn in_active_transaction(&self) -> bool {
523        let guard = self.session_state.lock();
524        guard
525            .txn_state
526            .as_ref()
527            .map(|s| s.eq_ignore_ascii_case(TXN_STATE_ACTIVE))
528            .unwrap_or(false)
529    }
530
531    pub fn username(&self) -> String {
532        self.auth.username()
533    }
534
535    fn gen_query_id(&self) -> String {
536        uuid::Uuid::now_v7().simple().to_string()
537    }
538
539    async fn handle_session(&self, session: &Option<SessionState>) {
540        let session = match session {
541            Some(session) => session,
542            None => return,
543        };
544
545        // save the updated session state from the server side
546        {
547            let mut session_state = self.session_state.lock();
548            *session_state = session.clone();
549        }
550
551        // process warehouse changed via session settings
552        if let Some(settings) = session.settings.as_ref() {
553            if let Some(v) = settings.get("warehouse") {
554                let mut warehouse = self.warehouse.lock();
555                *warehouse = Some(v.clone());
556            }
557        }
558    }
559
560    pub fn set_last_node_id(&self, node_id: String) {
561        *self.last_node_id.lock() = Some(node_id)
562    }
563
564    pub fn set_last_query_id(&self, query_id: Option<String>) {
565        *self.last_query_id.lock() = query_id
566    }
567
568    pub fn last_query_id(&self) -> Option<String> {
569        self.last_query_id.lock().clone()
570    }
571
572    fn last_node_id(&self) -> Option<String> {
573        self.last_node_id.lock().clone()
574    }
575
576    fn handle_warnings(&self, resp: &QueryResponse) {
577        if let Some(warnings) = &resp.warnings {
578            for w in warnings {
579                warn!(target: "server_warnings", "server warning: {w}");
580            }
581        }
582    }
583
584    pub async fn start_query(
585        self: &Arc<Self>,
586        sql: &str,
587        need_progress: bool,
588        params: Option<serde_json::Value>,
589    ) -> Result<Pages> {
590        info!("start query: {sql}");
591        let (resp, batches) = self.start_query_inner(sql, None, false, params).await?;
592        Pages::new(self.clone(), resp, batches, need_progress)
593    }
594
595    pub fn finalize_query(self: &Arc<Self>, query_id: &str) {
596        let mut mgr = self.queries_need_heartbeat.lock();
597        if let Some(state) = mgr.remove(query_id) {
598            let self_cloned = self.clone();
599            let query_id = query_id.to_owned();
600            GLOBAL_RUNTIME.spawn(async move {
601                if let Err(e) = self_cloned
602                    .end_query(&query_id, false, Some(state.node_id.as_str()))
603                    .await
604                {
605                    error!("failed to final query {query_id}: {e}");
606                }
607            });
608        }
609    }
610
611    fn wrap_auth_or_session_token(&self, builder: RequestBuilder) -> Result<RequestBuilder> {
612        if let Some(info) = &self.session_token_info {
613            let info = info.lock();
614            Ok(builder.bearer_auth(info.0.session_token.clone()))
615        } else {
616            self.auth.wrap(builder)
617        }
618    }
619
620    async fn start_query_inner(
621        &self,
622        sql: &str,
623        stage_attachment_config: Option<StageAttachmentConfig<'_>>,
624        force_json_body: bool,
625        params: Option<serde_json::Value>,
626    ) -> Result<(QueryResponse, Vec<RecordBatch>)> {
627        if !self.in_active_transaction() {
628            self.route_hint.next();
629        }
630        let endpoint = self.endpoint.join("v1/query")?;
631
632        // body
633        let session_state = self.session_state();
634        let need_sticky = session_state.need_sticky.unwrap_or(false);
635        let mut req = QueryRequest::new(sql)
636            .with_pagination(self.make_pagination())
637            .with_session(Some(session_state))
638            .with_stage_attachment(stage_attachment_config)
639            .with_params(params);
640
641        // headers
642        let query_id = self.gen_query_id();
643        let mut headers = self.make_headers(Some(&query_id))?;
644        if self.capability.arrow_data && self.query_result_format == "arrow" && !force_json_body {
645            debug!("accept arrow data");
646            headers.insert(ACCEPT, HeaderValue::from_static(CONTENT_TYPE_ARROW_OR_JSON));
647            req = req.with_arrow();
648        }
649
650        if need_sticky {
651            if let Some(node_id) = self.last_node_id() {
652                headers.insert(HEADER_STICKY_NODE, node_id.parse()?);
653            }
654        }
655        let mut builder = self.cli.post(endpoint.clone()).json(&req);
656        builder = self.wrap_auth_or_session_token(builder)?;
657        let request = builder
658            .headers(headers.clone())
659            .timeout(self.page_request_timeout)
660            .build()?;
661        let response = self
662            .query_request_helper(request, true, true, true, RequestKind::QueryStart)
663            .await?;
664        self.handle_page(response, true).await
665    }
666
667    fn is_arrow_data(headers: &HeaderMap) -> bool {
668        if let Some(typ) = headers.get(CONTENT_TYPE) {
669            if let Ok(t) = typ.to_str() {
670                return t == CONTENT_TYPE_ARROW;
671            }
672        }
673        false
674    }
675
676    async fn handle_page(
677        &self,
678        response: HttpResponseData,
679        is_first: bool,
680    ) -> Result<(QueryResponse, Vec<RecordBatch>)> {
681        let status = response.status;
682        if status != StatusCode::OK {
683            let error = Self::status_body_to_error(status, &response.body);
684            if !is_first {
685                if let Error::Logic(code, ec) = &error {
686                    if *code == StatusCode::NOT_FOUND {
687                        return Err(Error::QueryNotFound(ec.message.clone()));
688                    }
689                }
690            }
691            return Err(error);
692        }
693        let is_arrow_data = Self::is_arrow_data(&response.headers);
694        if is_first {
695            if let Some(route_hint) = response.headers.get(HEADER_ROUTE_HINT) {
696                self.route_hint.set(route_hint.to_str().unwrap_or_default());
697            }
698        }
699        let mut body = response.body;
700        let mut batches = vec![];
701        if is_arrow_data {
702            if is_first {
703                debug!("received arrow data");
704            }
705            let cursor = std::io::Cursor::new(body.as_ref());
706            let reader = StreamReader::try_new(cursor, None)
707                .map_err(|e| Error::Decode(format!("failed to decode arrow stream: {e}")))?;
708            let schema = reader.schema();
709            let json_body = if let Some(json_resp) = schema.metadata.get("response_header") {
710                bytes::Bytes::copy_from_slice(json_resp.as_bytes())
711            } else {
712                return Err(Error::Decode(
713                    "missing response_header metadata in arrow payload".to_string(),
714                ));
715            };
716            for batch in reader {
717                let batch = batch
718                    .map_err(|e| Error::Decode(format!("failed to decode arrow batch: {e}")))?;
719                batches.push(batch);
720            }
721            body = json_body
722        };
723        let resp: QueryResponse = json_from_slice(&body)?;
724        self.handle_session(&resp.session).await;
725        if let Some(err) = &resp.error {
726            return Err(Error::QueryFailed(err.clone()));
727        }
728        if is_first {
729            self.handle_warnings(&resp);
730            self.set_last_query_id(Some(resp.id.clone()));
731            if let Some(node_id) = &resp.node_id {
732                self.set_last_node_id(node_id.clone());
733            }
734        }
735        Ok((resp, batches))
736    }
737
738    pub async fn query_page(
739        &self,
740        query_id: &str,
741        next_uri: &str,
742        node_id: &Option<String>,
743    ) -> Result<(QueryResponse, Vec<RecordBatch>)> {
744        info!("query page: {next_uri}");
745        let endpoint = self.endpoint.join(next_uri)?;
746        let mut headers = self.make_headers(Some(query_id))?;
747        if self.capability.arrow_data && self.query_result_format == "arrow" {
748            headers.insert(ACCEPT, HeaderValue::from_static(CONTENT_TYPE_ARROW_OR_JSON));
749        }
750        let mut builder = self.cli.get(endpoint.clone());
751        builder = self
752            .wrap_auth_or_session_token(builder)?
753            .headers(headers.clone())
754            .timeout(self.page_request_timeout);
755        if let Some(node_id) = node_id {
756            builder = builder.header(HEADER_STICKY_NODE, node_id)
757        }
758        let request = builder.build()?;
759
760        let response = self
761            .query_request_helper(request, false, true, true, RequestKind::QueryPage)
762            .await?;
763        self.handle_page(response, false).await
764    }
765
766    pub async fn kill_query(&self, query_id: &str) -> Result<()> {
767        self.end_query(query_id, true, None).await
768    }
769
770    pub async fn final_query(&self, query_id: &str, node_id: Option<&str>) -> Result<()> {
771        self.end_query(query_id, false, node_id).await
772    }
773
774    pub async fn end_query(
775        &self,
776        query_id: &str,
777        is_kill: bool,
778        node_id: Option<&str>,
779    ) -> Result<()> {
780        let (method, request_kind) = if is_kill {
781            ("kill", RequestKind::QueryKill)
782        } else {
783            ("final", RequestKind::QueryFinal)
784        };
785        let uri = format!("/v1/query/{query_id}/{method}");
786        let endpoint = self.endpoint.join(&uri)?;
787        let headers = self.make_headers(Some(query_id))?;
788
789        info!("{method} query: {uri}");
790
791        let mut builder = self.cli.post(endpoint.clone());
792        if let Some(node_id) = node_id {
793            builder = builder.header(HEADER_STICKY_NODE, node_id)
794        }
795        builder = self.wrap_auth_or_session_token(builder)?;
796        let resp = builder.headers(headers.clone()).send().await?;
797        if resp.status() != 200 {
798            return Err(Error::response_error(resp.status(), &resp.bytes().await?)
799                .with_context(request_kind)
800                .with_query_id(query_id));
801        }
802        Ok(())
803    }
804
805    pub async fn query_all(
806        self: &Arc<Self>,
807        sql: &str,
808        params: Option<serde_json::Value>,
809    ) -> Result<Page> {
810        self.query_all_inner(sql, false, params).await
811    }
812
813    pub async fn query_all_inner(
814        self: &Arc<Self>,
815        sql: &str,
816        force_json_body: bool,
817        params: Option<serde_json::Value>,
818    ) -> Result<Page> {
819        let (resp, batches) = self
820            .start_query_inner(sql, None, force_json_body, params)
821            .await?;
822        let mut pages = Pages::new(self.clone(), resp, batches, false)?;
823        let mut all = Page::default();
824        while let Some(page) = pages.next().await {
825            all.update(page?);
826        }
827        Ok(all)
828    }
829
830    fn session_state(&self) -> SessionState {
831        self.session_state.lock().clone()
832    }
833
834    fn make_pagination(&self) -> Option<PaginationConfig> {
835        if self.wait_time_secs.is_none()
836            && self.max_rows_in_buffer.is_none()
837            && self.max_rows_per_page.is_none()
838        {
839            return None;
840        }
841        let mut pagination = PaginationConfig {
842            wait_time_secs: None,
843            max_rows_in_buffer: None,
844            max_rows_per_page: None,
845        };
846        if let Some(wait_time_secs) = self.wait_time_secs {
847            pagination.wait_time_secs = Some(wait_time_secs);
848        }
849        if let Some(max_rows_in_buffer) = self.max_rows_in_buffer {
850            pagination.max_rows_in_buffer = Some(max_rows_in_buffer);
851        }
852        if let Some(max_rows_per_page) = self.max_rows_per_page {
853            pagination.max_rows_per_page = Some(max_rows_per_page);
854        }
855        Some(pagination)
856    }
857
858    fn make_headers(&self, query_id: Option<&str>) -> Result<HeaderMap> {
859        let mut headers = HeaderMap::new();
860        if let Some(tenant) = &self.tenant {
861            headers.insert(HEADER_TENANT, tenant.parse()?);
862        }
863        let warehouse = self.warehouse.lock().clone();
864        if let Some(warehouse) = warehouse {
865            headers.insert(HEADER_WAREHOUSE, warehouse.parse()?);
866        }
867        let route_hint = self.route_hint.current();
868        headers.insert(HEADER_ROUTE_HINT, route_hint.parse()?);
869        if let Some(query_id) = query_id {
870            headers.insert(HEADER_QUERY_ID, query_id.parse()?);
871        }
872        Ok(headers)
873    }
874
875    pub async fn insert_with_stage(
876        self: &Arc<Self>,
877        sql: &str,
878        stage: &str,
879        file_format_options: BTreeMap<&str, &str>,
880        copy_options: BTreeMap<&str, &str>,
881    ) -> Result<QueryStats> {
882        info!("insert with stage: {sql}, format: {file_format_options:?}, copy: {copy_options:?}");
883        let stage_attachment = Some(StageAttachmentConfig {
884            location: stage,
885            file_format_options: Some(file_format_options),
886            copy_options: Some(copy_options),
887        });
888        let (resp, batches) = self
889            .start_query_inner(sql, stage_attachment, true, None)
890            .await?;
891        let mut pages = Pages::new(self.clone(), resp, batches, false)?;
892        let mut all = Page::default();
893        while let Some(page) = pages.next().await {
894            all.update(page?);
895        }
896        Ok(all.stats)
897    }
898
899    async fn get_presigned_upload_url(self: &Arc<Self>, stage: &str) -> Result<PresignedResponse> {
900        info!("get presigned upload url: {stage}");
901        let sql = format!("PRESIGN UPLOAD {stage}");
902        let resp = self.query_all_inner(&sql, true, None).await?;
903        if resp.data.len() != 1 {
904            return Err(Error::Decode(
905                "Empty response from server for presigned request".to_string(),
906            ));
907        }
908        if resp.data[0].len() != 3 {
909            return Err(Error::Decode(
910                "Invalid response from server for presigned request".to_string(),
911            ));
912        }
913        // resp.data[0]: [ "PUT", "{\"host\":\"s3.us-east-2.amazonaws.com\"}", "https://s3.us-east-2.amazonaws.com/query-storage-xxxxx/tnxxxxx/stage/user/xxxx/xxx?" ]
914        let method = resp.data[0][0].clone().unwrap_or_default();
915        if method != "PUT" {
916            return Err(Error::Decode(format!(
917                "Invalid method for presigned upload request: {method}"
918            )));
919        }
920        let headers: BTreeMap<String, String> =
921            serde_json::from_str(resp.data[0][1].clone().unwrap_or("{}".to_string()).as_str())?;
922        let url = resp.data[0][2].clone().unwrap_or_default();
923        Ok(PresignedResponse {
924            method,
925            headers,
926            url,
927        })
928    }
929
930    pub async fn upload_to_stage(
931        self: &Arc<Self>,
932        stage: &str,
933        data: Reader,
934        size: u64,
935    ) -> Result<()> {
936        match self.get_presign_mode() {
937            PresignMode::Off => self.upload_to_stage_with_stream(stage, data, size).await,
938            PresignMode::On => {
939                let presigned = self.get_presigned_upload_url(stage).await?;
940                presign_upload_to_stage(presigned, data, size).await
941            }
942            PresignMode::Auto => {
943                unreachable!("PresignMode::Auto should be handled during client initialization")
944            }
945            PresignMode::Detect => {
946                unreachable!("PresignMode::Detect should be handled during client initialization")
947            }
948        }
949    }
950
951    /// Upload data to stage with stream api, should not be used directly, use `upload_to_stage` instead.
952    async fn upload_to_stage_with_stream(
953        &self,
954        stage: &str,
955        data: Reader,
956        size: u64,
957    ) -> Result<()> {
958        info!("upload to stage with stream: {stage}, size: {size}");
959        if let Some(info) = self.need_pre_refresh_session().await {
960            self.refresh_session_token(info).await?;
961        }
962        let endpoint = self.endpoint.join("v1/upload_to_stage")?;
963        let location = StageLocation::try_from(stage)?;
964        let query_id = self.gen_query_id();
965        let mut headers = self.make_headers(Some(&query_id))?;
966        headers.insert(HEADER_STAGE_NAME, location.name.parse()?);
967        let stream = Body::wrap_stream(ReaderStream::new(data));
968        let part = Part::stream_with_length(stream, size).file_name(location.path);
969        let form = Form::new().part("upload", part);
970        let mut builder = self.cli.put(endpoint.clone());
971        builder = self.wrap_auth_or_session_token(builder)?;
972        let resp = builder.headers(headers).multipart(form).send().await?;
973        let status = resp.status();
974        if status != 200 {
975            return Err(Error::response_error(status, &resp.bytes().await?)
976                .with_context(RequestKind::UploadToStage)
977                .with_query_id(query_id));
978        }
979        Ok(())
980    }
981
982    // use base64 encode whenever possible for safety
983    // but also accept raw JSON for test/debug/one-shot operations
984    pub fn decode_json_header<T>(key: &str, value: &str) -> Result<T, String>
985    where
986        T: de::DeserializeOwned,
987    {
988        if value.starts_with("{") {
989            serde_json::from_slice(value.as_bytes())
990                .map_err(|e| format!("Invalid value {value} for {key} JSON decode error: {e}",))?
991        } else {
992            let json = URL_SAFE.decode(value).map_err(|e| {
993                format!(
994                    "Invalid value {} for {key}, base64 decode error: {}",
995                    value, e
996                )
997            })?;
998            serde_json::from_slice(&json).map_err(|e| {
999                format!(
1000                    "Invalid value {value} for {key}, JSON value {},  decode error: {e}",
1001                    String::from_utf8_lossy(&json)
1002                )
1003            })
1004        }
1005    }
1006
1007    pub async fn streaming_load(
1008        &self,
1009        sql: &str,
1010        data: Reader,
1011        file_name: &str,
1012    ) -> Result<LoadResponse> {
1013        let body = Body::wrap_stream(ReaderStream::new(data));
1014        let part = Part::stream(body).file_name(file_name.to_string());
1015        let endpoint = self.endpoint.join("v1/streaming_load")?;
1016        let mut builder = self.cli.put(endpoint.clone());
1017        builder = self.wrap_auth_or_session_token(builder)?;
1018        let query_id = self.gen_query_id();
1019        let mut headers = self.make_headers(Some(&query_id))?;
1020        headers.insert(HEADER_SQL, sql.parse()?);
1021        let session = serde_json::to_string(&*self.session_state.lock())
1022            .expect("serialize session state should not fail");
1023        headers.insert(HEADER_QUERY_CONTEXT, session.parse()?);
1024        let form = Form::new().part("upload", part);
1025        let resp = builder.headers(headers).multipart(form).send().await?;
1026        let status = resp.status();
1027        if let Some(value) = resp.headers().get(HEADER_QUERY_CONTEXT) {
1028            match Self::decode_json_header::<SessionState>(
1029                HEADER_QUERY_CONTEXT,
1030                value.to_str().unwrap(),
1031            ) {
1032                Ok(session) => *self.session_state.lock() = session,
1033                Err(e) => {
1034                    error!("Error decoding session state when streaming load: {e}");
1035                }
1036            }
1037        };
1038        if status != 200 {
1039            return Err(Error::response_error(status, &resp.bytes().await?)
1040                .with_context(RequestKind::StreamingLoad)
1041                .with_query_id(query_id));
1042        }
1043        let resp = resp.json::<LoadResponse>().await?;
1044        Ok(resp)
1045    }
1046
1047    async fn login(&mut self) -> Result<()> {
1048        let endpoint = self.endpoint.join("/v1/session/login")?;
1049        let headers = self.make_headers(None)?;
1050        let body = LoginRequest::from(&*self.session_state.lock());
1051        let mut builder = self.cli.post(endpoint.clone()).json(&body);
1052        if self.disable_session_token {
1053            builder = builder.query(&[("disable_session_token", true)]);
1054        }
1055        let builder = self.auth.wrap(builder)?;
1056        let request = builder
1057            .headers(headers.clone())
1058            .timeout(self.connect_timeout.saturating_add(Duration::from_secs(10)))
1059            .build()?;
1060        let response = self
1061            .query_request_helper(request, true, false, true, RequestKind::Login)
1062            .await?;
1063        if response.status == StatusCode::NOT_FOUND {
1064            info!("login return 404, skip login on the old version server");
1065            return Ok(());
1066        }
1067        if response.status != StatusCode::OK {
1068            return Err(Self::status_body_to_error(response.status, &response.body)
1069                .with_context(RequestKind::Login));
1070        }
1071        if let Some(v) = response.headers.get(HEADER_SESSION_ID) {
1072            if let Ok(s) = v.to_str() {
1073                self.session_id = s.to_string();
1074            }
1075        }
1076
1077        let response = json_from_slice(&response.body)?;
1078        match response {
1079            LoginResponseResult::Err { error } => return Err(Error::AuthFailure(error)),
1080            LoginResponseResult::Ok(info) => {
1081                let server_version = info
1082                    .version
1083                    .parse()
1084                    .map_err(|e| Error::Decode(format!("invalid server version: {e}")))?;
1085                self.capability = Capability::from_server_version(&server_version);
1086                self.server_version = Some(server_version.clone());
1087                let session_id = self.session_id.as_str();
1088                if let Some(tokens) = info.tokens {
1089                    info!(
1090                        "[session {session_id}] login success with session token version = {server_version}",
1091                    );
1092                    self.session_token_info = Some(Arc::new(Mutex::new((tokens, Instant::now()))))
1093                } else {
1094                    info!("[session {session_id}] login success, version = {server_version}");
1095                }
1096            }
1097        }
1098        Ok(())
1099    }
1100
1101    pub(crate) async fn try_heartbeat(&self) -> Result<()> {
1102        let endpoint = self.endpoint.join("/v1/session/heartbeat")?;
1103        let queries = self.queries_need_heartbeat.lock().clone();
1104        let mut node_to_queries = HashMap::<String, Vec<String>>::new();
1105        let now = Instant::now();
1106
1107        let mut query_ids = Vec::new();
1108        for (qid, state) in queries {
1109            if state.need_heartbeat(now) {
1110                query_ids.push(qid.to_string());
1111                if let Some(arr) = node_to_queries.get_mut(&state.node_id) {
1112                    arr.push(qid);
1113                } else {
1114                    node_to_queries.insert(state.node_id, vec![qid]);
1115                }
1116            }
1117        }
1118
1119        if node_to_queries.is_empty() && !self.session_state.lock().need_sticky.unwrap_or_default()
1120        {
1121            return Ok(());
1122        }
1123
1124        let body = json!({
1125           "node_to_queries": node_to_queries
1126        });
1127        let headers = self.make_headers(None)?;
1128        let builder = self.cli.post(endpoint.clone()).json(&body).headers(headers);
1129        let request = self.wrap_auth_or_session_token(builder)?.build()?;
1130        let response = self
1131            .query_request_helper(request, true, false, true, RequestKind::Heartbeat)
1132            .await?;
1133        if response.status != StatusCode::OK {
1134            return Err(Self::status_body_to_error(response.status, &response.body)
1135                .with_context(RequestKind::Heartbeat));
1136        }
1137        let json: Value = json_from_slice(&response.body)?;
1138        let session_id = self.session_id.as_str();
1139        info!("[session {session_id}] heartbeat request={body}, response={json}");
1140        if let Some(queries_to_remove) = json.get("queries_to_remove") {
1141            if let Some(arr) = queries_to_remove.as_array() {
1142                if !arr.is_empty() {
1143                    let mut queries = self.queries_need_heartbeat.lock();
1144                    for q in arr {
1145                        if let Some(q) = q.as_str() {
1146                            queries.remove(q);
1147                        }
1148                    }
1149                }
1150            }
1151        }
1152        let now = Instant::now();
1153        let mut queries = self.queries_need_heartbeat.lock();
1154        for qid in query_ids {
1155            if let Some(state) = queries.get_mut(&qid) {
1156                *state.last_access_time.lock() = now;
1157            }
1158        }
1159        Ok(())
1160    }
1161
1162    fn build_log_out_request(&self) -> Result<Request> {
1163        let endpoint = self.endpoint.join("/v1/session/logout")?;
1164
1165        let session_state = self.session_state();
1166        let need_sticky = session_state.need_sticky.unwrap_or(false);
1167        let mut headers = self.make_headers(None)?;
1168        if need_sticky {
1169            if let Some(node_id) = self.last_node_id() {
1170                headers.insert(HEADER_STICKY_NODE, node_id.parse()?);
1171            }
1172        }
1173        let builder = self.cli.post(endpoint.clone()).headers(headers.clone());
1174
1175        let builder = self.wrap_auth_or_session_token(builder)?;
1176        let req = builder.build()?;
1177        Ok(req)
1178    }
1179
1180    pub(crate) fn need_logout(&self) -> bool {
1181        self.session_token_info.is_some()
1182            || self.session_state.lock().need_keep_alive.unwrap_or(false)
1183    }
1184
1185    async fn refresh_session_token(
1186        &self,
1187        self_login_info: Arc<parking_lot::Mutex<(SessionTokenInfo, Instant)>>,
1188    ) -> Result<()> {
1189        let (session_token_info, _) = { self_login_info.lock().clone() };
1190        let endpoint = self.endpoint.join("/v1/session/refresh")?;
1191        let body = RefreshSessionTokenRequest {
1192            session_token: session_token_info.session_token.clone(),
1193        };
1194        let headers = self.make_headers(None)?;
1195        let request = self
1196            .cli
1197            .post(endpoint.clone())
1198            .json(&body)
1199            .headers(headers.clone())
1200            .bearer_auth(session_token_info.refresh_token.clone())
1201            .timeout(self.connect_timeout.saturating_add(Duration::from_secs(10)))
1202            .build()?;
1203        let response = self
1204            .query_request_helper(request, true, false, false, RequestKind::SessionRefresh)
1205            .await?;
1206        if response.status != StatusCode::OK {
1207            return Err(Self::status_body_to_error(response.status, &response.body)
1208                .with_context(RequestKind::SessionRefresh));
1209        }
1210        let response = json_from_slice(&response.body)?;
1211        match response {
1212            RefreshResponse::Err { error } => Err(Error::AuthFailure(error)),
1213            RefreshResponse::Ok(info) => {
1214                *self_login_info.lock() = (info, Instant::now());
1215                Ok(())
1216            }
1217        }
1218    }
1219
1220    async fn need_pre_refresh_session(&self) -> Option<Arc<Mutex<(SessionTokenInfo, Instant)>>> {
1221        if let Some(info) = &self.session_token_info {
1222            let (start, ttl) = {
1223                let guard = info.lock();
1224                (guard.1, guard.0.session_token_ttl_in_secs)
1225            };
1226            if Instant::now() > start + Duration::from_secs(ttl) {
1227                return Some(info.clone());
1228            }
1229        }
1230        None
1231    }
1232
1233    /// Send request with retry and return raw HTTP status/headers/body.
1234    ///
1235    /// retry on
1236    ///   - network/request/body transient errors
1237    ///   - (optional) 503
1238    ///
1239    /// refresh databend token or reload jwt token if needed.
1240    async fn query_request_helper(
1241        &self,
1242        mut request: Request,
1243        retry_if_503: bool,
1244        refresh_if_401: bool,
1245        reload_auth_if_401: bool,
1246        request_kind: RequestKind,
1247    ) -> Result<HttpResponseData> {
1248        let mut refreshed = false;
1249        let mut retries = 0;
1250        let max_retries = self.retry_count;
1251        let retry_delay = Duration::from_secs(self.retry_delay_secs);
1252
1253        loop {
1254            let req = request.try_clone().expect("request not cloneable");
1255            let response = match self.cli.execute(req).await {
1256                Ok(response) => response,
1257                Err(err) => {
1258                    if let Some(reason) = Self::retry_reason_for_reqwest(&err) {
1259                        if retries >= max_retries.saturating_sub(1) {
1260                            return Err(Self::with_request_context(
1261                                err,
1262                                &request_kind,
1263                                &request,
1264                                Some(retries),
1265                            ));
1266                        }
1267                        retries += 1;
1268                        warn!(
1269                            "retry {}/{} for {} due to: {} (error: {}), retrying after {} seconds",
1270                            retries,
1271                            max_retries,
1272                            request.url(),
1273                            reason,
1274                            err,
1275                            retry_delay.as_secs(),
1276                        );
1277                        sleep(jitter(retry_delay)).await;
1278                        continue;
1279                    }
1280                    return Err(Self::with_request_context(
1281                        err,
1282                        &request_kind,
1283                        &request,
1284                        Some(retries),
1285                    ));
1286                }
1287            };
1288
1289            let status = response.status();
1290            let headers = response.headers().clone();
1291            let body = match response.bytes().await {
1292                Ok(body) => body,
1293                Err(err) => {
1294                    if let Some(reason) = Self::retry_reason_for_reqwest(&err) {
1295                        if retries >= max_retries.saturating_sub(1) {
1296                            return Err(Self::with_request_context(
1297                                err,
1298                                &request_kind,
1299                                &request,
1300                                Some(retries),
1301                            ));
1302                        }
1303                        retries += 1;
1304                        warn!(
1305                            "retry {}/{} for {} due to: {} (error: {}), retrying after {} seconds",
1306                            retries,
1307                            max_retries,
1308                            request.url(),
1309                            reason,
1310                            err,
1311                            retry_delay.as_secs(),
1312                        );
1313                        sleep(jitter(retry_delay)).await;
1314                        continue;
1315                    }
1316                    return Err(Self::with_request_context(
1317                        err,
1318                        &request_kind,
1319                        &request,
1320                        Some(retries),
1321                    ));
1322                }
1323            };
1324
1325            if retry_if_503 && status == StatusCode::SERVICE_UNAVAILABLE {
1326                if retries >= max_retries.saturating_sub(1) {
1327                    return Ok(HttpResponseData {
1328                        status,
1329                        headers,
1330                        body,
1331                    });
1332                }
1333                retries += 1;
1334                warn!(
1335                    "retry {}/{} for {} due to: service unavailable (503), server may be starting, retrying after {} seconds",
1336                    retries,
1337                    max_retries,
1338                    request.url(),
1339                    retry_delay.as_secs()
1340                );
1341                sleep(jitter(retry_delay)).await;
1342                continue;
1343            }
1344
1345            if status == StatusCode::UNAUTHORIZED {
1346                request.headers_mut().remove(reqwest::header::AUTHORIZATION);
1347                let unauthorized_error =
1348                    serde_json::from_slice::<ResponseWithErrorCode>(&body).ok();
1349                if let Some(session_token_info) = &self.session_token_info {
1350                    let should_refresh = unauthorized_error
1351                        .as_ref()
1352                        .map(|r| need_refresh_token(r.error.code))
1353                        .unwrap_or(false);
1354                    if refresh_if_401 && should_refresh && !refreshed {
1355                        Box::pin(self.refresh_session_token(session_token_info.clone())).await?;
1356                        refreshed = true;
1357                        retries = 0;
1358                        warn!(
1359                            "retry for {} due to: session token expired and refreshed, retrying after {} seconds",
1360                            request.url(),
1361                            retry_delay.as_secs()
1362                        );
1363                        sleep(jitter(retry_delay)).await;
1364                        continue;
1365                    }
1366                }
1367                if reload_auth_if_401 && self.auth.can_reload() {
1368                    if retries >= max_retries.saturating_sub(1) {
1369                        return Ok(HttpResponseData {
1370                            status,
1371                            headers,
1372                            body,
1373                        });
1374                    }
1375                    retries += 1;
1376                    let builder =
1377                        RequestBuilder::from_parts(HttpClient::new(), request.try_clone().unwrap());
1378                    let builder = self.auth.wrap(builder)?;
1379                    request = builder.build()?;
1380                    warn!(
1381                        "retry {}/{} for {} due to: authentication token reloaded, retrying after {} seconds",
1382                        retries,
1383                        max_retries,
1384                        request.url(),
1385                        retry_delay.as_secs()
1386                    );
1387                    sleep(jitter(retry_delay)).await;
1388                    continue;
1389                }
1390            }
1391
1392            return Ok(HttpResponseData {
1393                status,
1394                headers,
1395                body,
1396            });
1397        }
1398    }
1399
1400    pub async fn logout(http_client: HttpClient, request: Request, session_id: &str) {
1401        if let Err(err) = http_client.execute(request).await {
1402            error!("[session {session_id}] logout request failed: {err}");
1403        } else {
1404            info!("[session {session_id}] logout success");
1405        };
1406    }
1407
1408    pub async fn close(&self) {
1409        let session_id = &self.session_id;
1410        info!("[session {session_id}] try closing now");
1411        if self
1412            .closed
1413            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
1414            .is_ok()
1415        {
1416            GLOBAL_CLIENT_MANAGER.unregister_client(&self.session_id);
1417            if self.need_logout() {
1418                let cli = self.cli.clone();
1419                let req = self
1420                    .build_log_out_request()
1421                    .expect("failed to build logout request");
1422                Self::logout(cli, req, &self.session_id).await;
1423            }
1424        }
1425    }
1426    pub fn close_with_spawn(&self) {
1427        let session_id = &self.session_id;
1428        info!("[session {session_id}]: try closing with spawn");
1429        if self
1430            .closed
1431            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
1432            .is_ok()
1433        {
1434            GLOBAL_CLIENT_MANAGER.unregister_client(&self.session_id);
1435            if self.need_logout() {
1436                let cli = self.cli.clone();
1437                let req = self
1438                    .build_log_out_request()
1439                    .expect("failed to build logout request");
1440                let session_id = self.session_id.clone();
1441                GLOBAL_RUNTIME.spawn(async move {
1442                    Self::logout(cli, req, session_id.as_str()).await;
1443                });
1444            }
1445        }
1446    }
1447
1448    pub(crate) fn register_query_for_heartbeat(&self, query_id: &str, state: QueryState) {
1449        let mut queries = self.queries_need_heartbeat.lock();
1450        queries.insert(query_id.to_string(), state);
1451    }
1452}
1453
1454fn json_from_slice<'a, T>(body: &'a [u8]) -> Result<T>
1455where
1456    T: Deserialize<'a>,
1457{
1458    serde_json::from_slice::<T>(body).map_err(|e| {
1459        Error::Decode(format!(
1460            "fail to decode JSON response: {e}, body: {}",
1461            String::from_utf8_lossy(body)
1462        ))
1463    })
1464}
1465
1466impl Default for APIClient {
1467    fn default() -> Self {
1468        Self {
1469            session_id: Default::default(),
1470            cli: HttpClient::new(),
1471            scheme: "http".to_string(),
1472            endpoint: Url::parse("http://localhost:8080").unwrap(),
1473            host: "localhost".to_string(),
1474            port: 8000,
1475            tenant: None,
1476            warehouse: Mutex::new(None),
1477            auth: Arc::new(BasicAuth::new(DEFAULT_USERNAME, "")) as Arc<dyn Auth>,
1478            session_state: Mutex::new(SessionState::default()),
1479            wait_time_secs: None,
1480            max_rows_in_buffer: None,
1481            max_rows_per_page: None,
1482            connect_timeout: Duration::from_secs(10),
1483            page_request_timeout: Duration::from_secs(300),
1484            tls_ca_file: None,
1485            presign: Mutex::new(PresignMode::Auto),
1486            route_hint: RouteHintGenerator::new(),
1487            last_node_id: Default::default(),
1488            disable_session_token: true,
1489            disable_login: false,
1490            query_result_format: "json".to_string(),
1491            session_token_info: None,
1492            closed: AtomicBool::new(false),
1493            last_query_id: Default::default(),
1494            server_version: None,
1495            capability: Default::default(),
1496            queries_need_heartbeat: Default::default(),
1497            retry_count: 3,
1498            retry_delay_secs: 10,
1499        }
1500    }
1501}
1502
1503struct RouteHintGenerator {
1504    nonce: AtomicU64,
1505    current: std::sync::Mutex<String>,
1506}
1507
1508impl RouteHintGenerator {
1509    fn new() -> Self {
1510        let gen = Self {
1511            nonce: AtomicU64::new(0),
1512            current: std::sync::Mutex::new("".to_string()),
1513        };
1514        gen.next();
1515        gen
1516    }
1517
1518    fn current(&self) -> String {
1519        let guard = self.current.lock().unwrap();
1520        guard.clone()
1521    }
1522
1523    fn set(&self, hint: &str) {
1524        let mut guard = self.current.lock().unwrap();
1525        *guard = hint.to_string();
1526    }
1527
1528    fn next(&self) -> String {
1529        let nonce = self.nonce.fetch_add(1, Ordering::AcqRel);
1530        let uuid = uuid::Uuid::new_v4();
1531        let current = format!("rh:{uuid}:{nonce:06}");
1532        let mut guard = self.current.lock().unwrap();
1533        guard.clone_from(&current);
1534        current
1535    }
1536}
1537
1538#[cfg(test)]
1539mod test {
1540    use super::*;
1541    use std::io::Write;
1542    use tempfile::NamedTempFile;
1543
1544    #[tokio::test]
1545    async fn parse_dsn() -> Result<()> {
1546        let dsn = "databend://username:password@app.databend.com/test?wait_time_secs=10&max_rows_in_buffer=5000000&max_rows_per_page=10000&warehouse=wh&sslmode=disable";
1547        let client = APIClient::from_dsn(dsn).await?;
1548        assert_eq!(client.host, "app.databend.com");
1549        assert_eq!(client.endpoint, Url::parse("http://app.databend.com:80")?);
1550        assert_eq!(client.wait_time_secs, Some(10));
1551        assert_eq!(client.max_rows_in_buffer, Some(5000000));
1552        assert_eq!(client.max_rows_per_page, Some(10000));
1553        assert_eq!(client.tenant, None);
1554        assert_eq!(
1555            *client.warehouse.try_lock().unwrap(),
1556            Some("wh".to_string())
1557        );
1558        Ok(())
1559    }
1560
1561    #[tokio::test]
1562    async fn parse_dsn_with_private_key_uses_keypair_auth() -> Result<()> {
1563        let output = std::process::Command::new("openssl")
1564            .args([
1565                "genpkey",
1566                "-algorithm",
1567                "RSA",
1568                "-pkeyopt",
1569                "rsa_keygen_bits:2048",
1570            ])
1571            .output();
1572        let output = match output {
1573            Ok(o) if o.status.success() => o,
1574            _ => return Ok(()),
1575        };
1576
1577        let mut key_file = NamedTempFile::new()?;
1578        key_file.write_all(&output.stdout)?;
1579
1580        let dsn = format!(
1581            "databend://username:password@app.databend.com/test?private_key_file={}&sslmode=disable",
1582            url::form_urlencoded::byte_serialize(key_file.path().to_string_lossy().as_bytes())
1583                .collect::<String>()
1584        );
1585        let client = APIClient::from_dsn(&dsn).await?;
1586        assert_eq!(client.auth.username(), "username");
1587        assert!(client.auth.can_reload());
1588
1589        let request = client
1590            .auth
1591            .wrap(client.cli.get(client.endpoint.join("v1/query")?))?
1592            .build()?;
1593        assert_eq!(
1594            request
1595                .headers()
1596                .get("X-DATABEND-AUTH-METHOD")
1597                .and_then(|value| value.to_str().ok()),
1598            Some("keypair")
1599        );
1600        let authorization = request
1601            .headers()
1602            .get(reqwest::header::AUTHORIZATION)
1603            .and_then(|value| value.to_str().ok())
1604            .unwrap_or_default();
1605        assert!(authorization.starts_with("Bearer "));
1606        assert_eq!(authorization["Bearer ".len()..].split('.').count(), 3);
1607        Ok(())
1608    }
1609
1610    #[tokio::test]
1611    async fn parse_dsn_with_private_key_requires_user() -> Result<()> {
1612        let output = std::process::Command::new("openssl")
1613            .args([
1614                "genpkey",
1615                "-algorithm",
1616                "RSA",
1617                "-pkeyopt",
1618                "rsa_keygen_bits:2048",
1619            ])
1620            .output();
1621        let output = match output {
1622            Ok(o) if o.status.success() => o,
1623            _ => return Ok(()),
1624        };
1625
1626        let mut key_file = NamedTempFile::new()?;
1627        key_file.write_all(&output.stdout)?;
1628
1629        let dsn = format!(
1630            "databend://app.databend.com/test?private_key_file={}&sslmode=disable",
1631            url::form_urlencoded::byte_serialize(key_file.path().to_string_lossy().as_bytes())
1632                .collect::<String>()
1633        );
1634        let err = APIClient::from_dsn(&dsn)
1635            .await
1636            .err()
1637            .expect("key-pair authentication should require an explicit username");
1638        assert!(
1639            err.to_string()
1640                .contains("username is required for key-pair authentication"),
1641            "unexpected error: {err}"
1642        );
1643
1644        Ok(())
1645    }
1646
1647    #[tokio::test]
1648    async fn parse_encoded_password() -> Result<()> {
1649        let dsn = "databend://username:3a%40SC(nYE1k%3D%7B%7BR@localhost";
1650        let client = APIClient::from_dsn(dsn).await?;
1651        assert_eq!(client.host(), "localhost");
1652        assert_eq!(client.port(), 443);
1653        Ok(())
1654    }
1655
1656    #[tokio::test]
1657    async fn parse_special_chars_password() -> Result<()> {
1658        let dsn = "databend://username:3a@SC(nYE1k={{R@localhost:8000";
1659        let client = APIClient::from_dsn(dsn).await?;
1660        assert_eq!(client.host(), "localhost");
1661        assert_eq!(client.port(), 8000);
1662        Ok(())
1663    }
1664}