1use std::collections::{HashMap, HashSet};
9use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
10use std::sync::{Arc, RwLock};
11
12use bytes::Bytes;
13use prost::Message;
14
15use futu_auth::KeyStore;
16use futu_codec::header::ProtoFmtType;
17use futu_server::conn::IncomingRequest;
18use futu_server::router::RequestRouter;
19use futu_surface_spec::{DispatchError, EndpointSpec};
20
21use crate::ws::WsBroadcaster;
22
23pub(crate) type RestAccSubscriptionMap = HashMap<String, HashSet<u64>>;
24pub(crate) type RestAccSubscriptions = Arc<RwLock<RestAccSubscriptionMap>>;
25
26pub(crate) fn with_rest_acc_subscriptions_read<T>(
27 subscriptions: &RestAccSubscriptions,
28 f: impl FnOnce(&RestAccSubscriptionMap) -> T,
29) -> T {
30 match subscriptions.read() {
31 Ok(guard) => f(&guard),
32 Err(poisoned) => {
33 tracing::warn!("rest_acc_subscriptions read lock poisoned; recovering inner state");
34 let guard = poisoned.into_inner();
35 f(&guard)
36 }
37 }
38}
39
40pub(crate) fn with_rest_acc_subscriptions_write<T>(
41 subscriptions: &RestAccSubscriptions,
42 f: impl FnOnce(&mut RestAccSubscriptionMap) -> T,
43) -> T {
44 match subscriptions.write() {
45 Ok(mut guard) => f(&mut guard),
46 Err(poisoned) => {
47 tracing::warn!("rest_acc_subscriptions write lock poisoned; recovering inner state");
48 let mut guard = poisoned.into_inner();
49 f(&mut guard)
50 }
51 }
52}
53
54pub const MAX_SYMBOLS_PER_REQUEST: usize = 100;
69
70pub type AdminStatusProvider = Arc<dyn Fn() -> serde_json::Value + Send + Sync>;
76
77pub type AdminShutdownHandler = Arc<dyn Fn() -> Result<(), String> + Send + Sync>;
83
84pub type AdminReloadHandler = Arc<
92 dyn Fn() -> std::pin::Pin<
93 Box<dyn std::future::Future<Output = serde_json::Value> + Send + 'static>,
94 > + Send
95 + Sync,
96>;
97
98pub type PushHealthSnapshotProvider = Arc<dyn Fn() -> serde_json::Value + Send + Sync>;
104
105pub type CardNumResolver = Arc<dyn Fn(&str) -> Vec<u64> + Send + Sync>;
119
120mod endpoint_policy;
122mod proto_request;
123mod response;
124mod state;
125mod symbol_normalize;
126#[cfg(test)]
127use axum::Json;
128#[cfg(test)]
129use axum::http::StatusCode;
130#[cfg(test)]
131use proto_request::{
132 ProtoJsonBody, encode_proto_response_raw_or_error_value, map_dispatch_error,
133 validation_error_body,
134};
135#[cfg(test)]
136use symbol_normalize::to_snake_case;
137#[cfg(test)]
138mod tests;
139
140#[cfg(test)]
141pub(crate) use endpoint_policy::{
142 endpoint_adapter_policy_for_rest_path, endpoint_adapter_policy_for_spec,
143};
144#[cfg(test)]
145pub(crate) use proto_request::decode_json_request_with_surface_spec;
146pub(crate) use proto_request::proto_request_with_surface_spec_and_idempotency;
147pub(crate) use proto_request::{
148 JsonRequestMode, decode_json_request, maybe_wrap_err_code_prefix, raw_json_from_proto_response,
149};
150pub use proto_request::{
151 RawJson, proto_request, proto_request_raw, proto_request_raw_spec_body, proto_request_with_ctx,
152 proto_request_with_filter, proto_request_with_idempotency,
153 proto_request_with_idempotency_and_caller,
154};
155pub use response::ApiResponse;
156#[cfg(test)]
157pub(crate) use response::apply_known_field_aliases;
158pub(crate) use response::{
159 apply_known_field_aliases_for_proto_id, endpoint_local_request_normalizer_for_spec,
160 expand_symbol_shorthand, maybe_expand_flat_trd_header, maybe_wrap_flat_body_as_c2s,
161 normalize_endpoint_local_request_aliases_for_rest_path,
162 normalize_endpoint_local_request_aliases_for_spec,
163};
164pub use state::RestState;
165pub use symbol_normalize::normalize_json_keys_snake_case;