dhcpv6_client/
provider.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl::endpoints::ServerEnd;
6use fidl_fuchsia_net_dhcpv6::{ClientMarker, ClientProviderRequest, ClientProviderRequestStream};
7use fidl_fuchsia_net_dhcpv6_ext::NewClientParams;
8
9use futures::{Future, StreamExt as _};
10
11use anyhow::Result;
12use log::warn;
13
14/// Handles client provider requests from the input stream.
15pub(crate) async fn run_client_provider<Fut, F>(
16    stream: ClientProviderRequestStream,
17    serve_client: F,
18) where
19    Fut: Future<Output = Result<()>>,
20    F: Fn(NewClientParams, ServerEnd<ClientMarker>) -> Fut,
21{
22    stream
23        .for_each_concurrent(None, |request| async {
24            match request {
25                Ok(ClientProviderRequest::NewClient { params, request, control_handle: _ }) => {
26                    let params: NewClientParams = match params.try_into() {
27                        Ok(params) => params,
28                        Err(e) => {
29                            warn!("NewClientParams validation error: {}", e);
30                            // All param fields are required.
31                            request
32                                .close_with_epitaph(zx::Status::INVALID_ARGS)
33                                .unwrap_or_else(|e| warn!("closing NewClient request channel with epitaph INVALID_ARGS: {}", e));
34                            return;
35                        }
36                    };
37                    // `NewClientParams` does not implement `Clone`. It is also non-trivial to pass
38                    // a reference of `params` to `serve_client` because that would require adding
39                    // lifetimes in quite a few places.
40                    let params_str = format!("{:?}", params);
41                    let () =
42                        serve_client(params, request).await.unwrap_or_else(|e: anyhow::Error| {
43                            // TODO(https://fxbug.dev/42069288): Return error through
44                            // a terminal event.
45                            warn!("error running client with params {}: {:?}", params_str, e);
46                        });
47                }
48                Err(e) => warn!("client provider request FIDL error: {}", e),
49            }
50        })
51        .await
52}
53
54#[cfg(test)]
55mod tests {
56    use fidl::endpoints::create_endpoints;
57    use fidl_fuchsia_net_dhcpv6::ClientProviderMarker;
58    use fidl_fuchsia_net_dhcpv6_ext::ClientConfig;
59    use fuchsia_async as fasync;
60    use futures::join;
61
62    use anyhow::{anyhow, Error};
63    use assert_matches::assert_matches;
64    use net_declare::fidl_socket_addr_v6;
65
66    use super::*;
67
68    async fn serve_client(
69        _param: NewClientParams,
70        _request: ServerEnd<ClientMarker>,
71    ) -> Result<()> {
72        Ok(())
73    }
74
75    async fn start_err_client(
76        _param: NewClientParams,
77        _request: ServerEnd<ClientMarker>,
78    ) -> Result<()> {
79        Err(anyhow!("fake test error"))
80    }
81
82    async fn test_client_provider<Fut, F>(serve_client: F)
83    where
84        Fut: Future<Output = Result<()>>,
85        F: Fn(NewClientParams, ServerEnd<ClientMarker>) -> Fut,
86    {
87        let (client_end, server_end) = create_endpoints::<ClientProviderMarker>();
88        let client_provider_proxy = client_end.into_proxy();
89        let client_provider_stream = server_end.into_stream();
90
91        let test_fut = async {
92            for interface_id in 0..10 {
93                let (_client_end, server_end) = create_endpoints::<ClientMarker>();
94                client_provider_proxy
95                    .new_client(
96                        &NewClientParams {
97                            interface_id: interface_id,
98                            address: fidl_socket_addr_v6!("[fe01::1:2]:546"),
99                            config: ClientConfig {
100                                information_config: Default::default(),
101                                non_temporary_address_config: Default::default(),
102                                prefix_delegation_config: None,
103                            },
104                            duid: None,
105                        }
106                        .into(),
107                        server_end,
108                    )
109                    .expect("failed to request new client");
110            }
111            drop(client_provider_proxy);
112            Ok(())
113        };
114        let provider_fut = run_client_provider(client_provider_stream, serve_client);
115
116        let (test_res, ()): (Result<_, Error>, ()) = join!(test_fut, provider_fut);
117        assert_matches!(test_res, Ok(()));
118    }
119
120    #[fasync::run_singlethreaded(test)]
121    async fn test_client_provider_serve_client_success() {
122        let () = test_client_provider(serve_client).await;
123    }
124
125    #[fasync::run_singlethreaded(test)]
126    async fn test_client_provider_should_keep_running_on_client_err() {
127        let () = test_client_provider(start_err_client).await;
128    }
129}