1use axum::Json;
6use axum::http::StatusCode;
7use axum::http::header::CONTENT_TYPE;
8use axum::response::{IntoResponse, Response};
9use serde_json::Value;
10
11use super::*;
12
13mod decode;
14pub(crate) use decode::{
15 JsonRequestMode, decode_json_request, decode_json_request_with_surface_spec,
16};
17
18#[derive(Debug, Clone)]
19pub struct RawJson {
20 bytes: Bytes,
21}
22
23impl RawJson {
24 pub fn new(bytes: impl Into<Bytes>) -> Self {
25 Self {
26 bytes: bytes.into(),
27 }
28 }
29}
30
31impl IntoResponse for RawJson {
32 fn into_response(self) -> Response {
33 ([(CONTENT_TYPE, "application/json")], self.bytes).into_response()
34 }
35}
36
37pub async fn proto_request<Req, Rsp>(
44 state: &RestState,
45 proto_id: u32,
46 json_body: Option<Value>,
47) -> Result<Json<Value>, (StatusCode, Json<Value>)>
48where
49 Req: Message + Default + serde::de::DeserializeOwned,
50 Rsp: Message + Default + serde::Serialize,
51{
52 proto_request_internal::<Req, Rsp>(state, proto_id, json_body, None, None, None).await
53}
54
55pub(crate) async fn proto_request_with_surface_spec_and_idempotency<Req, Rsp>(
56 state: &RestState,
57 proto_id: u32,
58 json_body: Option<Value>,
59 idempotency_key: Option<String>,
60 surface_spec: &'static EndpointSpec,
61) -> Result<Json<Value>, (StatusCode, Json<Value>)>
62where
63 Req: Message + Default + serde::de::DeserializeOwned,
64 Rsp: Message + Default + serde::Serialize,
65{
66 proto_request_internal::<Req, Rsp>(
67 state,
68 proto_id,
69 json_body,
70 idempotency_key,
71 None,
72 Some(surface_spec),
73 )
74 .await
75}
76
77pub async fn proto_request_raw<Req, Rsp>(
78 state: &RestState,
79 proto_id: u32,
80 json_body: Option<Value>,
81) -> Result<RawJson, (StatusCode, Json<Value>)>
82where
83 Req: Message + Default + serde::de::DeserializeOwned,
84 Rsp: Message + Default + serde::Serialize,
85{
86 proto_request_raw_internal::<Req, Rsp>(
87 state,
88 proto_id,
89 json_body,
90 None,
91 None,
92 JsonRequestMode::GenericRest,
93 )
94 .await
95}
96
97pub async fn proto_request_raw_spec_body<Req, Rsp>(
98 state: &RestState,
99 proto_id: u32,
100 json_body: Option<Value>,
101) -> Result<RawJson, (StatusCode, Json<Value>)>
102where
103 Req: Message + Default + serde::de::DeserializeOwned,
104 Rsp: Message + Default + serde::Serialize,
105{
106 proto_request_raw_internal::<Req, Rsp>(
107 state,
108 proto_id,
109 json_body,
110 None,
111 None,
112 JsonRequestMode::RawSpecBody,
113 )
114 .await
115}
116
117pub(super) fn map_dispatch_error(
121 spec: &'static EndpointSpec,
122 err: DispatchError,
123) -> (StatusCode, Json<Value>) {
124 let proto_id = spec
125 .proto_id()
126 .map(|id| id.to_string())
127 .unwrap_or_else(|| "daemon-local".to_string());
128 let ret_msg = format!(
129 "{} (endpoint: {}, proto_id: {})",
130 err, spec.canonical_name, proto_id
131 );
132 let status = StatusCode::from_u16(spec.runtime.error.validation_http_status)
133 .unwrap_or(StatusCode::BAD_REQUEST);
134 let machine_error_field = spec.runtime.error.machine_error_field;
135 let mut body = serde_json::json!({
136 "ret_type": -1,
137 "ret_msg": ret_msg,
138 });
139 if let Some(obj) = body.as_object_mut() {
140 obj.insert(
141 machine_error_field.to_string(),
142 serde_json::json!({
143 "kind": "validation_error",
144 "message": err.to_string(),
145 "endpoint": spec.canonical_name,
146 "proto_id": proto_id,
147 }),
148 );
149 }
150 (status, Json(body))
151}
152
153pub(super) fn validation_error_body(message: impl Into<String>) -> Value {
154 let message = message.into();
155 serde_json::json!({
156 "ret_type": -1,
157 "ret_msg": message,
158 "error": message,
159 })
160}
161
162pub async fn proto_request_with_filter<Req, Rsp>(
176 state: &RestState,
177 proto_id: u32,
178 json_body: Option<Value>,
179 allowed_acc_ids: Option<&std::collections::HashSet<u64>>,
180) -> Result<Json<Value>, (StatusCode, Json<Value>)>
181where
182 Req: Message + Default + serde::de::DeserializeOwned,
183 Rsp: Message + Default + serde::Serialize,
184{
185 let ctx = if let Some(allowed) = allowed_acc_ids {
189 crate::caller_context::CallerContext {
190 key_id: None,
191 allowed_acc_ids: Some(std::sync::Arc::new(allowed.clone())),
192 }
193 } else {
194 crate::caller_context::CallerContext::legacy()
195 };
196 proto_request_internal::<Req, Rsp>(state, proto_id, json_body, None, Some(&ctx), None).await
197}
198
199pub async fn proto_request_with_idempotency<Req, Rsp>(
203 state: &RestState,
204 proto_id: u32,
205 json_body: Option<Value>,
206 idempotency_key: Option<String>,
207) -> Result<Json<Value>, (StatusCode, Json<Value>)>
208where
209 Req: Message + Default + serde::de::DeserializeOwned,
210 Rsp: Message + Default + serde::Serialize,
211{
212 proto_request_internal::<Req, Rsp>(state, proto_id, json_body, idempotency_key, None, None)
213 .await
214}
215
216pub async fn proto_request_with_idempotency_and_caller<Req, Rsp>(
223 state: &RestState,
224 proto_id: u32,
225 json_body: Option<Value>,
226 idempotency_key: Option<String>,
227 caller_key_id: Option<String>,
228) -> Result<Json<Value>, (StatusCode, Json<Value>)>
229where
230 Req: Message + Default + serde::de::DeserializeOwned,
231 Rsp: Message + Default + serde::Serialize,
232{
233 let ctx = caller_key_id.map(|k| crate::caller_context::CallerContext {
234 key_id: Some(k),
235 allowed_acc_ids: None,
236 });
237 proto_request_internal::<Req, Rsp>(
238 state,
239 proto_id,
240 json_body,
241 idempotency_key,
242 ctx.as_ref(),
243 None,
244 )
245 .await
246}
247
248pub async fn proto_request_with_ctx<Req, Rsp>(
267 state: &RestState,
268 proto_id: u32,
269 json_body: Option<Value>,
270 idempotency_key: Option<String>,
271 ctx: Option<&crate::caller_context::CallerContext>,
272) -> Result<Json<Value>, (StatusCode, Json<Value>)>
273where
274 Req: Message + Default + serde::de::DeserializeOwned,
275 Rsp: Message + Default + serde::Serialize,
276{
277 proto_request_internal::<Req, Rsp>(state, proto_id, json_body, idempotency_key, ctx, None).await
278}
279
280async fn proto_request_raw_internal<Req, Rsp>(
281 state: &RestState,
282 proto_id: u32,
283 json_body: Option<Value>,
284 idempotency_key: Option<String>,
285 ctx: Option<&crate::caller_context::CallerContext>,
286 mode: JsonRequestMode,
287) -> Result<RawJson, (StatusCode, Json<Value>)>
288where
289 Req: Message + Default + serde::de::DeserializeOwned,
290 Rsp: Message + Default + serde::Serialize,
291{
292 let req_msg: Req = decode_json_request(proto_id, json_body, mode)?;
293 let conn_id = state.next_conn_id();
294 let serial_no = state.next_serial();
295 let body = futu_server::trade_packet_id::fill_omitted_trade_packet_id_bytes(
296 proto_id,
297 req_msg.encode_to_vec(),
298 conn_id,
299 serial_no,
300 )
301 .map(Bytes::from)
302 .map_err(|e| {
303 (
304 StatusCode::INTERNAL_SERVER_ERROR,
305 Json(serde_json::json!({
306 "error": format!("failed to prepare trade PacketID: {e}")
307 })),
308 )
309 })?;
310
311 let incoming =
312 IncomingRequest::builder(conn_id, proto_id, serial_no, ProtoFmtType::Protobuf, body)
313 .with_idempotency_key(idempotency_key)
314 .with_caller_scope(
315 ctx.and_then(|c| c.caller_allowed_acc_ids_arc()),
316 ctx.and_then(|c| c.caller_key_id()),
317 )
318 .build();
319
320 let resp_bytes = state
321 .router
322 .dispatch(incoming.conn_id, &incoming)
323 .await
324 .ok_or_else(|| {
325 (
326 StatusCode::INTERNAL_SERVER_ERROR,
327 Json(serde_json::json!({
328 "error": "handler returned no response"
329 })),
330 )
331 })?;
332
333 let resp_bytes = state.filter_registry.apply(
334 proto_id,
335 resp_bytes,
336 ctx.and_then(|c| c.allowed_acc_ids_borrow()),
337 );
338
339 let rsp_msg = Rsp::decode(Bytes::from(resp_bytes)).map_err(|e| {
340 (
341 StatusCode::INTERNAL_SERVER_ERROR,
342 Json(serde_json::json!({
343 "error": format!("failed to decode response: {e}")
344 })),
345 )
346 })?;
347
348 raw_json_from_proto_response(&rsp_msg)
349}
350
351pub(crate) fn raw_json_from_proto_response<Rsp>(
352 rsp_msg: &Rsp,
353) -> Result<RawJson, (StatusCode, Json<Value>)>
354where
355 Rsp: serde::Serialize,
356{
357 let encoded = encode_proto_response_raw_or_error_value(rsp_msg).map_err(|e| {
358 (
359 StatusCode::INTERNAL_SERVER_ERROR,
360 Json(serde_json::json!({
361 "error": e
362 })),
363 )
364 })?;
365 match encoded {
366 ProtoJsonBody::Raw(raw) => Ok(RawJson::new(raw)),
367 ProtoJsonBody::Value(value) => {
368 let raw = serde_json::to_vec(&value).map_err(|e| {
369 (
370 StatusCode::INTERNAL_SERVER_ERROR,
371 Json(serde_json::json!({
372 "error": format!("failed to serialize response: {e}")
373 })),
374 )
375 })?;
376 Ok(RawJson::new(raw))
377 }
378 }
379}
380
381async fn proto_request_internal<Req, Rsp>(
386 state: &RestState,
387 proto_id: u32,
388 json_body: Option<Value>,
389 idempotency_key: Option<String>,
390 ctx: Option<&crate::caller_context::CallerContext>,
391 surface_spec: Option<&'static EndpointSpec>,
392) -> Result<Json<Value>, (StatusCode, Json<Value>)>
393where
394 Req: Message + Default + serde::de::DeserializeOwned,
395 Rsp: Message + Default + serde::Serialize,
396{
397 let req_msg: Req = decode_json_request_with_surface_spec(
401 proto_id,
402 json_body,
403 JsonRequestMode::GenericRest,
404 surface_spec,
405 )?;
406
407 let conn_id = state.next_conn_id();
409 let serial_no = state.next_serial();
410 let body = futu_server::trade_packet_id::fill_omitted_trade_packet_id_bytes(
411 proto_id,
412 req_msg.encode_to_vec(),
413 conn_id,
414 serial_no,
415 )
416 .map(Bytes::from)
417 .map_err(|e| {
418 (
419 StatusCode::INTERNAL_SERVER_ERROR,
420 Json(serde_json::json!({
421 "error": format!("failed to prepare trade PacketID: {e}")
422 })),
423 )
424 })?;
425
426 let incoming =
430 IncomingRequest::builder(conn_id, proto_id, serial_no, ProtoFmtType::Protobuf, body)
431 .with_idempotency_key(idempotency_key)
432 .with_caller_scope(
433 ctx.and_then(|c| c.caller_allowed_acc_ids_arc()),
434 ctx.and_then(|c| c.caller_key_id()),
435 )
436 .build();
437
438 let resp_bytes = state
439 .router
440 .dispatch(incoming.conn_id, &incoming)
441 .await
442 .ok_or_else(|| {
443 (
444 StatusCode::INTERNAL_SERVER_ERROR,
445 Json(serde_json::json!({
446 "error": "handler returned no response"
447 })),
448 )
449 })?;
450
451 let resp_bytes = state.filter_registry.apply(
458 proto_id,
459 resp_bytes,
460 ctx.and_then(|c| c.allowed_acc_ids_borrow()),
461 );
462
463 let rsp_msg = Rsp::decode(Bytes::from(resp_bytes)).map_err(|e| {
465 (
466 StatusCode::INTERNAL_SERVER_ERROR,
467 Json(serde_json::json!({
468 "error": format!("failed to decode response: {e}")
469 })),
470 )
471 })?;
472
473 let mut json_rsp = serde_json::to_value(&rsp_msg).map_err(|e| {
475 (
476 StatusCode::INTERNAL_SERVER_ERROR,
477 Json(serde_json::json!({
478 "error": format!("failed to serialize response: {e}")
479 })),
480 )
481 })?;
482
483 maybe_wrap_err_code_prefix(&mut json_rsp);
490
491 Ok(Json(json_rsp))
492}
493
494pub(super) enum ProtoJsonBody {
495 Raw(Bytes),
496 Value(Value),
497}
498
499pub(super) fn encode_proto_response_raw_or_error_value<Rsp>(
500 rsp_msg: &Rsp,
501) -> Result<ProtoJsonBody, String>
502where
503 Rsp: serde::Serialize,
504{
505 let raw =
506 serde_json::to_vec(rsp_msg).map_err(|e| format!("failed to serialize response: {e}"))?;
507 if serialized_ret_type(&raw) == Some(0) {
508 return Ok(ProtoJsonBody::Raw(Bytes::from(raw)));
509 }
510
511 let mut json_rsp =
512 serde_json::to_value(rsp_msg).map_err(|e| format!("failed to serialize response: {e}"))?;
513 maybe_wrap_err_code_prefix(&mut json_rsp);
514 Ok(ProtoJsonBody::Value(json_rsp))
515}
516
517fn serialized_ret_type(raw: &[u8]) -> Option<i64> {
518 #[derive(serde::Deserialize)]
519 struct RetOnly {
520 ret_type: Option<i64>,
521 }
522
523 serde_json::from_slice::<RetOnly>(raw)
524 .ok()
525 .and_then(|ret| ret.ret_type)
526}
527
528pub(crate) fn maybe_wrap_err_code_prefix(v: &mut Value) {
543 let obj = match v.as_object_mut() {
544 Some(o) => o,
545 None => return,
546 };
547 let is_err = obj
549 .get("ret_type")
550 .and_then(|t| t.as_i64())
551 .map(|t| t != 0)
552 .unwrap_or(false);
553 if !is_err {
554 return;
555 }
556 let raw_msg = obj
558 .get("ret_msg")
559 .and_then(|m| m.as_str())
560 .unwrap_or("")
561 .to_string();
562 if raw_msg.starts_with("[err_code=") {
564 return;
565 }
566 let err_code_label = match obj.get("err_code") {
568 Some(Value::Number(n)) => n
569 .as_i64()
570 .map(|i| i.to_string())
571 .unwrap_or("none".to_string()),
572 _ => "none".to_string(),
573 };
574 let new_msg = if raw_msg.is_empty() {
575 format!("[err_code={err_code_label}]")
576 } else {
577 format!("[err_code={err_code_label}] {raw_msg}")
578 };
579 obj.insert("ret_msg".to_string(), Value::String(new_msg));
580}