1use crate::bss_cache::{Bss, BssId};
6use async_trait::async_trait;
7use fidl::{Error as FidlError, Socket as FidlSocket};
8use fidl_fuchsia_location_position::{Position, PositionExtras};
9use fidl_fuchsia_mem::Buffer as MemBuffer;
10use fidl_fuchsia_net_http::{
11 Body as HttpBody, LoaderProxyInterface, Request as HttpRequest, Response as HttpResponse,
12};
13
14use futures::io::AsyncReadExt;
15use futures::Future;
16use itertools::Itertools;
17use serde_json::json;
18use serde_json::map::Map as JsonMap;
19use serde_json::value::Value as JsonValue;
20use static_assertions::assert_eq_size_val;
21use std::borrow::Borrow;
22
23const LATITUDE_RANGE: std::ops::RangeInclusive<f64> = -90.0..=90.0;
25const LONGITUDE_RANGE: std::ops::RangeInclusive<f64> = -180.0..=180.0;
26
27const SERVICE_URL: &'static str = "https://www.googleapis.com/geolocation/v1/geolocate";
29const MAC_ADDR_KEY: &'static str = "macAddress";
30const AP_LIST_KEY: &'static str = "wifiAccessPoints";
31const RSSI_KEY: &'static str = "signalStrength";
32const LOCATION_KEY: &'static str = "location";
33const LATITUDE_KEY: &'static str = "lat";
34const LONGITUDE_KEY: &'static str = "lng";
35const ACCURACY_KEY: &'static str = "accuracy";
36
37const HTTP_METHOD_POST: &'static str = "POST";
39
40#[async_trait(?Send)]
41pub trait BssResolver {
42 async fn resolve<'a, I, T, U>(&self, bsses: I) -> Result<Position, ResolverError>
44 where
45 I: IntoIterator,
46 I::Item: Borrow<(T, U)>,
47 T: Borrow<BssId>,
48 U: Borrow<Bss>;
49}
50
51pub struct RealBssResolver<L: LoaderProxyInterface> {
53 http_loader: L,
54 api_key: String,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq)]
58pub enum ResolverError {
59 NoBsses,
60 Internal,
61 Lookup,
62}
63
64impl<L: LoaderProxyInterface> RealBssResolver<L> {
65 pub fn new(http_loader: L, api_key: impl Into<String>) -> Self {
66 Self { http_loader, api_key: api_key.into() }
67 }
68}
69
70#[async_trait(?Send)]
71impl<L: LoaderProxyInterface> BssResolver for RealBssResolver<L> {
72 async fn resolve<'a, I, T, U>(&self, bsses: I) -> Result<Position, ResolverError>
73 where
74 I: IntoIterator,
75 I::Item: Borrow<(T, U)>,
76 T: Borrow<BssId>,
77 U: Borrow<Bss>,
78 {
79 let mut bsses = bsses.into_iter().peekable();
80 if bsses.peek().is_none() {
81 return Err(ResolverError::NoBsses);
82 }
83 parse_response(send_query(bsses, &self.api_key, &self.http_loader)?.await).await
84 }
85}
86
87fn send_query<'a, I, T, U>(
88 bsses: I,
89 api_key: impl AsRef<str>,
90 http_loader: &impl LoaderProxyInterface,
91) -> Result<impl Future<Output = Result<HttpResponse, FidlError>>, ResolverError>
92where
93 I: Iterator,
94 I::Item: Borrow<(T, U)>,
95 T: Borrow<BssId>,
96 U: Borrow<Bss>,
97{
98 let api_key = api_key.as_ref();
99 let request_body = serialize_bsses(bsses);
100 let request_size = {
101 let body_size = request_body.len();
102 assert_eq_size_val!(body_size, 0u64);
103 u64::try_from(body_size).expect("failed to convert usize to u64")
104 };
105 let request_vmo = zx::Vmo::create(request_size).map_err(|_| ResolverError::Internal)?;
106 let _ = request_vmo.write(&request_body.as_bytes(), 0).map_err(|_| ResolverError::Internal)?;
107 Ok(http_loader.fetch(HttpRequest {
108 method: Some(HTTP_METHOD_POST.to_owned()),
109 url: Some(format!("{}?key={}", SERVICE_URL, api_key)),
110 headers: None,
111 body: Some(HttpBody::Buffer(MemBuffer { vmo: request_vmo, size: request_size })),
112 deadline: None,
113 ..Default::default()
114 }))
115}
116
117async fn parse_response(
118 response: Result<HttpResponse, FidlError>,
119) -> Result<Position, ResolverError> {
120 let response: HttpResponse = response.map_err(|_fidl_error| ResolverError::Internal)?;
121 let response: FidlSocket = response.body.ok_or(ResolverError::Lookup)?;
122 let response: String = read_socket(response).await.ok_or(ResolverError::Lookup)?;
123 json_to_position(serde_json::from_str(&response).map_err(|_| ResolverError::Lookup)?)
124}
125
126fn serialize_bsses<'a, I, T, U>(bsses: I) -> String
127where
128 I: Iterator,
129 I::Item: Borrow<(T, U)>,
130 T: Borrow<BssId>,
131 U: Borrow<Bss>,
132{
133 let bsses = bsses
134 .map(|item| bss_to_json(item.borrow().0.borrow(), item.borrow().1.borrow()))
135 .collect::<Vec<_>>();
136 json!({ AP_LIST_KEY: bsses }).to_string()
137}
138
139fn bss_to_json(bss_id: impl Borrow<BssId>, bss: impl Borrow<Bss>) -> JsonValue {
140 let mut json = JsonMap::new();
141 json.insert(
142 MAC_ADDR_KEY.to_owned(),
143 json!(bss_id.borrow().iter().map(|bss_byte| format!("{:02x}", bss_byte)).join(":")),
144 );
145 if let Some(rssi) = bss.borrow().rssi {
146 json.insert(RSSI_KEY.to_owned(), json!(rssi));
147 };
148 JsonValue::from(json)
149}
150
151async fn read_socket(socket: zx::Socket) -> Option<String> {
152 let mut buf = Vec::new();
153 let mut socket = fuchsia_async::Socket::from_socket(socket);
154 match socket.read_to_end(&mut buf).await {
155 Ok(_num_bytes_read) => String::from_utf8(buf).ok(),
156 Err(_) => None,
157 }
158}
159
160fn json_to_position(response: JsonValue) -> Result<Position, ResolverError> {
161 let latitude = response[LOCATION_KEY][LATITUDE_KEY].as_f64().ok_or(ResolverError::Lookup)?;
162 if !LATITUDE_RANGE.contains(&latitude) {
163 return Err(ResolverError::Lookup);
164 }
165
166 let longitude = response[LOCATION_KEY][LONGITUDE_KEY].as_f64().ok_or(ResolverError::Lookup)?;
167 if !LONGITUDE_RANGE.contains(&longitude) {
168 return Err(ResolverError::Lookup);
169 }
170
171 let extras = match response[ACCURACY_KEY].as_f64() {
172 Some(accuracy) => PositionExtras {
173 accuracy_meters: Some(accuracy),
174 altitude_meters: None,
175 ..Default::default()
176 },
177 None => {
178 PositionExtras { accuracy_meters: None, altitude_meters: None, ..Default::default() }
179 }
180 };
181
182 Ok(Position { latitude, longitude, extras })
183}
184
185#[cfg(test)]
186mod tests {
187 mod request_generation {
188 use super::super::test_doubles::HttpRequestValidator;
189 use super::super::*;
190 use fuchsia_async as fasync;
191
192 #[fasync::run_until_stalled(test)]
193 async fn request_uses_post_method() {
194 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: Some(-30), frequency: Some(2412) })];
195 let mut request_method = None;
196 let http_loader = HttpRequestValidator::new(|request| {
197 request_method = Some(request.method.expect("request had no method"));
198 });
199 let _ = RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await;
200 assert_eq!(request_method.expect("request was never issued"), "POST".to_owned());
201 }
202
203 #[fasync::run_until_stalled(test)]
204 async fn request_url_is_correct() {
205 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: Some(-30), frequency: Some(2412) })];
206 let mut request_url = None;
207 let http_loader = HttpRequestValidator::new(|request| {
208 request_url = Some(request.url.expect("request had no url"));
209 });
210 let _ = RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await;
211 assert_eq!(
212 request_url.expect("request was never issued"),
213 "https://www.googleapis.com/geolocation/v1/geolocate?key=fake_key".to_owned()
214 );
215 }
216
217 #[fasync::run_until_stalled(test)]
218 async fn request_body_contains_all_bsses() {
219 let bsses = vec![
220 ([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None }),
221 ([1, 1, 1, 1, 1, 1], Bss { rssi: None, frequency: None }),
222 ];
223 let mut request_body = None;
224 let http_loader = HttpRequestValidator::new(|request| {
225 request_body = Some(request.body.expect("request had no body"));
226 });
227 let _ = RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await;
228 assert_eq!(
229 strip_whitespace(read_request_body(
230 request_body.expect("request was never issued")
231 )),
232 strip_whitespace(
233 r#"{
234 "wifiAccessPoints":[
235 {"macAddress":"00:00:00:00:00:00"},
236 {"macAddress":"01:01:01:01:01:01"}
237 ]
238 }"#
239 )
240 );
241 }
242
243 #[fasync::run_until_stalled(test)]
244 async fn request_body_includes_rssi() {
245 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: Some(-30), frequency: None })];
246 let mut request_body = None;
247 let http_loader = HttpRequestValidator::new(|request| {
248 request_body = Some(request.body.expect("request had no body"));
249 });
250 let _ = RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await;
251 assert_eq!(
252 strip_whitespace(read_request_body(
253 request_body.expect("request was never issued")
254 )),
255 strip_whitespace(
256 r#"{
257 "wifiAccessPoints":[
258 {"macAddress":"00:00:00:00:00:00",
259 "signalStrength": -30}
260 ]
261 }"#
262 )
263 )
264 }
265
266 #[fasync::run_until_stalled(test)]
267 async fn skips_api_query_when_bsses_is_empty() {
268 let http_loader =
269 HttpRequestValidator::new(|_request| panic!("should not issue API query"));
270 let _ = RealBssResolver::new(http_loader, "fake_key")
271 .resolve(std::iter::empty::<(BssId, Bss)>())
272 .await;
273 }
274
275 #[fasync::run_until_stalled(test)]
276 async fn returns_no_bsses_error_when_bsses_is_empty() {
277 let http_loader = HttpRequestValidator::new(|_| ());
278 assert_eq!(
279 RealBssResolver::new(http_loader, "fake_key")
280 .resolve(std::iter::empty::<(BssId, Bss)>())
281 .await,
282 Err(ResolverError::NoBsses)
283 );
284 }
285
286 fn read_request_body(body: HttpBody) -> String {
287 match body {
288 HttpBody::Buffer(shared_buf) => {
289 let mut local_buf = vec![
290 0_u8;
291 usize::try_from(shared_buf.size).expect(
292 "internal error: failed to convert u64 to usize"
293 )
294 ];
295 shared_buf
296 .vmo
297 .read(&mut local_buf, 0)
298 .expect("internal error: failed to read from VMO");
299 String::from_utf8(local_buf)
300 .expect("internal error: failed to convert buffer to UTF-8")
301 }
302 HttpBody::Stream(_) => panic!("internal error: stream bodies not supported"),
303 }
304 }
305
306 fn strip_whitespace<S: AsRef<str>>(input: S) -> String {
307 input.as_ref().replace("\n", "").replace(" ", "")
308 }
309 }
310
311 mod response_error_handling {
312 use super::super::test_doubles::{HttpByteResponder, HttpFidlResponder};
313 use super::super::*;
314 use fuchsia_async as fasync;
315
316 #[fasync::run_until_stalled(test)]
317 async fn returns_internal_error_on_fidl_error() {
318 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
319 let http_loader = HttpFidlResponder::new(|| Err(FidlError::InvalidHeader));
320 assert_eq!(
321 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
322 Err(ResolverError::Internal)
323 );
324 }
325
326 #[fasync::run_until_stalled(test)]
327 async fn returns_lookup_error_when_response_has_no_body() {
328 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
329 let http_loader = HttpFidlResponder::new(|| {
330 Ok(HttpResponse {
331 error: None,
332 body: None,
333 final_url: None,
334 status_code: None,
335 status_line: None,
336 headers: None,
337 redirect: None,
338 ..Default::default()
339 })
340 });
341 assert_eq!(
342 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
343 Err(ResolverError::Lookup)
344 );
345 }
346
347 #[fasync::run_until_stalled(test)]
348 async fn returns_lookup_error_when_body_is_unreadable() {
349 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
350 let http_loader = HttpFidlResponder::new(|| {
351 Ok(HttpResponse {
352 error: None,
353 body: Some(zx::Socket::from(zx::Handle::invalid())),
354 final_url: None,
355 status_code: None,
356 status_line: None,
357 headers: None,
358 redirect: None,
359 ..Default::default()
360 })
361 });
362 assert_eq!(
363 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
364 Err(ResolverError::Lookup)
365 );
366 }
367
368 #[fasync::run_until_stalled(test)]
369 async fn returns_lookup_error_when_body_is_not_valid_json() {
370 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
371 let http_loader = HttpByteResponder::new(b"hello world".to_vec());
372 assert_eq!(
373 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
374 Err(ResolverError::Lookup)
375 );
376 }
377
378 #[fasync::run_until_stalled(test)]
379 async fn returns_lookup_error_when_body_is_not_a_dictionary() {
380 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
381 let http_loader = HttpByteResponder::new(b"42".to_vec());
382 assert_eq!(
383 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
384 Err(ResolverError::Lookup)
385 );
386 }
387
388 #[fasync::run_until_stalled(test)]
389 async fn returns_lookup_error_when_body_is_missing_location() {
390 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
391 let http_loader = HttpByteResponder::new(b"{}".to_vec());
392 assert_eq!(
393 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
394 Err(ResolverError::Lookup)
395 );
396 }
397
398 #[fasync::run_until_stalled(test)]
399 async fn returns_lookup_error_when_body_is_missing_latitude() {
400 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
401 let http_loader = HttpByteResponder::new(
402 br#"{
403 "location": {
404 "lng": -0.1
405 },
406 "accuracy": 1200.4
407 }"#
408 .to_vec(),
409 );
410 assert_eq!(
411 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
412 Err(ResolverError::Lookup)
413 );
414 }
415
416 #[fasync::run_until_stalled(test)]
417 async fn returns_lookup_error_when_body_is_missing_longitude() {
418 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
419 let http_loader = HttpByteResponder::new(
420 br#"{
421 "location": {
422 "lat": 51.0,
423 },
424 "accuracy": 1200.4
425 }"#
426 .to_vec(),
427 );
428 assert_eq!(
429 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
430 Err(ResolverError::Lookup)
431 );
432 }
433
434 #[fasync::run_until_stalled(test)]
435 async fn returns_lookup_error_when_latitude_is_too_high() {
436 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
437 let http_loader = HttpByteResponder::new(
438 br#"{
439 "location": {
440 "lat": 90.1,
441 "lng": 0.0
442 }
443 }"#
444 .to_vec(),
445 );
446 assert_eq!(
447 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
448 Err(ResolverError::Lookup)
449 );
450 }
451
452 #[fasync::run_until_stalled(test)]
453 async fn returns_lookup_error_when_latitude_is_too_low() {
454 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
455 let http_loader = HttpByteResponder::new(
456 br#"{
457 "location": {
458 "lat": -90.1,
459 "lng": 0.0
460 }
461 }"#
462 .to_vec(),
463 );
464 assert_eq!(
465 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
466 Err(ResolverError::Lookup)
467 );
468 }
469
470 #[fasync::run_until_stalled(test)]
471 async fn returns_lookup_error_when_longitude_is_too_high() {
472 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
473 let http_loader = HttpByteResponder::new(
474 br#"{
475 "location": {
476 "lat": 0.0,
477 "lng": 180.1
478 }
479 }"#
480 .to_vec(),
481 );
482 assert_eq!(
483 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
484 Err(ResolverError::Lookup)
485 );
486 }
487
488 #[fasync::run_until_stalled(test)]
489 async fn returns_lookup_error_when_longitude_is_too_low() {
490 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
491 let http_loader = HttpByteResponder::new(
492 br#"{
493 "location": {
494 "lat": 0.0,
495 "lng": -180.1
496 }
497 }"#
498 .to_vec(),
499 );
500 assert_eq!(
501 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
502 Err(ResolverError::Lookup)
503 );
504 }
505 }
506
507 mod response_success_reporting {
508 use super::super::test_doubles::HttpByteResponder;
509 use super::super::*;
510 use assert_matches::assert_matches;
511 use fuchsia_async as fasync;
512
513 #[fasync::run_until_stalled(test)]
514 async fn returns_success_when_all_fields_are_present() {
515 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
516 let http_loader = HttpByteResponder::new(
517 br#"{
518 "location": {
519 "lat": 51.0,
520 "lng": -0.1
521 },
522 "accuracy": 1200.4
523 }"#
524 .to_vec(),
525 );
526 assert_matches!(
527 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
528 Ok(_)
529 );
530 }
531
532 #[fasync::run_until_stalled(test)]
533 async fn returns_success_when_all_fields_except_accuracy_are_present() {
534 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
535 let http_loader = HttpByteResponder::new(
536 br#"{
537 "location": {
538 "lat": 51.0,
539 "lng": -0.1
540 }
541 }"#
542 .to_vec(),
543 );
544 assert_matches!(
545 RealBssResolver::new(http_loader, "fake_key").resolve(bsses).await,
546 Ok(_)
547 );
548 }
549 }
550
551 mod response_success_contents {
552 use super::super::test_doubles::HttpByteResponder;
553 use super::super::*;
554 use assert_matches::assert_matches;
555 use fuchsia_async as fasync;
556
557 #[fasync::run_until_stalled(test)]
558 async fn provides_precise_latitude() {
559 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
560 let http_loader = HttpByteResponder::new(
561 br#"{
562 "location": {
563 "lat": 89.0,
564 "lng": 0
565 }
566 }"#
567 .to_vec(),
568 );
569 assert_matches!(
570 RealBssResolver::new(http_loader, "fake_key")
571 .resolve(bsses)
572 .await.expect("position is none").latitude,
573 latitude if (88.999_999_9..89.000_000_1).contains(&latitude)
577 );
578 }
579
580 #[fasync::run_until_stalled(test)]
581 async fn provides_precise_longitude() {
582 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
583 let http_loader = HttpByteResponder::new(
584 br#"{
585 "location": {
586 "lat": 0,
587 "lng": 179.0
588 }
589 }"#
590 .to_vec(),
591 );
592 assert_matches!(
593 RealBssResolver::new(http_loader, "fake_key")
594 .resolve(bsses)
595 .await.expect("no position").longitude,
596 longitude if (178.999_999_9..179.099_999_9).contains(&longitude)
600 );
601 }
602
603 #[fasync::run_until_stalled(test)]
604 async fn provides_precise_accuracy_when_present() {
605 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
606 let http_loader = HttpByteResponder::new(
607 br#"{
608 "location": {
609 "lat": 51.0,
610 "lng": -0.1
611 },
612 "accuracy": 1200.4
613 }"#
614 .to_vec(),
615 );
616 assert_matches!(
617 RealBssResolver::new(http_loader, "fake_key")
618 .resolve(bsses)
619 .await.expect("no position").extras.accuracy_meters.expect("accuracy is none"),
620 accuracy if (1200.39..1200.41).contains(&accuracy)
621 );
622 }
623
624 #[fasync::run_until_stalled(test)]
625 async fn does_not_fabricate_accuracy() {
626 let bsses = vec![([0, 0, 0, 0, 0, 0], Bss { rssi: None, frequency: None })];
627 let http_loader = HttpByteResponder::new(
628 br#"{
629 "location": {
630 "lat": 51.0,
631 "lng": -0.1
632 }
633 }"#
634 .to_vec(),
635 );
636 assert_eq!(
637 RealBssResolver::new(http_loader, "fake_key")
638 .resolve(bsses)
639 .await
640 .expect("no position")
641 .extras
642 .accuracy_meters,
643 None
644 );
645 }
646 }
647}
648
649#[cfg(test)]
650mod test_doubles {
651 use super::*;
652 use fidl::endpoints::ClientEnd;
653 use fidl_fuchsia_net_http::LoaderClientMarker;
654 use futures::future::{ready, Ready};
655 use std::sync::RwLock;
656
657 const HTTP_OK: u32 = 200;
658
659 type FetchResponse = Result<HttpResponse, FidlError>;
660
661 pub(super) struct HttpRequestValidator<F>
665 where
666 F: FnMut(HttpRequest) + Send + Sync,
667 {
668 validate: RwLock<F>,
672 }
673
674 pub(super) struct HttpFidlResponder<F>
678 where
679 F: Fn() -> FetchResponse + Send + Sync,
680 {
681 fetch: F,
682 }
683
684 pub(super) struct HttpByteResponder {
688 response: Vec<u8>,
689 }
690
691 impl<F> HttpRequestValidator<F>
692 where
693 F: FnMut(HttpRequest) + Send + Sync,
694 {
695 pub(super) fn new(validate: F) -> Self {
696 Self { validate: RwLock::new(validate) }
697 }
698 }
699
700 impl<F> LoaderProxyInterface for HttpRequestValidator<F>
701 where
702 F: FnMut(HttpRequest) + Send + Sync,
703 {
704 type FetchResponseFut = Ready<Result<HttpResponse, FidlError>>;
705
706 fn fetch(&self, request: HttpRequest) -> Self::FetchResponseFut {
707 let validate = &mut *self.validate.write().expect("internal error");
709 let final_url = request.url.clone();
710 validate(request);
711 ready(Ok(HttpResponse {
712 error: None,
713 body: None,
714 final_url,
715 status_code: Some(HTTP_OK),
716 status_line: None,
717 headers: None,
718 redirect: None,
719 ..Default::default()
720 }))
721 }
722
723 fn start(
724 &self,
725 _request: HttpRequest,
726 _client: ClientEnd<LoaderClientMarker>,
727 ) -> Result<(), FidlError> {
728 panic!("internal error: this fake does not implement `start()`");
729 }
730 }
731
732 impl<F> HttpFidlResponder<F>
733 where
734 F: Fn() -> FetchResponse + Send + Sync,
735 {
736 pub(super) fn new(fetch: F) -> Self {
737 Self { fetch }
738 }
739 }
740
741 impl<F> LoaderProxyInterface for HttpFidlResponder<F>
742 where
743 F: Fn() -> FetchResponse + Send + Sync,
744 {
745 type FetchResponseFut = Ready<FetchResponse>;
746
747 fn fetch(&self, _request: HttpRequest) -> Self::FetchResponseFut {
748 ready((self.fetch)())
749 }
750
751 fn start(
752 &self,
753 _request: HttpRequest,
754 _client: ClientEnd<LoaderClientMarker>,
755 ) -> Result<(), FidlError> {
756 panic!("internal error: this stub does not implement `start()`");
757 }
758 }
759
760 impl HttpByteResponder {
761 pub(super) fn new(response: Vec<u8>) -> Self {
762 Self { response }
763 }
764 }
765
766 impl LoaderProxyInterface for HttpByteResponder {
767 type FetchResponseFut = Ready<FetchResponse>;
768
769 fn fetch(&self, _request: HttpRequest) -> Self::FetchResponseFut {
770 let (local_socket, remote_socket) = zx::Socket::create_stream();
771 local_socket.write(&self.response).expect("internal error");
772 ready(Ok(HttpResponse {
773 error: None,
774 body: Some(remote_socket),
775 final_url: None,
776 status_code: Some(HTTP_OK),
777 status_line: None,
778 headers: None,
779 redirect: None,
780 ..Default::default()
781 }))
782 }
783
784 fn start(
785 &self,
786 _request: HttpRequest,
787 _client: ClientEnd<LoaderClientMarker>,
788 ) -> Result<(), FidlError> {
789 panic!("internal error: this stub does not implement `start()`");
790 }
791 }
792}