blob: ecc056cac13a62b8c5758a1339b063a6d49dd9c4 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![warn(missing_docs)]
//! `component_index` provides a fuzzy search for locally-known component URLs.
use anyhow::{Context as _, Error};
use fidl_fuchsia_sys_index::{
ComponentIndexRequest, ComponentIndexRequestStream, FuzzySearchError,
};
use fuchsia_async as fasync;
use fuchsia_component::server::ServiceFs;
use futures::prelude::*;
use std::fs;
use std::sync::Arc;
async fn run_fuzzy_search_server(
mut stream: ComponentIndexRequestStream,
index: Arc<Vec<String>>,
) -> Result<(), Error> {
while let Some(ComponentIndexRequest::FuzzySearch { needle, responder }) =
stream.try_next().await.context("Error serving fuzzy search")?
{
if !check_needle(&needle) {
responder
.send(&mut Err(FuzzySearchError::MalformedInput))
.context("error sending response")?;
} else {
let res: Vec<String> = index.iter().filter(|c| c.contains(&needle)).cloned().collect();
responder.send(&mut Ok(res)).context("error sending response")?;
}
}
Ok(())
}
/// Needle only accepts [A-Z a-z 0-9 / _ - .].
fn check_needle(needle: &str) -> bool {
return needle
.chars()
.all(|c| c.is_alphanumeric() || c == '/' || c == '_' || c == '-' || c == '.');
}
enum IncomingServices {
ComponentIndex(ComponentIndexRequestStream),
}
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let mut fs = ServiceFs::new_local();
let index_string = fs::read_to_string("/pkg/data/component_index.txt")
.expect("Error reading component_index.txt");
let index_vec: Vec<String> = index_string.lines().map(|l| l.to_string()).collect();
let index = Arc::new(index_vec);
fs.dir("svc").add_fidl_service(IncomingServices::ComponentIndex);
fs.take_and_serve_directory_handle()?;
const MAX_CONCURRENT: usize = 10_000;
let fut = fs.for_each_concurrent(MAX_CONCURRENT, |IncomingServices::ComponentIndex(stream)| {
run_fuzzy_search_server(stream, index.clone()).unwrap_or_else(|e| println!("{:?}", e))
});
fut.await;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_check_needle {
(
$(
$test_name:ident => {
needle = $fuzzy_needle:expr,
accept = $fuzzy_accept:expr,
}
)+
) => {
$(
mod $test_name {
use super::*;
#[test]
fn test_eq() {
assert_eq!(check_needle($fuzzy_needle), $fuzzy_accept);
}
}
)+
}
}
test_check_needle! {
test_parse_alphanumeric => {
needle = "F00bar",
accept = true,
}
test_parse_dashes => {
needle = "foo_bar-baz",
accept = true,
}
test_parse_forward_slash => {
needle = "foo/bar",
accept = true,
}
test_parse_period => {
needle = "foo.cmx",
accept = true,
}
test_parse_false => {
needle = "foo bar",
accept = false,
}
test_parse_url => {
// colon is invalid character
needle = "fuchsia-pkg://",
accept = false,
}
test_parse_bang => {
needle = "hello!",
accept = false,
}
test_parse_shebang => {
needle = "#!/etc/bin",
accept = false,
}
test_parse_hash => {
needle = "package#foo.cmx",
accept = false,
}
}
}