oasis_core_runtime/enclave_rpc/
dispatcher.rs

1//! RPC dispatcher.
2use std::collections::HashMap;
3
4use anyhow::{bail, Result};
5use thiserror::Error;
6
7use crate::{common::sgx::QuotePolicy, consensus::state::keymanager::Status as KeyManagerStatus};
8
9use super::{
10    context::Context,
11    types::{Body, Kind, Request, Response},
12};
13
14/// Dispatch error.
15#[derive(Error, Debug)]
16enum DispatchError {
17    #[error("method not found: {method:?}")]
18    MethodNotFound { method: String },
19    #[error("invalid RPC kind: {method:?} ({kind:?})")]
20    InvalidRpcKind { method: String, kind: Kind },
21}
22
23/// RPC handler.
24pub trait Handler {
25    /// Returns the list of RPC methods supported by this handler.
26    fn methods(&'static self) -> Vec<Method>;
27}
28
29/// Descriptor of a RPC API method.
30#[derive(Clone, Debug)]
31pub struct MethodDescriptor {
32    /// Method name.
33    pub name: String,
34    /// Specifies which kind of RPC is allowed to call the method.
35    pub kind: Kind,
36}
37
38/// Handler for a RPC method.
39pub trait MethodHandler<Rq, Rsp> {
40    /// Invoke the method implementation and return a response.
41    fn handle(&self, ctx: &Context, request: &Rq) -> Result<Rsp>;
42}
43
44impl<Rq, Rsp, F> MethodHandler<Rq, Rsp> for F
45where
46    Rq: 'static,
47    Rsp: 'static,
48    F: Fn(&Context, &Rq) -> Result<Rsp> + 'static,
49{
50    fn handle(&self, ctx: &Context, request: &Rq) -> Result<Rsp> {
51        (*self)(ctx, request)
52    }
53}
54
55/// Dispatcher for a RPC method.
56pub trait MethodHandlerDispatch {
57    /// Get method descriptor.
58    fn get_descriptor(&self) -> &MethodDescriptor;
59
60    /// Dispatch request.
61    fn dispatch(&self, ctx: &Context, request: Request) -> Result<Response>;
62}
63
64struct MethodHandlerDispatchImpl<Rq, Rsp> {
65    /// Method descriptor.
66    descriptor: MethodDescriptor,
67    /// Method handler.
68    handler: Box<dyn MethodHandler<Rq, Rsp> + Send + Sync>,
69}
70
71impl<Rq, Rsp> MethodHandlerDispatch for MethodHandlerDispatchImpl<Rq, Rsp>
72where
73    Rq: cbor::Decode + 'static,
74    Rsp: cbor::Encode + 'static,
75{
76    fn get_descriptor(&self) -> &MethodDescriptor {
77        &self.descriptor
78    }
79
80    fn dispatch(&self, ctx: &Context, request: Request) -> Result<Response> {
81        let request = cbor::from_value(request.args)?;
82        let response = self.handler.handle(ctx, &request)?;
83
84        Ok(Response {
85            body: Body::Success(cbor::to_value(response)),
86        })
87    }
88}
89
90/// RPC method dispatcher implementation.
91pub struct Method {
92    /// Method dispatcher.
93    dispatcher: Box<dyn MethodHandlerDispatch + Send + Sync>,
94}
95
96impl Method {
97    /// Create a new enclave method descriptor.
98    pub fn new<Rq, Rsp, Handler>(method: MethodDescriptor, handler: Handler) -> Self
99    where
100        Rq: cbor::Decode + 'static,
101        Rsp: cbor::Encode + 'static,
102        Handler: MethodHandler<Rq, Rsp> + Send + Sync + 'static,
103    {
104        Method {
105            dispatcher: Box::new(MethodHandlerDispatchImpl {
106                descriptor: method,
107                handler: Box::new(handler),
108            }),
109        }
110    }
111
112    /// Return method name.
113    fn get_name(&self) -> &String {
114        &self.dispatcher.get_descriptor().name
115    }
116
117    /// Return RPC call kind.
118    fn get_kind(&self) -> Kind {
119        self.dispatcher.get_descriptor().kind
120    }
121
122    /// Dispatch a request.
123    fn dispatch(&self, ctx: &mut Context, request: Request) -> Result<Response> {
124        self.dispatcher.dispatch(ctx, request)
125    }
126}
127
128/// Key manager status update handler callback.
129pub type KeyManagerStatusHandler = dyn Fn(KeyManagerStatus) + Send + Sync;
130/// Key manager quote policy update handler callback.
131pub type KeyManagerQuotePolicyHandler = dyn Fn(QuotePolicy) + Send + Sync;
132
133/// RPC call dispatcher.
134#[derive(Default)]
135pub struct Dispatcher {
136    /// Registered RPC methods.
137    methods: HashMap<String, Method>,
138    /// Registered key manager status handler.
139    km_status_handler: Option<Box<KeyManagerStatusHandler>>,
140    /// Registered key manager quote policy handler.
141    km_quote_policy_handler: Option<Box<KeyManagerQuotePolicyHandler>>,
142}
143
144impl Dispatcher {
145    /// Register a new method in the dispatcher.
146    pub fn add_method(&mut self, method: Method) {
147        self.methods.insert(method.get_name().clone(), method);
148    }
149
150    /// Register new methods in the dispatcher.
151    pub fn add_methods(&mut self, methods: Vec<Method>) {
152        for method in methods {
153            self.add_method(method);
154        }
155    }
156
157    /// Dispatch request.
158    pub fn dispatch(&self, mut ctx: Context, request: Request, kind: Kind) -> Response {
159        match self.dispatch_fallible(&mut ctx, request, kind) {
160            Ok(response) => response,
161            Err(error) => Response {
162                body: Body::Error(format!("{error}")),
163            },
164        }
165    }
166
167    fn dispatch_fallible(
168        &self,
169        ctx: &mut Context,
170        request: Request,
171        kind: Kind,
172    ) -> Result<Response> {
173        let method = match self.methods.get(&request.method) {
174            Some(method) => method,
175            None => bail!(DispatchError::MethodNotFound {
176                method: request.method,
177            }),
178        };
179
180        if method.get_kind() != kind {
181            bail!(DispatchError::InvalidRpcKind {
182                method: request.method,
183                kind,
184            });
185        };
186
187        method.dispatch(ctx, request)
188    }
189
190    /// Handle key manager status update.
191    pub fn handle_km_status_update(&self, status: KeyManagerStatus) {
192        if let Some(handler) = self.km_status_handler.as_ref() {
193            handler(status)
194        }
195    }
196
197    /// Handle key manager quote policy update.
198    pub fn handle_km_quote_policy_update(&self, policy: QuotePolicy) {
199        if let Some(handler) = self.km_quote_policy_handler.as_ref() {
200            handler(policy)
201        }
202    }
203
204    /// Update key manager status update handler.
205    pub fn set_keymanager_status_update_handler(
206        &mut self,
207        f: Option<Box<KeyManagerStatusHandler>>,
208    ) {
209        self.km_status_handler = f;
210    }
211
212    /// Update key manager quote policy update handler.
213    pub fn set_keymanager_quote_policy_update_handler(
214        &mut self,
215        f: Option<Box<KeyManagerQuotePolicyHandler>>,
216    ) {
217        self.km_quote_policy_handler = f;
218    }
219}