parsec_service/front/
front_end.rs

1// Copyright 2019 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3//! Entry point for IPC data into the service
4//!
5//! The front end handler accepts streams of data that it can use to read requests,
6//! pass them to the rest of the service and write the responses back.
7use crate::authenticators::Authenticate;
8use crate::back::dispatcher::Dispatcher;
9use crate::front::listener::Connection;
10use derivative::Derivative;
11use log::{info, trace};
12use parsec_interface::requests::AuthType;
13use parsec_interface::requests::ResponseStatus;
14use parsec_interface::requests::{Request, Response};
15use std::collections::HashMap;
16use std::io::{Error, ErrorKind, Result};
17
18/// Read and verify request from IPC stream
19///
20/// Service component that serializes requests and deserializes responses
21/// from/to the stream provided by the listener.
22///
23/// Requests are passed forward to the `Dispatcher`.
24#[derive(Derivative)]
25#[derivative(Debug)]
26pub struct FrontEndHandler {
27    dispatcher: Dispatcher,
28    // Send and Sync are required for Arc<FrontEndHandler> to be Send.
29    #[derivative(Debug = "ignore")]
30    authenticators: HashMap<AuthType, Box<dyn Authenticate + Send + Sync>>,
31    /// Value used to limit the size of the request body to be that can be accepted by the service.
32    body_len_limit: usize,
33}
34
35impl FrontEndHandler {
36    /// Handle new connections on the underlying IPC mechanism.
37    ///
38    /// Unmarshalls a request from the stream, passes it to the dispatcher and marshalls
39    /// the response back onto the stream.
40    ///
41    /// If an error occurs during (un)marshalling; no operation will be performed, an error will be logged
42    /// and the method will return.
43    pub fn handle_request(&self, mut connection: Connection) {
44        trace!("handle_request ingress");
45        // Read bytes from stream
46        // De-Serialise bytes into a request
47        let request = match Request::read_from_stream(&mut connection.stream, self.body_len_limit) {
48            Ok(request) => request,
49            Err(status) => {
50                format_error!("Failed to read request", status);
51
52                let response = Response::from_status(status);
53                if response.header.status != ResponseStatus::Success {
54                    format_error!("Sending back an error", response.header.status);
55                }
56                if let Err(status) = response.write_to_stream(&mut connection.stream) {
57                    format_error!("Failed to write response", status);
58                }
59                return;
60            }
61        };
62
63        // Check if the request was sent without authentication
64        let (app, err_response) = if AuthType::NoAuth == request.header.auth_type {
65            (None, None)
66        // Otherwise find an authenticator that is capable to authenticate the request
67        } else if let Some(authenticator) = self.authenticators.get(&request.header.auth_type) {
68            // Authenticate the request
69            match authenticator.authenticate(&request.auth, connection.metadata) {
70                // Send the request to the dispatcher
71                // Get a response back
72                Ok(app) => (Some(app), None),
73                Err(status) => (
74                    None,
75                    Some(Response::from_request_header(request.header, status)),
76                ),
77            }
78        } else {
79            (
80                None,
81                Some(Response::from_request_header(
82                    request.header,
83                    ResponseStatus::AuthenticatorNotRegistered,
84                )),
85            )
86        };
87
88        let response = if let Some(err_response) = err_response {
89            err_response
90        } else {
91            if crate::utils::GlobalConfig::log_error_details() {
92                if let Some(app) = &app.as_ref() {
93                    info!(
94                        "New request received from application name \"{}\"",
95                        app.identity().name()
96                    )
97                } else {
98                    info!("New request received without authentication")
99                }
100            };
101            let response = self.dispatcher.dispatch_request(request, app.clone());
102            trace!("dispatch_request egress");
103            response
104        };
105
106        // Serialise the response into bytes
107        // Write bytes to stream
108        match response.write_to_stream(&mut connection.stream) {
109            Ok(_) => {
110                if crate::utils::GlobalConfig::log_error_details() {
111                    if let Some(app) = app {
112                        info!(
113                            "Response for application name \"{}\" sent back",
114                            app.identity().name()
115                        );
116                    } else {
117                        info!("Response sent back from request without authentication");
118                    }
119                }
120            }
121            Err(err) => format_error!("Failed to send response", err),
122        }
123    }
124}
125
126/// Builder for `FrontEndHandler`
127#[derive(Default, Derivative)]
128#[derivative(Debug)]
129pub struct FrontEndHandlerBuilder {
130    dispatcher: Option<Dispatcher>,
131    #[derivative(Debug = "ignore")]
132    authenticators: Option<HashMap<AuthType, Box<dyn Authenticate + Send + Sync>>>,
133    body_len_limit: Option<usize>,
134}
135
136impl FrontEndHandlerBuilder {
137    /// Create a new FrontEndHandler builder
138    pub fn new() -> Self {
139        FrontEndHandlerBuilder {
140            dispatcher: None,
141            authenticators: None,
142            body_len_limit: None,
143        }
144    }
145
146    /// Add a dispatcher to the builder
147    pub fn with_dispatcher(mut self, dispatcher: Dispatcher) -> Self {
148        self.dispatcher = Some(dispatcher);
149        self
150    }
151
152    /// Add an authenticator to the builder
153    pub fn with_authenticator(
154        mut self,
155        auth_type: AuthType,
156        authenticator: Box<dyn Authenticate + Send + Sync>,
157    ) -> Self {
158        match &mut self.authenticators {
159            Some(authenticators) => {
160                let _ = authenticators.insert(auth_type, authenticator);
161            }
162            None => {
163                let mut map = HashMap::new();
164                let _ = map.insert(auth_type, authenticator);
165                self.authenticators = Some(map);
166            }
167        };
168
169        self
170    }
171
172    /// Set a limit on the maximal body length received
173    pub fn with_body_len_limit(mut self, body_len_limit: usize) -> Self {
174        self.body_len_limit = Some(body_len_limit);
175        self
176    }
177
178    /// Build into a FrontEndHandler
179    pub fn build(self) -> Result<FrontEndHandler> {
180        Ok(FrontEndHandler {
181            dispatcher: self
182                .dispatcher
183                .ok_or_else(|| Error::new(ErrorKind::InvalidData, "dispatcher is missing"))?,
184            authenticators: self
185                .authenticators
186                .ok_or_else(|| Error::new(ErrorKind::InvalidData, "authenticators is missing"))?,
187            body_len_limit: self
188                .body_len_limit
189                .ok_or_else(|| Error::new(ErrorKind::InvalidData, "body_len_limit is missing"))?,
190        })
191    }
192}