std_embedded_nal_coap/
std_embedded_nal_coap.rs

1use log::info;
2
3/// This function works on *any* UdpFullStack, including embedded ones -- only main() is what makes
4/// this use POSIX sockets. (It does make use of a std based RNG, but that could be passed in just
5/// as well for no_std operation).
6async fn run<S>(stack: &mut S)
7where
8    S: embedded_nal_async::UdpStack,
9{
10    let mut sock = stack
11        .bind_multiple(core::net::SocketAddr::new("::".parse().unwrap(), 5683))
12        .await
13        .expect("Can't create a socket");
14
15    let log = Some(coap_message_demos::log::Log::start_once());
16
17    let mut handler = coap_message_demos::full_application_tree(log);
18
19    info!("Server is ready.");
20
21    let coap = embedded_nal_coap::CoAPShared::<3>::new();
22    let (client, server) = coap.split();
23
24    // going with an embassy_futures join instead of an async_std::task::spawn b/c CoAPShared is not
25    // Sync, and async_std expects to work in multiple threads
26    embassy_futures::join::join(
27        async {
28            use rand::SeedableRng;
29            server
30                .run(
31                    &mut sock,
32                    &mut handler,
33                    &mut rand::rngs::StdRng::from_entropy(),
34                )
35                .await
36                .expect("UDP error")
37        },
38        run_client_operations(client),
39    )
40    .await;
41}
42
43#[async_std::main]
44async fn main() {
45    let mut stack = std_embedded_nal_async::Stack::default();
46
47    run(&mut stack).await;
48}
49
50/// In parallel to server operation, this function performs some operations as a client.
51///
52/// This doubles as an experimentation ground for the client side of embedded_nal_coap and
53/// coap-request in general.
54async fn run_client_operations<const N: usize>(
55    client: embedded_nal_coap::CoAPRuntimeClient<'_, N>,
56) {
57    let demoserver = "[::1]:1234".parse().unwrap();
58
59    use coap_request::Stack;
60    println!("Sending GET to {}...", demoserver);
61    let response = client
62        .to(demoserver)
63        .request(
64            coap_request_implementations::Code::get()
65                .with_path("/other/separate")
66                .processing_response_payload_through(|p| {
67                    println!("Got payload {:?}", p);
68                }),
69        )
70        .await;
71    println!("Response {:?}", response);
72
73    // This demonstrates that we don't leak requests, and (later) that we don't lock up when there
74    // are too many concurrent requests, and still adhere to protocol.
75    //
76    // Well, except for rate limiting...
77    println!("Sending 10 (>> 3) requests in short succession, forgetting them after a moment");
78    for _i in 0..10 {
79        embassy_futures::select::select(
80            client.to(demoserver).request(
81                coap_request_implementations::Code::get()
82                    .with_path("/other/separate")
83                    .processing_response_payload_through(|p| {
84                        println!("Got payload {:?}", p);
85                    }),
86            ),
87            // Knowing that /other/separate takes some time, this is definitely faster
88            async_std::task::sleep(std::time::Duration::from_millis(300)),
89        )
90        // The other future is dropped.
91        .await;
92    }
93    println!("Sending 10 (>> 3) requests in parallel, keeping all of them around");
94    // It's not NSTART that's limiting us here (although it should), it's .
95    let build_request = || {
96        // The async block allows us to keep the temporary client.to() that'd be otherwise limit
97        // the request's lifetime inside the Future.
98        let block = async {
99            client
100                .to(demoserver)
101                .request(
102                    coap_request_implementations::Code::get()
103                        .with_path("/other/separate")
104                        .processing_response_payload_through(|p| {
105                            println!("Got payload {:?} (truncated)", p.get(..5).unwrap_or(p));
106                        }),
107                )
108                .await
109        };
110        block
111    };
112    // That's not even that easy without TAIT and other trickery...
113    use embassy_futures::join::join;
114    join(
115        build_request(),
116        join(
117            build_request(),
118            join(
119                build_request(),
120                join(
121                    build_request(),
122                    join(
123                        build_request(),
124                        join(
125                            build_request(),
126                            join(
127                                build_request(),
128                                join(
129                                    build_request(),
130                                    join(
131                                        build_request(),
132                                        build_request(), // Hello LISP my old friend
133                                    ),
134                                ),
135                            ),
136                        ),
137                    ),
138                ),
139            ),
140        ),
141    )
142    .await;
143    println!("All through");
144
145    // What follows are experiments with the request payload setting functions of
146    // coap-request-implementations that have so far not been used successfully without
147    // force over lifetimes.
148
149    // Which of these two signatures I take makes the difference in whether this works
150    // (upper) or errs like the closure, no matter whether we go through paywriter_f or
151    // not.
152    fn paywriter<S: Stack>(m: &mut S::RequestMessage<'_>) {
153        // fn paywriter<'a, 'b>(m: &'a mut coap_message_utils::inmemory_write::Message<'b>) {
154        use coap_message::MinimalWritableMessage;
155        m.set_payload(b"Set time to 1955-11-05").unwrap();
156    }
157    // let paywriter_f: &mut _ = &mut paywriter;
158
159    // FIXME: This is needed for the with_request_callback variant that takes a function,
160    // and I don't yet understand why. (Clearly it's unacceptable as a consequence of the
161    // interface; question is, is it a consequence or did I just use it wrong).
162    // let client: &embedded_nal_coap::CoAPRuntimeClient<'_, 3> =
163    //     unsafe { core::mem::transmute(&client) };
164
165    // let mut paywriter_direct = paywriter;
166    // let paywriter_cl = |m: &mut <embedded_nal_coap::RequestingCoAPClient<3> as Stack>::RequestMessage<'_>| {
167    // // or let paywriter_cl = |m: &mut coap_message_utils::inmemory_write::Message<'_>| {
168    //                 use coap_message::MinimalWritableMessage;
169    //                 m.set_payload(b"Set time to 1955-11-05")
170    //             };
171
172    let req =
173            coap_request_implementations::Code::post()
174                .with_path("/uppercase")
175            // We can build everything up to this point outside, but pulling more of req
176            // starts failing. Is that a hint as to where the lifetime trouble comes from?
177            ;
178
179    println!("Sending POST...");
180    let mut response = client.to(demoserver);
181    let response = response.request(
182        req
183            // This works (but needs the unjustifiable lifetime extension above)
184            //                 .with_request_callback(&mut paywriter_direct)
185            // Does this work?
186            //                 .with_request_callback(paywriter_f)
187            // This fails with type mismatches
188            //                 .with_request_callback(&mut paywriter_cl)
189            // But this works because it is simple
190            .with_request_payload_slice(b"Set time to 1955-11-05")
191            .processing_response_payload_through(|p| {
192                println!("Uppercase is {}", core::str::from_utf8(p).unwrap())
193            }),
194    );
195    let response = response.await;
196    println!("Response {:?}", response);
197}