mina_node_native/http_server/routes/
webrtc.rs1use axum::{
7 extract::{Path, State},
8 http::StatusCode,
9 Json,
10};
11use utoipa_axum::{router::OpenApiRouter, routes};
12
13use mina_node::{
14 p2p::{
15 connection::{
16 incoming::{IncomingSignalingMethod, P2pConnectionIncomingInitOpts},
17 P2pConnectionResponse,
18 },
19 webrtc, PeerId,
20 },
21 rpc::RpcRequest,
22};
23use mina_node_common::rpc::RpcP2pConnectionIncomingResponse;
24
25use crate::http_server::{types::AssumeJson, AppState};
26
27pub fn routes() -> OpenApiRouter<AppState> {
29 OpenApiRouter::new()
30 .routes(routes!(signal_get))
31 .routes(routes!(signal_post))
32}
33
34#[utoipa::path(
36 get,
37 path = "/mina/webrtc/signal/{offer}",
38 tag = "webrtc",
39 params(
40 ("offer" = String, Path, description = "Base58 encoded WebRTC offer")
41 ),
42 responses(
43 (status = 200, description = "Connection accepted or rejected"),
44 (status = 400, description = "Bad offer or decryption failed"),
45 (status = 500, description = "Internal error")
46 )
47)]
48async fn signal_get(
49 State(state): State<AppState>,
50 Path(offer): Path<String>,
51) -> (StatusCode, Json<P2pConnectionResponse>) {
52 let decode_result = bs58::decode(&offer)
56 .into_vec()
57 .ok()
58 .and_then(|json| serde_json::from_slice(&json).ok());
59
60 match decode_result {
61 None => (
62 StatusCode::BAD_REQUEST,
63 Json(P2pConnectionResponse::SignalDecryptionFailed),
64 ),
65 Some(offer) => handle_offer(state, offer).await,
66 }
67}
68
69#[utoipa::path(
71 post,
72 path = "/mina/webrtc/signal",
73 tag = "webrtc",
74 responses(
75 (status = 200, description = "Connection accepted or rejected"),
76 (status = 400, description = "Bad offer"),
77 (status = 415, description = "Unsupported Content-Type"),
78 (status = 422, description = "Malformed JSON"),
79 (status = 500, description = "Internal error")
80 )
81)]
82async fn signal_post(
83 State(state): State<AppState>,
84 AssumeJson(offer): AssumeJson<Box<webrtc::Offer>>,
85) -> (StatusCode, Json<P2pConnectionResponse>) {
86 handle_offer(state, offer).await
90}
91
92async fn handle_offer(
94 state: AppState,
95 offer: Box<webrtc::Offer>,
96) -> (StatusCode, Json<P2pConnectionResponse>) {
97 let mut rx = state
98 .rpc_sender()
99 .multishot_request(
100 2,
101 RpcRequest::P2pConnectionIncoming(P2pConnectionIncomingInitOpts {
102 peer_id: PeerId::from_public_key(offer.identity_pub_key.clone()),
103 signaling: IncomingSignalingMethod::Http,
104 offer,
105 }),
106 )
107 .await;
108
109 match rx.recv().await {
110 Some(RpcP2pConnectionIncomingResponse::Answer(answer)) => {
111 let status = match &answer {
112 P2pConnectionResponse::Accepted(_) => StatusCode::OK,
113 P2pConnectionResponse::Rejected(reason) => {
114 if reason.is_bad() {
115 StatusCode::BAD_REQUEST
116 } else {
117 StatusCode::OK
118 }
119 }
120 P2pConnectionResponse::SignalDecryptionFailed => StatusCode::BAD_REQUEST,
121 P2pConnectionResponse::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
122 };
123 (status, Json(answer))
124 }
125 _ => (
126 StatusCode::INTERNAL_SERVER_ERROR,
127 Json(P2pConnectionResponse::InternalError),
128 ),
129 }
130}