Skip to main content

rocketmq_protocol/protocol/
remoting_command.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::sync::atomic::AtomicI32;
17use std::sync::atomic::Ordering;
18use std::sync::Arc;
19
20#[cfg(test)]
21use std::collections::HashMap;
22
23use bytes::Bytes;
24use cheetah_string::CheetahString;
25use serde::ser::SerializeMap;
26use serde::Deserialize;
27use serde::Deserializer;
28use serde::Serialize;
29use serde::Serializer;
30
31use super::RemotingCommandType;
32use super::SerializeType;
33use crate::protocol::command_custom_header::CommandCustomHeader;
34use crate::protocol::header_codec::BinaryHeaderFields;
35use crate::protocol::header_codec::JsonHeaderFields;
36use crate::protocol::LanguageCode;
37
38#[cfg(test)]
39use crate::protocol::command_custom_header::FromMap;
40#[cfg(test)]
41use bytes::BufMut;
42#[cfg(test)]
43use bytes::BytesMut;
44
45pub const SERIALIZE_TYPE_PROPERTY: &str = "rocketmq.serialize.type";
46pub const SERIALIZE_TYPE_ENV: &str = "ROCKETMQ_SERIALIZE_TYPE";
47pub const REMOTING_VERSION_KEY: &str = "rocketmq.remoting.version";
48
49static REQUEST_ID: AtomicI32 = AtomicI32::new(0);
50
51#[cfg(test)]
52std::thread_local! {
53    static REQUEST_ID_GENERATIONS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
54}
55
56#[cfg(test)]
57pub(crate) fn request_id_generation_count() -> usize {
58    REQUEST_ID_GENERATIONS.get()
59}
60
61#[inline]
62fn next_request_id_from(counter: &AtomicI32) -> i32 {
63    counter.fetch_add(1, Ordering::AcqRel)
64}
65
66#[inline]
67fn next_request_id() -> i32 {
68    #[cfg(test)]
69    REQUEST_ID_GENERATIONS.set(REQUEST_ID_GENERATIONS.get() + 1);
70    next_request_id_from(&REQUEST_ID)
71}
72
73mod accessors;
74mod constructors;
75mod custom_header;
76mod decode;
77mod encode;
78mod extension_fields;
79mod flags;
80mod frame;
81mod legacy_api;
82
83use extension_fields::ExtensionFields;
84
85fn serialize_ext_fields<S>(ext_fields: &ExtensionFields, serializer: S) -> Result<S::Ok, S::Error>
86where
87    S: Serializer,
88{
89    let Some(ext_fields) = ext_fields.as_map() else {
90        return serializer.serialize_none();
91    };
92    let mut entries = ext_fields.iter().collect::<Vec<_>>();
93    entries.sort_unstable_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
94    let mut map = serializer.serialize_map(Some(entries.len()))?;
95    for (key, value) in entries {
96        map.serialize_entry(key, value)?;
97    }
98    map.end()
99}
100
101fn deserialize_ext_fields<'de, D>(deserializer: D) -> Result<ExtensionFields, D::Error>
102where
103    D: Deserializer<'de>,
104{
105    Option::<JsonHeaderFields>::deserialize(deserializer)
106        .map(|fields| fields.map_or_else(ExtensionFields::default, ExtensionFields::from_json_raw))
107}
108
109#[derive(Serialize, Deserialize)]
110pub struct RemotingCommand {
111    code: i32,
112    language: LanguageCode,
113    version: i32,
114    opaque: i32,
115
116    ///flag -> bit: 00
117    /// The lowest bit of the flag indicates whether it is a response command.
118    /// Non-zero indicates a response command, while 0 indicates a request command.
119    /// The second bit indicates whether it is a one-way request.
120    /// Non-zero indicates a one-way request.
121    flag: i32,
122    remark: Option<CheetahString>,
123
124    #[serde(
125        rename = "extFields",
126        default,
127        serialize_with = "serialize_ext_fields",
128        deserialize_with = "deserialize_ext_fields"
129    )]
130    ext_fields: ExtensionFields,
131
132    #[serde(skip)]
133    body: Option<Bytes>,
134    #[serde(skip)]
135    suspended: bool,
136    #[serde(skip)]
137    command_custom_header: Option<Arc<Box<dyn CommandCustomHeader + Send + Sync + 'static>>>,
138    #[serde(skip)]
139    custom_header_to_net: bool,
140    #[serde(rename = "serializeTypeCurrentRPC")]
141    serialize_type: SerializeType,
142}
143
144impl Clone for RemotingCommand {
145    fn clone(&self) -> Self {
146        Self {
147            code: self.code,
148            language: self.language,
149            version: self.version,
150            opaque: self.opaque,
151            flag: self.flag,
152            remark: self.remark.clone(),
153            ext_fields: self.ext_fields.clone(),
154            body: self.body.clone(),
155            suspended: self.suspended,
156            command_custom_header: self.command_custom_header.clone(),
157            custom_header_to_net: self.custom_header_to_net,
158            serialize_type: self.serialize_type,
159        }
160    }
161}
162
163impl fmt::Display for RemotingCommand {
164    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165        write!(
166            f,
167            "RemotingCommand [code={}, language={}, version={}, opaque={}, flag(B)={:b}, remark={}, extFields={:?}, \
168             serializeTypeCurrentRPC={}]",
169            self.code,
170            self.language,
171            self.version,
172            self.opaque,
173            self.flag,
174            self.remark.as_ref().unwrap_or(&CheetahString::default()),
175            self.ext_fields,
176            self.serialize_type
177        )
178    }
179}
180
181impl Default for RemotingCommand {
182    fn default() -> Self {
183        Self::with_resolved_defaults(0, SerializeType::JSON)
184    }
185}
186
187/// Extract header length from the combined serialize_type field
188#[inline]
189pub fn parse_header_length(size: i32) -> usize {
190    (size & 0x00FFFFFF) as usize
191}
192
193/// Combine serialize type code with header length
194#[inline]
195pub fn mark_protocol_type(source: i32, serialize_type: SerializeType) -> i32 {
196    ((serialize_type.get_code() as i32) << 24) | (source & 0x00FFFFFF)
197}
198
199/// Extract serialize type from the combined field
200#[inline]
201pub fn parse_serialize_type(size: i32) -> rocketmq_error::Result<SerializeType> {
202    let code = (size >> 24) as u8;
203    SerializeType::value_of(code).ok_or_else(|| crate::error::unsupported_encoding(code))
204}
205
206#[cfg(test)]
207mod tests;