1#![allow(
16 dead_code,
17 reason = "legacy private Client items remain under the non-growth lint debt registry"
18)]
19#![recursion_limit = "256"]
20
21extern crate core;
22
23#[macro_export]
26macro_rules! mq_client_err {
27 ($response_code:expr, $fmt:expr, $($arg:expr),*) => {{
29 let formatted_msg = format!($fmt, $($arg),*);
30 $crate::ClientError::from($crate::MQClientException::new_with_code(
31 $response_code as i32,
32 formatted_msg,
33 ))
34 }};
35
36 ($response_code:expr, $error_message:expr) => {{
37 $crate::ClientError::from($crate::MQClientException::new_with_code(
38 $response_code as i32,
39 $error_message,
40 ))
41 }};
42
43 ($error_message:expr) => {{
45 $crate::ClientError::from($crate::MQClientException::new($error_message))
46 }};
47}
48
49#[macro_export]
51macro_rules! client_broker_err {
52 ($response_code:expr, $error_message:expr, $broker_addr:expr) => {{
54 $crate::ClientError::broker_operation_failed("BROKER_OPERATION", $response_code as i32, $error_message)
55 .with_broker_addr($broker_addr)
56 }};
57 ($response_code:expr, $error_message:expr) => {{
59 $crate::ClientError::broker_operation_failed("BROKER_OPERATION", $response_code as i32, $error_message)
60 }};
61}
62
63mod admin;
64mod base;
65mod client_error;
66mod common;
67mod config_support;
68mod consumer;
69mod exception;
70mod factory;
71mod hook;
72mod implementation;
73mod latency;
74mod legacy;
75mod lock;
76mod nameserver_discovery;
77pub mod prelude;
78mod producer;
79mod public_api;
80mod runtime;
81mod session;
82mod stat;
83#[cfg(any(test, feature = "test-support"))]
84pub mod test_support;
85mod trace;
86mod types;
87mod utils;
88
89pub use crate::base::query_result::QueryResult;
90pub use crate::base::MQAdmin;
91pub use crate::base::MqClientAdmin;
92pub use crate::base::MqClientAdminInner;
93pub use crate::client_error::ClientError;
94pub use crate::client_error::ClientResult;
95pub use crate::common::acl::AclConstants;
96pub use crate::common::acl::AclException;
97pub use crate::common::acl::AclSigner;
98pub use crate::common::acl::AclUtils;
99pub use crate::common::acl::Permission;
100pub use crate::common::acl::SigningAlgorithm;
101pub use crate::common::acl_client_rpc_hook::AclClientRPCHook;
102pub use crate::common::admin_tool_result::AdminToolResult;
103pub use crate::common::admin_tools_result_code_enum::AdminToolsResultCodeEnum;
104pub use crate::common::nameserver_access_config::NameserverAccessConfig;
105pub use crate::common::session_credentials::SessionCredentials;
106pub use crate::common::thread_local_index::ThreadLocalIndex;
107pub use crate::consumer::pop_retry_policy::pop_retry_subscription_topics;
108pub use crate::consumer::pop_retry_policy::resolve_pop_retry_topic;
109
110mod cluster_session {
111 use std::collections::HashMap;
112 use std::net::SocketAddr;
113 use std::sync::Arc;
114 use std::sync::Mutex;
115
116 use cheetah_string::CheetahString;
117 use rocketmq_protocol::protocol::remoting_command::RemotingCommand;
118 use rocketmq_security_api::OutboundSigner;
119 use rocketmq_security_api::SecurityRequestView;
120 use rocketmq_transport::api::RPCHook;
121
122 pub use rocketmq_model::common::boundary_type::BoundaryType;
123 pub use rocketmq_model::common::message::message_ext::MessageExt;
124 pub use rocketmq_model::common::message::message_queue::MessageQueue;
125 pub use rocketmq_model::common::message::message_queue_assignment::MessageQueueAssignment;
126
127 pub type ClientRpcHook = dyn RPCHook;
128
129 pub struct ClientInstanceHandle {
135 inner: Arc<crate::factory::mq_client_instance::MQClientInstance>,
136 pool: crate::implementation::mq_client_manager::ClientPool,
137 token: Mutex<Option<crate::implementation::mq_client_manager::ClientPoolToken>>,
138 operation_gate: Arc<tokio::sync::Mutex<()>>,
139 }
140
141 pub fn client_config_for_managed_domain(
144 domain_id: u64,
145 mut client_config: crate::base::client_config::ClientConfig,
146 ) -> crate::base::client_config::ClientConfig {
147 client_config.set_instance_name(CheetahString::from_string(format!(
148 "{}#managed-domain-{domain_id}",
149 client_config.instance_name
150 )));
151 client_config
152 }
153
154 impl ClientInstanceHandle {
155 pub fn get_or_create(
156 client_runtime: &crate::runtime::ClientRuntime,
157 domain_id: u64,
158 client_config: crate::base::client_config::ClientConfig,
159 rpc_hook: Option<Arc<ClientRpcHook>>,
160 ) -> crate::ClientResult<Self> {
161 let client_config = client_config_for_managed_domain(domain_id, client_config);
162 let pool = client_runtime.pool().clone();
163 let (inner, token) = pool.get_or_create(client_config, rpc_hook)?.into_parts();
164 Ok(Self {
165 inner,
166 pool,
167 token: Mutex::new(Some(token)),
168 operation_gate: Arc::new(tokio::sync::Mutex::new(())),
169 })
170 }
171
172 pub async fn start(&self) -> crate::ClientResult<()> {
173 let _operation = self.operation().await;
174 self.inner.start().await
175 }
176
177 pub async fn invoke_remoting(
179 &self,
180 broker_addr: &CheetahString,
181 request: RemotingCommand,
182 timeout_millis: u64,
183 ) -> crate::ClientResult<RemotingCommand> {
184 let _operation = self.operation().await;
185 self.inner
186 .get_mq_client_api_impl()?
187 .invoke(broker_addr, request, timeout_millis)
188 .await
189 }
190
191 pub async fn nameserver_discovery_status(&self) -> Option<crate::NameServerDiscoveryStatus> {
193 let _operation = self.operation().await;
194 self.inner.nameserver_discovery_status().await
195 }
196
197 pub async fn topic_route(
198 &self,
199 topic: &str,
200 timeout_millis: u64,
201 ) -> crate::ClientResult<Option<rocketmq_protocol::protocol::route::topic_route_data::TopicRouteData>> {
202 let _operation = self.operation().await;
203 self.inner
204 .get_mq_client_api_impl()?
205 .get_topic_route_info_from_name_server(topic, timeout_millis)
206 .await
207 }
208
209 pub async fn lock_batch_mq(
210 &self,
211 broker_addr: &str,
212 request: rocketmq_protocol::protocol::body::request::lock_batch_request_body::LockBatchRequestBody,
213 timeout_millis: u64,
214 ) -> crate::ClientResult<std::collections::HashSet<MessageQueue>> {
215 let _operation = self.operation().await;
216 self.inner
217 .get_mq_client_api_impl()?
218 .lock_batch_mq(broker_addr, request, timeout_millis)
219 .await
220 }
221
222 pub async fn unlock_batch_mq(
223 &self,
224 broker_addr: &CheetahString,
225 request: rocketmq_protocol::protocol::body::unlock_batch_request_body::UnlockBatchRequestBody,
226 timeout_millis: u64,
227 ) -> crate::ClientResult<()> {
228 let _operation = self.operation().await;
229 self.inner
230 .get_mq_client_api_impl()?
231 .unlock_batch_mq(broker_addr, request, timeout_millis, false)
232 .await
233 }
234
235 #[allow(
236 clippy::too_many_arguments,
237 reason = "mirrors the RocketMQ query-assignment wire contract"
238 )]
239 pub async fn query_assignment(
240 &self,
241 broker_addr: &CheetahString,
242 topic: CheetahString,
243 consumer_group: CheetahString,
244 client_id: CheetahString,
245 strategy_name: CheetahString,
246 message_model: rocketmq_protocol::protocol::heartbeat::message_model::MessageModel,
247 timeout_millis: u64,
248 ) -> crate::ClientResult<Option<Vec<MessageQueueAssignment>>> {
249 let _operation = self.operation().await;
250 self.inner
251 .get_mq_client_api_impl()?
252 .query_assignment(
253 broker_addr,
254 topic,
255 consumer_group,
256 client_id,
257 strategy_name,
258 message_model,
259 rocketmq_transport::api::RequestDeadline::from_timeout_millis(timeout_millis),
260 )
261 .await
262 .map_err(assignment_query_error)
263 .map(|assignments| assignments.map(|items| items.into_iter().collect()))
264 }
265
266 pub async fn pop_message(
267 &self,
268 broker_name: &CheetahString,
269 broker_addr: &CheetahString,
270 request: rocketmq_protocol::protocol::header::pop_message_request_header::PopMessageRequestHeader,
271 timeout_millis: u64,
272 ) -> crate::ClientResult<crate::consumer::pop_result::PopResult> {
273 let _operation = self.operation().await;
274 let (sender, receiver) = tokio::sync::oneshot::channel();
275 self.inner
276 .get_mq_client_api_impl()?
277 .pop_message_async(
278 broker_name,
279 broker_addr,
280 request,
281 timeout_millis,
282 OwnedPopCallback { sender: Some(sender) },
283 )
284 .await?;
285 receiver.await.unwrap_or_else(|source| {
286 Err(crate::ClientError::internal(
287 "receive client pop callback result",
288 source,
289 ))
290 })
291 }
292
293 pub async fn ack_message(
294 &self,
295 broker_addr: &CheetahString,
296 request: rocketmq_protocol::protocol::header::ack_message_request_header::AckMessageRequestHeader,
297 timeout_millis: u64,
298 ) -> crate::ClientResult<crate::consumer::ack_result::AckResult> {
299 let _operation = self.operation().await;
300 self.inner
301 .get_mq_client_api_impl()?
302 .ack_message(broker_addr, request, timeout_millis)
303 .await
304 }
305
306 pub async fn batch_ack_message(
307 &self,
308 broker_addr: &CheetahString,
309 request: rocketmq_protocol::protocol::body::batch_ack_message_request_body::BatchAckMessageRequestBody,
310 timeout_millis: u64,
311 ) -> crate::ClientResult<crate::consumer::ack_result::AckResult> {
312 let _operation = self.operation().await;
313 self.inner
314 .get_mq_client_api_impl()?
315 .batch_ack_message(broker_addr, request, timeout_millis)
316 .await
317 }
318
319 #[cfg(feature = "admin-mutation")]
320 pub async fn sync_lite_subscription(
321 &self,
322 broker_addr: &CheetahString,
323 request: rocketmq_model::common::lite::LiteSubscriptionDTO,
324 timeout_millis: u64,
325 ) -> crate::ClientResult<()> {
326 let _operation = self.operation().await;
327 self.inner
328 .get_mq_client_api_impl()?
329 .sync_lite_subscription_async(broker_addr, request, timeout_millis)
330 .await
331 }
332
333 pub async fn change_invisible_time(
334 &self,
335 broker_name: &CheetahString,
336 broker_addr: &CheetahString,
337 request: rocketmq_protocol::protocol::header::change_invisible_time_request_header::ChangeInvisibleTimeRequestHeader,
338 timeout_millis: u64,
339 ) -> crate::ClientResult<crate::consumer::ack_result::AckResult> {
340 let _operation = self.operation().await;
341 let (sender, receiver) = tokio::sync::oneshot::channel();
342 self.inner
343 .get_mq_client_api_impl()?
344 .change_invisible_time_async(
345 broker_name,
346 broker_addr,
347 request,
348 timeout_millis,
349 OwnedAckCallback::new(sender),
350 )
351 .await?;
352 receiver.await.unwrap_or_else(|source| {
353 Err(crate::ClientError::internal(
354 "receive client change-invisible callback result",
355 source,
356 ))
357 })
358 }
359
360 pub async fn end_transaction(
361 &self,
362 broker_addr: &CheetahString,
363 request: rocketmq_protocol::protocol::header::end_transaction_request_header::EndTransactionRequestHeader,
364 remark: CheetahString,
365 timeout_millis: u64,
366 ) -> crate::ClientResult<()> {
367 let _operation = self.operation().await;
368 self.inner
369 .get_mq_client_api_impl()?
370 .end_transaction_oneway(broker_addr, request, remark, timeout_millis)
371 .await
372 }
373
374 pub async fn find_subscribe_broker_addr(
375 &self,
376 broker_name: &CheetahString,
377 broker_id: u64,
378 only_this_broker: bool,
379 ) -> Option<CheetahString> {
380 let _operation = self.operation().await;
381 let instance = self.inner.clone();
382 instance
383 .find_broker_address_in_subscribe(broker_name, broker_id, only_this_broker)
384 .await
385 .map(|found| found.broker_addr)
386 }
387
388 pub async fn refresh_topic_route(&self, topic: &CheetahString) -> bool {
389 let _operation = self.operation().await;
390 let instance = self.inner.clone();
391 instance.update_topic_route_info_from_name_server_topic(topic).await
392 }
393
394 pub async fn broker_name_for_queue(&self, queue: &MessageQueue) -> CheetahString {
395 let _operation = self.operation().await;
396 self.inner.get_broker_name_from_message_queue(queue).await
397 }
398
399 pub async fn pull_outcome_from_broker(
400 &self,
401 broker_addr: &str,
402 request: rocketmq_protocol::protocol::header::pull_message_request_header::PullMessageRequestHeader,
403 timeout_millis: u64,
404 ) -> crate::ClientResult<rocketmq_model::result::PullOutcome<MessageExt>> {
405 let _operation = self.operation().await;
406 let result = self
407 .inner
408 .pull_message_from_broker(broker_addr, request, timeout_millis)
409 .await?;
410 Ok((&result).into())
411 }
412
413 #[allow(clippy::too_many_arguments, reason = "mirrors the RocketMQ send-back wire contract")]
414 pub async fn consumer_send_message_back(
415 &self,
416 broker_addr: &str,
417 broker_name: Option<&str>,
418 message: &MessageExt,
419 consumer_group: &str,
420 delay_level: i32,
421 timeout_millis: u64,
422 max_consume_retry_times: i32,
423 ) -> crate::ClientResult<()> {
424 let _operation = self.operation().await;
425 self.inner
426 .consumer_send_message_back(
427 broker_addr,
428 broker_name,
429 message,
430 consumer_group,
431 delay_level,
432 timeout_millis,
433 max_consume_retry_times,
434 )
435 .await
436 }
437
438 pub async fn update_consumer_offset(
439 &self,
440 broker_addr: &CheetahString,
441 request: rocketmq_protocol::protocol::header::update_consumer_offset_header::UpdateConsumerOffsetRequestHeader,
442 timeout_millis: u64,
443 ) -> crate::ClientResult<()> {
444 let _operation = self.operation().await;
445 self.inner
446 .update_consumer_offset(broker_addr, request, timeout_millis)
447 .await
448 }
449
450 pub async fn query_consumer_offset(
451 &self,
452 broker_addr: &str,
453 request: rocketmq_protocol::protocol::header::query_consumer_offset_request_header::QueryConsumerOffsetRequestHeader,
454 timeout_millis: u64,
455 ) -> crate::ClientResult<i64> {
456 let _operation = self.operation().await;
457 self.inner
458 .query_consumer_offset(broker_addr, request, timeout_millis)
459 .await
460 }
461
462 pub async fn min_offset(
463 &self,
464 broker_addr: &str,
465 queue: &MessageQueue,
466 timeout_millis: u64,
467 ) -> crate::ClientResult<i64> {
468 let _operation = self.operation().await;
469 self.inner.get_min_offset(broker_addr, queue, timeout_millis).await
470 }
471
472 pub async fn max_offset(
473 &self,
474 broker_addr: &str,
475 queue: &MessageQueue,
476 timeout_millis: u64,
477 ) -> crate::ClientResult<i64> {
478 let _operation = self.operation().await;
479 self.inner.get_max_offset(broker_addr, queue, timeout_millis).await
480 }
481
482 pub async fn search_offset(
483 &self,
484 broker_addr: &str,
485 queue: &MessageQueue,
486 timestamp: i64,
487 boundary_type: BoundaryType,
488 timeout_millis: u64,
489 ) -> crate::ClientResult<i64> {
490 let _operation = self.operation().await;
491 self.inner
492 .search_offset_by_timestamp(broker_addr, queue, timestamp, boundary_type, timeout_millis)
493 .await
494 }
495
496 pub async fn topic_config(
497 &self,
498 broker_addr: &CheetahString,
499 topic: CheetahString,
500 timeout_millis: u64,
501 ) -> crate::ClientResult<rocketmq_model::common::config::TopicConfig> {
502 let _operation = self.operation().await;
503 self.inner.get_topic_config(broker_addr, topic, timeout_millis).await
504 }
505
506 pub async fn subscription_group_config(
507 &self,
508 broker_addr: &CheetahString,
509 group: CheetahString,
510 timeout_millis: u64,
511 ) -> crate::ClientResult<
512 rocketmq_protocol::protocol::subscription::subscription_group_config::SubscriptionGroupConfig,
513 > {
514 let _operation = self.operation().await;
515 self.inner
516 .get_subscription_group_config(broker_addr, group, timeout_millis)
517 .await
518 }
519
520 pub async fn broker_cluster_info(
521 &self,
522 timeout_millis: u64,
523 ) -> crate::ClientResult<rocketmq_protocol::protocol::body::broker_body::cluster_info::ClusterInfo> {
524 let _operation = self.operation().await;
525 self.inner.get_broker_cluster_info(timeout_millis).await
526 }
527
528 pub async fn user(
529 &self,
530 broker_addr: CheetahString,
531 username: CheetahString,
532 timeout_millis: u64,
533 ) -> crate::ClientResult<Option<rocketmq_protocol::protocol::body::user_info::UserInfo>> {
534 let _operation = self.operation().await;
535 self.inner.get_user(broker_addr, username, timeout_millis).await
536 }
537
538 pub async fn acl(
539 &self,
540 broker_addr: CheetahString,
541 subject: CheetahString,
542 timeout_millis: u64,
543 ) -> crate::ClientResult<Option<rocketmq_protocol::protocol::body::acl_info::AclInfo>> {
544 let _operation = self.operation().await;
545 self.inner.get_acl(broker_addr, subject, timeout_millis).await
546 }
547
548 pub async fn shutdown_owned(&self) {
549 let token = self
550 .token
551 .lock()
552 .unwrap_or_else(|poisoned| poisoned.into_inner())
553 .take();
554 let Some(token) = token else {
555 return;
556 };
557 let _operation = self.operation().await;
558 self.pool.release(token).await;
559 }
560
561 async fn operation(&self) -> tokio::sync::OwnedMutexGuard<()> {
562 self.operation_gate.clone().lock_owned().await
563 }
564 }
565
566 fn assignment_query_error(input: crate::common::retry_policy::RetryInput) -> crate::ClientError {
567 use crate::common::retry_policy::RetryInput;
568 use crate::ClientError;
569 use rocketmq_error::fields;
570 use rocketmq_error::Error;
571 use rocketmq_error::ErrorContext;
572 use rocketmq_transport::api::OutboundRequestContractReason;
573 use rocketmq_transport::api::OutboundRequestRejectionReason;
574
575 match input {
576 RetryInput::Transport(error) => ClientError::from_shared(error.into_shared_error()),
577 RetryInput::Rejected(rejection) => match rejection.reason() {
578 OutboundRequestRejectionReason::DeadlineExpired => {
579 let mut context = ErrorContext::new().with_text(fields::OPERATION_DIAGNOSTIC, "query_assignment");
580 if let Some(timeout_millis) = rejection.timeout_millis() {
581 context = context.with_u64(fields::TIMEOUT_MS, timeout_millis);
582 }
583 ClientError::from_shared(Arc::new(
584 Error::new(&rocketmq_error::CORE_OPERATION_TIMED_OUT).with_context(context),
585 ))
586 }
587 OutboundRequestRejectionReason::ClientStopping => ClientError::not_started(),
588 OutboundRequestRejectionReason::QueueSaturated => ClientError::from_shared(Arc::new(Error::new(
589 &rocketmq_error::TRANSPORT_ADMISSION_QUEUE_SATURATED,
590 ))),
591 OutboundRequestRejectionReason::Cancelled
592 | OutboundRequestRejectionReason::SessionClosed
593 | OutboundRequestRejectionReason::EndpointUnavailable => {
594 ClientError::from_shared(Arc::new(Error::new(&rocketmq_error::TRANSPORT_CONNECTION_FAILED)))
595 }
596 },
597 RetryInput::Contract(contract) => match contract.reason() {
598 OutboundRequestContractReason::NameServerEndpointMissing => {
599 ClientError::from_shared(Arc::new(Error::new(&rocketmq_error::TRANSPORT_CONNECTION_FAILED)))
600 }
601 },
602 RetryInput::Response { terminal_error, .. } | RetryInput::BusinessError(terminal_error) => terminal_error,
603 RetryInput::SendStatus(_) | RetryInput::RouteUnavailable => {
606 unreachable!("query-assignment API returned an unrelated retry input")
607 }
608 }
609 }
610
611 impl Drop for ClientInstanceHandle {
612 fn drop(&mut self) {
613 if self
614 .token
615 .get_mut()
616 .unwrap_or_else(|poisoned| poisoned.into_inner())
617 .is_some()
618 {
619 tracing::warn!(
620 client_id = %self.inner.client_id,
621 "managed Client handle dropped before asynchronous shutdown completed"
622 );
623 }
624 }
625 }
626
627 struct OwnedPopCallback {
628 sender: Option<tokio::sync::oneshot::Sender<crate::ClientResult<crate::consumer::pop_result::PopResult>>>,
629 }
630
631 impl crate::consumer::pop_callback::PopCallback for OwnedPopCallback {
632 async fn on_success(&mut self, pop_result: crate::consumer::pop_result::PopResult) {
633 if let Some(sender) = self.sender.take() {
634 let _ = sender.send(Ok(pop_result));
635 }
636 }
637
638 fn on_error(&mut self, error: crate::ClientError) {
639 if let Some(sender) = self.sender.take() {
640 let _ = sender.send(Err(error));
641 }
642 }
643 }
644
645 struct OwnedAckCallback {
646 sender:
647 Mutex<Option<tokio::sync::oneshot::Sender<crate::ClientResult<crate::consumer::ack_result::AckResult>>>>,
648 }
649
650 impl OwnedAckCallback {
651 fn new(
652 sender: tokio::sync::oneshot::Sender<crate::ClientResult<crate::consumer::ack_result::AckResult>>,
653 ) -> Self {
654 Self {
655 sender: Mutex::new(Some(sender)),
656 }
657 }
658
659 fn send(&self, result: crate::ClientResult<crate::consumer::ack_result::AckResult>) {
660 let mut sender = match self.sender.lock() {
661 Ok(sender) => sender,
662 Err(poisoned) => poisoned.into_inner(),
663 };
664 if let Some(sender) = sender.take() {
665 let _ = sender.send(result);
666 }
667 }
668 }
669
670 impl crate::consumer::ack_callback::AckCallback for OwnedAckCallback {
671 fn on_success(&self, ack_result: crate::consumer::ack_result::AckResult) {
672 self.send(Ok(ack_result));
673 }
674
675 fn on_exception(&self, error: crate::ClientError) {
676 self.send(Err(error));
677 }
678 }
679
680 pub fn rpc_hook_from_outbound_signer(signer: Arc<dyn OutboundSigner>) -> Arc<ClientRpcHook> {
681 Arc::new(OutboundSignerRpcHook { signer })
682 }
683
684 struct OutboundSignerRpcHook {
685 signer: Arc<dyn OutboundSigner>,
686 }
687
688 impl RPCHook for OutboundSignerRpcHook {
689 fn do_before_request(
690 &self,
691 _remote_addr: SocketAddr,
692 request: &mut RemotingCommand,
693 ) -> Result<(), rocketmq_error::SharedError> {
694 request.make_custom_header_to_net();
698 let empty_fields = HashMap::new();
699 let fields = request.ext_fields().unwrap_or(&empty_fields);
700 let signature = self
701 .signer
702 .sign(SecurityRequestView::new(
703 request.code(),
704 request.version(),
705 fields,
706 request.body().map(AsRef::as_ref),
707 None,
708 ))
709 .map_err(|source| source.into_shared_error())?;
710
711 request.ensure_ext_fields_initialized();
712 for (key, value) in signature.fields() {
713 request.add_ext_field(key.clone(), value.expose_secret().clone());
714 }
715 Ok(())
716 }
717
718 fn do_after_response(
719 &self,
720 _remote_addr: SocketAddr,
721 _request: &RemotingCommand,
722 _response: &mut RemotingCommand,
723 ) -> Result<(), rocketmq_error::SharedError> {
724 Ok(())
725 }
726 }
727
728 #[cfg(test)]
729 mod tests {
730 use std::sync::Arc;
731
732 use super::rpc_hook_from_outbound_signer;
733 use super::ClientInstanceHandle;
734 use super::OutboundSigner;
735 use super::RemotingCommand;
736 use super::SecurityRequestView;
737 use rocketmq_protocol::code::request_code::RequestCode;
738 use rocketmq_protocol::protocol::header::client_request_header::GetRouteInfoRequestHeader;
739 use rocketmq_security_api::Secret;
740 use rocketmq_security_api::SecurityOperation;
741 use rocketmq_security_api::SecurityProviderError;
742 use rocketmq_security_api::SecurityProviderFailure;
743 use rocketmq_security_api::Signature;
744
745 struct FixedSigner;
746
747 impl OutboundSigner for FixedSigner {
748 fn sign(&self, request: SecurityRequestView<'_>) -> Result<Signature, SecurityProviderError> {
749 assert_eq!(
750 request.fields().get("topic").map(|value| value.as_str()),
751 Some("TopicA")
752 );
753 Ok(Signature::new(vec![
754 ("AccessKey".into(), Secret::new("cluster-client".into())),
755 ("Signature".into(), Secret::new("redacted-signature".into())),
756 ]))
757 }
758 }
759
760 struct FailingSigner;
761
762 impl OutboundSigner for FailingSigner {
763 fn sign(&self, _request: SecurityRequestView<'_>) -> Result<Signature, SecurityProviderError> {
764 Err(SecurityProviderError::caused_by(
765 SecurityProviderFailure::OperationFailed,
766 SecurityOperation::SignRequest,
767 std::io::Error::other("secret signing diagnostic"),
768 ))
769 }
770 }
771
772 #[test]
773 fn outbound_signer_adapter_applies_fields_to_client_rpc_request() {
774 let hook = rpc_hook_from_outbound_signer(Arc::new(FixedSigner));
775 let mut request = RemotingCommand::create_request_command(
776 RequestCode::GetRouteinfoByTopic,
777 GetRouteInfoRequestHeader::new("TopicA", None),
778 );
779
780 hook.do_before_request("127.0.0.1:9876".parse().unwrap(), &mut request)
781 .unwrap();
782
783 let fields = request.ext_fields().unwrap();
784 assert_eq!(
785 fields.get("AccessKey").map(|value| value.as_str()),
786 Some("cluster-client")
787 );
788 assert_eq!(
789 fields.get("Signature").map(|value| value.as_str()),
790 Some("redacted-signature")
791 );
792 }
793
794 #[test]
795 fn outbound_signer_adapter_redacts_signer_failures() {
796 let hook = rpc_hook_from_outbound_signer(Arc::new(FailingSigner));
797 let mut request = RemotingCommand::create_request_command(
798 RequestCode::GetRouteinfoByTopic,
799 GetRouteInfoRequestHeader::new("TopicA", None),
800 );
801
802 let error = hook
803 .do_before_request("127.0.0.1:9876".parse().unwrap(), &mut request)
804 .expect_err("signing failure must fail closed");
805 let message = error.to_string();
806 assert!(message.contains("auth.security_provider.operation_failed"));
807 assert!(!message.contains("secret signing diagnostic"));
808 assert_eq!(error.descriptor(), &rocketmq_error::SECURITY_PROVIDER_OPERATION_FAILED);
809 let io = std::error::Error::source(error.as_ref()).expect("I/O cause must remain available");
810 assert!(io.downcast_ref::<std::io::Error>().is_some());
811 }
812
813 #[tokio::test]
814 async fn managed_client_is_shared_until_the_last_owner_releases_it() {
815 let runtime = crate::runtime::test_client_runtime("managed-client-shared-test");
816 let domain_id = 71_001;
817 let mut config = crate::base::client_config::ClientConfig::default();
818 config.set_instance_name("managed-client-shared".into());
819
820 let first = ClientInstanceHandle::get_or_create(&runtime, domain_id, config.clone(), None).unwrap();
821 let second = ClientInstanceHandle::get_or_create(&runtime, domain_id, config, None).unwrap();
822
823 assert!(Arc::ptr_eq(&first.inner, &second.inner));
824 assert_eq!(runtime.pool().instance_count(), 1);
825
826 first.shutdown_owned().await;
827 assert_eq!(runtime.pool().instance_count(), 1);
828
829 second.shutdown_owned().await;
830 assert_eq!(runtime.pool().instance_count(), 0);
831 }
832
833 #[tokio::test]
834 async fn managed_client_rejects_conflicting_configuration_for_the_same_identity() {
835 let runtime = crate::runtime::test_client_runtime("managed-client-conflict-test");
836 let domain_id = 71_002;
837 let mut first_config = crate::base::client_config::ClientConfig::default();
838 first_config.set_instance_name("managed-client-conflict".into());
839 first_config.set_namesrv_addr("127.0.0.1:9876".into());
840 let mut second_config = first_config.clone();
841 second_config.set_namesrv_addr("127.0.0.2:9876".into());
842
843 let first = ClientInstanceHandle::get_or_create(&runtime, domain_id, first_config, None).unwrap();
844 let error = ClientInstanceHandle::get_or_create(&runtime, domain_id, second_config, None)
845 .err()
846 .expect("conflicting managed configuration must fail closed");
847
848 assert!(error.is(&rocketmq_error::CORE_ARGUMENT_INVALID));
849 first.shutdown_owned().await;
850 }
851
852 #[tokio::test]
853 async fn managed_client_isolated_across_runtime_domains() {
854 let mut config = crate::base::client_config::ClientConfig::default();
855 config.set_instance_name("managed-client-cross-domain".into());
856 let first_runtime = crate::runtime::test_client_runtime("managed-client-domain-a");
857 let second_runtime = crate::runtime::test_client_runtime("managed-client-domain-b");
858 let first = ClientInstanceHandle::get_or_create(&first_runtime, 71_004, config.clone(), None).unwrap();
859 let second = ClientInstanceHandle::get_or_create(&second_runtime, 71_004, config, None).unwrap();
860
861 assert!(!Arc::ptr_eq(&first.inner, &second.inner));
862 assert_eq!(first_runtime.pool().instance_count(), 1);
863 assert_eq!(second_runtime.pool().instance_count(), 1);
864
865 first.shutdown_owned().await;
866 assert_eq!(first_runtime.pool().instance_count(), 0);
867 assert_eq!(second_runtime.pool().instance_count(), 1);
868 second.shutdown_owned().await;
869 assert_eq!(second_runtime.pool().instance_count(), 0);
870 }
871
872 #[tokio::test]
873 async fn releasing_the_last_lease_prevents_manager_aba_reuse() {
874 let runtime = crate::runtime::test_client_runtime("managed-client-aba-test");
875 let mut config = crate::base::client_config::ClientConfig::default();
876 config.set_instance_name("managed-client-aba".into());
877 let first = ClientInstanceHandle::get_or_create(&runtime, 71_008, config.clone(), None).unwrap();
878 first.shutdown_owned().await;
879
880 let second = ClientInstanceHandle::get_or_create(&runtime, 71_008, config, None).unwrap();
881 assert!(!Arc::ptr_eq(&first.inner, &second.inner));
882 second.shutdown_owned().await;
883 }
884 }
885}
886pub use crate::consumer::consumer_impl::pull_request_ext::PullResultExt;
887pub use crate::consumer::listener::message_listener_concurrently::ArcMessageListenerConcurrently;
888pub use crate::consumer::listener::message_listener_orderly::ArcMessageListenerOrderly;
889pub use crate::consumer::notify_result::NotifyResult;
890pub use crate::consumer::pull_result::PullOutcome;
891pub use crate::consumer::AbstractAllocateMessageQueueStrategy;
892pub use crate::consumer::AckCallback;
893pub use crate::consumer::AckCallbackFn;
894pub use crate::consumer::AckResult;
895pub use crate::consumer::AckStatus;
896pub use crate::consumer::AllocateMachineRoomNearby;
897pub use crate::consumer::AllocateMessageQueueAveragely;
898pub use crate::consumer::AllocateMessageQueueAveragelyByCircle;
899pub use crate::consumer::AllocateMessageQueueByConfig;
900pub use crate::consumer::AllocateMessageQueueByMachineRoom;
901pub use crate::consumer::AllocateMessageQueueByMachineRoomNearby;
902pub use crate::consumer::AllocateMessageQueueConsistentHash;
903pub use crate::consumer::AllocateMessageQueueStrategy;
904pub use crate::consumer::ArcMessageQueueListener;
905pub use crate::consumer::ClassicPullCallback;
906pub use crate::consumer::ConsumeConcurrentlyContext;
907pub use crate::consumer::ConsumeConcurrentlyStatus;
908pub use crate::consumer::ConsumeOrderlyContext;
909pub use crate::consumer::ConsumeOrderlyStatus;
910pub use crate::consumer::ConsumerTuningProfile;
911pub use crate::consumer::ControllableOffset;
912pub use crate::consumer::DefaultLitePullConsumerBuilder;
913#[allow(deprecated)]
914pub use crate::consumer::DefaultMQPullConsumer;
915pub use crate::consumer::DefaultMQPullConsumerBuilder;
916pub use crate::consumer::DefaultMQPushConsumerBuilder;
917pub use crate::consumer::HashFunction;
918pub use crate::consumer::LocalFileOffsetStore;
919pub use crate::consumer::MQConsumer;
920pub use crate::consumer::MQConsumerInner;
921#[allow(deprecated)]
922pub use crate::consumer::MQPullConsumer;
923pub use crate::consumer::MQPushConsumer;
924pub use crate::consumer::MachineRoomResolver;
925pub use crate::consumer::MessageListener;
926pub use crate::consumer::MessageListenerConcurrently;
927pub use crate::consumer::MessageListenerOrderly;
928pub use crate::consumer::MessageQueueListener;
929pub use crate::consumer::MessageSelector;
930pub use crate::consumer::OffsetSerialize;
931pub use crate::consumer::OffsetSerializeWrapper;
932pub use crate::consumer::OffsetStore;
933pub use crate::consumer::PopCallback;
934pub use crate::consumer::PopCallbackFn;
935pub use crate::consumer::PopResult;
936pub use crate::consumer::PopStatus;
937pub use crate::consumer::PullCallback;
938pub use crate::consumer::PullCallbackFn;
939pub use crate::consumer::PullOptions;
940pub use crate::consumer::PullResult;
941pub use crate::consumer::PullStatus;
942pub use crate::consumer::ReadOffsetType;
943pub use crate::consumer::RemoteBrokerOffsetStore;
944pub use crate::consumer::TopicMessageQueueChangeListener;
945pub use crate::exception::MQBrokerException;
946pub use crate::exception::MQClientException;
947pub use crate::exception::OffsetNotFoundException;
948pub use crate::exception::RequestTimeoutException;
949pub use crate::hook::check_forbidden_context::CheckForbiddenContext;
950pub use crate::hook::check_forbidden_hook::CheckForbiddenHook;
951pub use crate::hook::consume_message_context::ConsumeMessageContext;
952pub use crate::hook::consume_message_hook::ConsumeMessageHook;
953pub use crate::hook::consume_message_hook::ConsumeMessageHookArc;
954pub use crate::hook::namespace_rpc_hook::NamespaceRpcHook;
955pub use crate::implementation::mq_client_api_factory::MQClientAPIFactory;
956pub use crate::implementation::mq_client_api_impl::AdminClient;
957pub use crate::implementation::mq_client_api_impl::ConsumerClient;
958pub use crate::implementation::mq_client_api_impl::ProducerClient;
959pub use crate::implementation::mq_client_api_impl::RouteClient;
960pub use crate::implementation::mq_client_api_impl::TransactionClient;
961pub use crate::implementation::mq_client_manager::ClientPool;
962pub use crate::latency::BrokerFilter;
963pub use crate::latency::MQFaultStrategy;
964pub use crate::latency::Resolver;
965pub use crate::latency::ServiceDetector;
966#[allow(deprecated)]
967pub use crate::legacy::ConsumeMessageOpenTracingHookImpl;
968pub use crate::legacy::ConsumeRequest;
969#[allow(deprecated)]
970pub use crate::legacy::DefaultMQPullConsumerImpl;
971pub use crate::legacy::DoNothingClientRemotingProcessor;
972#[allow(deprecated)]
973pub use crate::legacy::EndTransactionOpenTracingHookImpl;
974#[allow(deprecated)]
975pub use crate::legacy::MQHelper;
976#[allow(deprecated)]
977pub use crate::legacy::MQPullConsumerScheduleService;
978#[allow(deprecated)]
979pub use crate::legacy::PullTaskCallback;
980#[allow(deprecated)]
981pub use crate::legacy::PullTaskContext;
982#[allow(deprecated)]
983pub use crate::legacy::PullTaskImpl;
984pub use crate::legacy::RebalanceImpl;
985#[allow(deprecated)]
986pub use crate::legacy::RebalancePullImpl;
987#[allow(deprecated)]
988pub use crate::legacy::SendMessageOpenTracingHookImpl;
989#[allow(deprecated)]
990pub use crate::legacy::TransactionCheckListener;
991pub use crate::lock::ReadWriteCASLock;
992pub use crate::producer::send_callback::ArcSendCallback;
993pub use crate::producer::transaction_listener::ArcTransactionListener;
994pub use crate::producer::BatchSendCallbackRequest;
995pub use crate::producer::BatchSendRequest;
996pub use crate::producer::JavaHashCode;
997pub use crate::producer::LocalTransactionState;
998pub use crate::producer::MessageQueueSelector;
999pub use crate::producer::MessageQueueSelectorFn;
1000pub use crate::producer::ProducerLifecycle;
1001pub use crate::producer::ProducerQueryRequest;
1002pub use crate::producer::ProducerQueryResponse;
1003pub use crate::producer::RecallRequest;
1004pub use crate::producer::RequestCallback;
1005pub use crate::producer::RequestReplyCallbackRequest;
1006pub use crate::producer::RequestReplyRequest;
1007pub use crate::producer::SelectMessageQueueByHash;
1008pub use crate::producer::SelectMessageQueueByMachineRoom;
1009pub use crate::producer::SelectMessageQueueByRandom;
1010pub use crate::producer::SelectedRequestReplyCallbackRequest;
1011pub use crate::producer::SelectedRequestReplyRequest;
1012pub use crate::producer::SelectedSendCallbackRequest;
1013pub use crate::producer::SelectedSendRequest;
1014pub use crate::producer::SendCallback;
1015pub use crate::producer::SendCallbackRequest;
1016pub use crate::producer::SendDestination;
1017pub use crate::producer::SendMode;
1018pub use crate::producer::SendRequest;
1019pub use crate::producer::SendResult;
1020pub use crate::producer::SendStatus;
1021pub use crate::producer::TopicCreateRequest;
1022pub use crate::producer::TransactionListener;
1023pub use crate::producer::TransactionMQProducer;
1024pub use crate::producer::TransactionMQProducerBuilder;
1025pub use crate::producer::TransactionSendRequest;
1026pub use crate::producer::TransactionSendResult;
1027pub use crate::runtime::ClientMetrics;
1028pub use crate::runtime::ClientRuntime;
1029pub use crate::runtime::ClientRuntimeConfig;
1030pub use crate::runtime::TelemetryHandle;
1031pub use crate::trace::async_trace_dispatcher::AsyncTraceDispatcher;
1032pub use crate::trace::trace_data_encoder::TraceDataEncoder;
1033pub use crate::trace::trace_dispatcher::ArcTraceDispatcher;
1034pub use crate::trace::trace_dispatcher::TraceDispatcher;
1035pub use crate::trace::trace_dispatcher::Type as TraceDispatcherOperation;
1036pub use crate::trace::trace_dispatcher_type::TraceDispatcherType;
1037pub use crate::trace::trace_type::TraceType;
1038pub use crate::utils::message_util::MessageUtil;
1039pub use public_api::*;
1040
1041#[cfg(test)]
1042mod tests {
1043 #[test]
1044 fn mq_client_err_without_response_code_preserves_message() {
1045 let err = crate::mq_client_err!("simple client error");
1046
1047 assert!(err.is(&rocketmq_error::CORE_ARGUMENT_INVALID));
1048 assert!(!err.to_string().contains("simple client error"));
1049 }
1050}