1#![no_std]
2
3mod con;
59mod handler;
60#[doc(hidden)]
61pub mod mock;
62mod sink;
63mod source;
64pub mod tcp;
65
66pub use con::*;
67pub use handler::*;
68pub use sink::*;
69pub use source::*;
70
71#[cfg(test)]
72mod test {
73 use super::*;
74 use core::str::from_utf8;
75 use heapless::consts::*;
76 use heapless::{ArrayLength, String, Vec};
77
78 fn init() {
79 let _ = env_logger::builder().is_test(true).try_init();
80 }
81
82 #[test]
83 fn idea() -> Result<(), ()> {
84 init();
85
86 let mut sink_buffer = Vec::<u8, U1024>::new();
87 let con = HttpConnection::<U1024>::new();
88
89 let headers = [("Content-Type", "text/json")];
90
91 let handler = BufferResponseHandler::<U1024>::new();
92
93 let mut req = {
94 con.post("/foo.bar")
95 .headers(&headers)
96 .handler(handler)
97 .execute::<_, U128>(&mut sink_buffer)
98 };
99
100 req.push_data(b"HTTP/1.1 ");
103 req.push_data(b"200 OK\r\n");
104 req.push_data(b"\r\n");
105 req.push_data(b"123");
106 req.push_close();
107
108 let (_, handler) = req.complete();
109
110 assert_eq!(
113 String::from_utf8(sink_buffer).unwrap().as_str(),
114 "POST /foo.bar HTTP/1.1\r\nContent-Type: text/json\r\n\r\n",
115 );
116
117 assert_eq!(200, handler.code());
120 assert_eq!("OK", handler.reason());
121 assert_eq!(core::str::from_utf8(handler.payload()), Ok("123"));
122
123 assert!(handler.is_complete());
124
125 Ok(())
128 }
129
130 #[test]
131 fn simple() {
132 assert_http(
133 "POST",
134 "/",
135 &[],
136 None,
137 b"POST / HTTP/1.1\r\n\r\n",
138 &[b"HTTP/1.1 200 OK\r\n\r\n0123456789"],
139 200,
140 "OK",
141 b"0123456789",
142 );
143 }
144
145 #[test]
146 fn simple_split_1() {
147 assert_http(
148 "POST",
149 "/",
150 &[],
151 None,
152 b"POST / HTTP/1.1\r\n\r\n",
153 &[b"HTTP/1.1 200 OK\r\n\r\n01234", b"56789"],
154 200,
155 "OK",
156 b"0123456789",
157 );
158 }
159
160 #[test]
161 fn simple_split_2() {
162 assert_http(
163 "POST",
164 "/",
165 &[],
166 None,
167 b"POST / HTTP/1.1\r\n\r\n",
168 &[b"HTTP/1.1 200 ", b"OK\r\n\r\n01234", b"56789"],
169 200,
170 "OK",
171 b"0123456789",
172 );
173 }
174
175 #[test]
176 fn simple_header() {
177 assert_http(
178 "POST",
179 "/",
180 &[("Content-Type", "text/json")],
181 None,
182 b"POST / HTTP/1.1\r\nContent-Type: text/json\r\n\r\n",
183 &[b"HTTP/1.1 200 OK\r\n\r\n0123456789"],
184 200,
185 "OK",
186 b"0123456789",
187 );
188 }
189
190 #[test]
191 fn simple_send_payload() {
192 assert_http(
193 "POST",
194 "/",
195 &[("Content-Type", "text/json")],
196 Some(b"0123456789"),
197 b"POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Type: text/json\r\n\r\n0123456789",
198 &[b"HTTP/1.1 200 OK\r\n\r\n0123456789"],
199 200,
200 "OK",
201 b"0123456789",
202 );
203 }
204
205 #[test]
206 fn multiple() {
207 let expected = &[
208 &b"POST / HTTP/1.1\r\nContent-Type: text/plain\r\n\r\n"[..],
209 &b"POST / HTTP/1.1\r\nContent-Type: text/plain\r\n\r\n"[..],
210 ];
211 let mut mock_sink = MockSinkImpl::<U1024>::new(expected);
212
213 let con = HttpConnection::<U1024>::new();
214
215 let con = assert_request(
216 con,
217 &mut mock_sink,
218 "POST",
219 "/",
220 &[("Content-Type", "text/plain")],
221 None,
222 &[b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n0123456789"],
223 false,
224 200,
225 "OK",
226 b"0123456789",
227 );
228
229 assert_request(
230 con,
231 &mut mock_sink,
232 "POST",
233 "/",
234 &[("Content-Type", "text/plain")],
235 None,
236 &[b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n0123456789"],
237 true,
238 200,
239 "OK",
240 b"0123456789",
241 );
242 }
243
244 fn assert_request<IN, S>(
245 con: HttpConnection<IN>,
246 sink: &mut S,
247 method: &'static str,
248 path: &'static str,
249 headers: &[(&str, &str)],
250 payload: Option<&[u8]>,
251 push: &[&[u8]],
252 close_after_push: bool,
253 code: u16,
254 reason: &str,
255 expected_payload: &[u8],
256 ) -> HttpConnection<IN>
257 where
258 IN: ArrayLength<u8>,
259 S: Sink + MockSink,
260 {
261 let handler = BufferResponseHandler::<U1024>::new();
264
265 let mut req = {
268 con.begin(method, path)
269 .headers(&headers)
270 .handler(handler)
271 .execute_with::<_, U1024>(sink, payload)
272 };
273
274 for p in push {
277 req.push_data(p);
278 }
279
280 if close_after_push {
281 req.push_close();
282 }
283
284 let (con, handler) = req.complete();
287
288 sink.assert();
291
292 assert_eq!(code, handler.code());
295 assert_eq!(reason, handler.reason());
296
297 assert_eq!(
298 core::str::from_utf8(handler.payload()),
299 core::str::from_utf8(expected_payload)
300 );
301
302 assert!(handler.is_complete());
303
304 con
305 }
306
307 fn assert_http<'m>(
308 method: &'static str,
309 path: &'static str,
310 headers: &[(&str, &str)],
311 payload: Option<&[u8]>,
312 expected_sink: &'m [u8],
313 push: &[&[u8]],
314 code: u16,
315 reason: &str,
316 expected_payload: &[u8],
317 ) {
318 let expected = &[expected_sink];
321 let mut mock_sink = MockSinkImpl::<U1024>::new(expected);
322
323 let con = HttpConnection::<U1024>::new();
324
325 assert_request(
326 con,
327 &mut mock_sink,
328 method,
329 path,
330 headers,
331 payload,
332 push,
333 true,
334 code,
335 reason,
336 expected_payload,
337 );
338 }
339
340 pub(crate) struct MockSinkImpl<'m, N>
341 where
342 N: ArrayLength<u8>,
343 {
344 buffer: Vec<u8, N>,
345 iter: core::slice::Iter<'m, &'m [u8]>,
346 }
347
348 impl<'m, N> MockSinkImpl<'m, N>
349 where
350 N: ArrayLength<u8>,
351 {
352 pub fn new(expected: &'m [&'m [u8]]) -> Self {
353 let i = expected.iter();
354 MockSinkImpl {
355 buffer: Vec::<u8, N>::new(),
356 iter: i,
357 }
358 }
359 }
360
361 impl<'m, N> Sink for MockSinkImpl<'m, N>
362 where
363 N: ArrayLength<u8>,
364 {
365 fn send(&mut self, data: &[u8]) -> Result<usize, ()> {
366 (&mut self.buffer).send(data)
367 }
368 }
369
370 pub trait MockSink {
371 fn assert(&mut self);
372 }
373
374 impl<'m, N> MockSink for MockSinkImpl<'m, N>
375 where
376 N: ArrayLength<u8>,
377 {
378 fn assert(&mut self) {
379 let expected = self.iter.next();
380
381 assert_eq!(
384 expected.and_then(|b| from_utf8(b).ok()),
385 from_utf8(self.buffer.as_ref()).ok(),
386 );
387
388 self.buffer.clear();
390 }
391 }
392}