Skip to content

Commit

Permalink
feat: use more descriptive types for enabling features on serve
Browse files Browse the repository at this point in the history
  • Loading branch information
ereslibre committed Aug 28, 2023
1 parent 8b8c333 commit ce60065
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 97 deletions.
10 changes: 7 additions & 3 deletions crates/api-manage/src/handlers/v0/workers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use actix_web::{
web::{Data, Json, Path},
HttpResponse, Responder, Result,
};
use wws_router::Routes;
use wws_router::{Routes, WORKERS};

/// Return the list of loaded workers.
#[utoipa::path(
Expand All @@ -34,11 +34,15 @@ pub async fn handle_api_workers(routes: Data<Routes>) -> Result<impl Responder>
)]
#[get("/_api/v0/workers/{id}")]
pub async fn handle_api_worker(routes: Data<Routes>, path: Path<String>) -> HttpResponse {
let workers = WORKERS
.read()
.expect("error locking worker lock for reading");
let worker = routes
.routes
.iter()
.find(|r| &r.worker.id == path.as_ref())
.map(|r| &r.worker);
.find(|r| &r.worker == path.as_ref())
.map(|r| workers.get(&r.worker))
.expect("unexpected missing worker");

if let Some(worker) = worker {
HttpResponse::Ok().json(WorkerConfig::from(worker))
Expand Down
14 changes: 11 additions & 3 deletions crates/api-manage/src/models/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use serde::Serialize;
use utoipa::ToSchema;
use wws_router::Route;
use wws_router::{Route, WORKERS};

#[derive(Serialize, ToSchema)]
/// Defines a worker in a given application.
Expand All @@ -23,10 +23,18 @@ pub struct Worker {

impl From<&Route> for Worker {
fn from(value: &Route) -> Self {
let name = value.worker.config.name.as_ref();
let workers = WORKERS
.read()
.expect("error locking worker lock for reading");
let name = workers
.get(&value.worker)
.expect("unexpected missing worker")
.config
.name
.as_ref();

Self {
id: value.worker.id.clone(),
id: value.worker.clone(),
name: name.unwrap_or(&String::from("default")).to_string(),
path: value.path.clone(),
filepath: value.handler.to_string_lossy().to_string(),
Expand Down
7 changes: 6 additions & 1 deletion crates/router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use std::path::{Path, PathBuf};
use std::time::Instant;
use wws_config::Config;

pub use route::Route;
pub use route::{Route, WORKERS};

/// Contains all registered routes
#[derive(Clone, Default)]
pub struct Routes {
pub routes: Vec<Route>,
pub prefix: String,
Expand Down Expand Up @@ -54,6 +55,10 @@ impl Routes {
Self { routes, prefix }
}

pub fn iter(&self) -> impl Iterator<Item = &Route> {
self.routes.iter()
}

/// Based on a set of routes and a given path, it provides the best
/// match based on the parametrized URL score. See the [`Route::can_manage_path`]
/// method to understand how to calculate the score.
Expand Down
17 changes: 14 additions & 3 deletions crates/router/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
use lazy_static::lazy_static;
use regex::Regex;
use std::{
collections::HashMap,
ffi::OsStr,
path::{Component, Path, PathBuf},
sync::RwLock,
};
use wws_config::Config as ProjectConfig;
use wws_worker::Worker;

lazy_static! {
static ref PARAMETER_REGEX: Regex = Regex::new(r"\[\w+\]").unwrap();
static ref DYNAMIC_ROUTE_REGEX: Regex = Regex::new(r".*\[\w+\].*").unwrap();
pub static ref WORKERS: RwLock<HashMap<String, Worker>> = RwLock::new(HashMap::default());
}

/// Identify if a route can manage a certain URL and generates
Expand All @@ -37,13 +40,14 @@ pub enum RouteAffinity {
/// api/index.wasm => /api
/// api/v2/ping.wasm => /api/v2/ping
/// ```
#[derive(Clone)]
pub struct Route {
/// The wasm module that will manage the route
pub handler: PathBuf,
/// The URL path
pub path: String,
/// The associated worker
pub worker: Worker,
pub worker: String,
}

impl Route {
Expand All @@ -57,12 +61,19 @@ impl Route {
prefix: &str,
project_config: &ProjectConfig,
) -> Self {
let worker = Worker::new(base_path, &filepath, project_config).unwrap();
let worker =
Worker::new(base_path, &filepath, project_config).expect("error creating worker");
let worker_id = worker.id.clone();

WORKERS
.write()
.expect("error locking worker lock for writing")
.insert(worker_id.clone(), worker);

Self {
path: Self::retrieve_route(base_path, &filepath, prefix),
handler: filepath,
worker,
worker: worker_id.clone(),
}
}

Expand Down
11 changes: 6 additions & 5 deletions crates/server/src/handlers/assets.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::AppData;
use actix_files::NamedFile;
use actix_web::{web::Data, HttpRequest};
use std::{
io::{Error, ErrorKind},
path::PathBuf,
};
use std::io::{Error, ErrorKind};

/// Find a static HTML file in the `public` folder. This function is used
/// when there's no direct file to be served. It will look for certain patterns
/// like "public/{uri}/index.html" and "public/{uri}.html".
///
/// If no file is present, it will try to get a default "public/404.html"
pub async fn handle_assets(req: &HttpRequest) -> Result<NamedFile, Error> {
let root_path = req.app_data::<Data<PathBuf>>().unwrap();
let root_path = &req
.app_data::<Data<AppData>>()
.expect("error fetching app data")
.root_path;
let uri_path = req.path();

// File path. This is required for the wasm_handler as dynamic routes may capture static files
Expand Down
7 changes: 5 additions & 2 deletions crates/server/src/handlers/not_found.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::AppData;
use actix_files::NamedFile;
use actix_web::{web::Data, HttpRequest, HttpResponse};
use std::path::PathBuf;

/// This method tries to render a custom 404 error file from the static
/// folder. If not, it will render an empty 404
pub async fn handle_not_found(req: &HttpRequest) -> HttpResponse {
let root_path = req.app_data::<Data<PathBuf>>().unwrap();
let root_path = &req
.app_data::<Data<AppData>>()
.expect("error fetching app data")
.root_path;
let public_404_path = root_path.join("public").join("404.html");

if let Ok(file) = NamedFile::open_async(public_404_path).await {
Expand Down
63 changes: 34 additions & 29 deletions crates/server/src/handlers/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// SPDX-License-Identifier: Apache-2.0

use super::{assets::handle_assets, not_found::handle_not_found};
use crate::DataConnectors;
use crate::AppData;
use actix_web::{
http::StatusCode,
web::{Bytes, Data},
HttpRequest, HttpResponse,
};
use std::{fs::File, io::Write, sync::RwLock};
use wws_router::Routes;
use std::io::Write;
use wws_router::WORKERS;
use wws_worker::io::WasmOutput;

/// Process an HTTP request by passing it to the right Runner. The Runner
Expand All @@ -30,18 +30,13 @@ use wws_worker::io::WasmOutput;
/// For these reasons, we are selecting the right handler at this point and not
/// allowing Actix to select it for us.
pub async fn handle_worker(req: HttpRequest, body: Bytes) -> HttpResponse {
let routes = req.app_data::<Data<Routes>>().unwrap();
let stderr_file = req.app_data::<Data<Option<File>>>().unwrap();
let data_connectors = req
.app_data::<Data<RwLock<DataConnectors>>>()
.unwrap()
.clone();
// We will improve error handling
let app_data = req
.app_data::<Data<AppData>>()
.expect("error fetching app data");
let result: HttpResponse;

// First, we need to identify the best suited route
let selected_route = routes.retrieve_best_route(req.path());

let selected_route = app_data.routes.retrieve_best_route(req.path());
if let Some(route) = selected_route {
// First, check if there's an existing static file. Static assets have more priority
// than dynamic routes. However, I cannot set the static assets as the first service
Expand All @@ -52,38 +47,47 @@ pub async fn handle_worker(req: HttpRequest, body: Bytes) -> HttpResponse {
}
}

let workers = WORKERS
.read()
.expect("error locking worker lock for reading");

let worker = workers
.get(&route.worker)
.expect("unexpected missing worker");

// Let's continue
let body_str = String::from_utf8(body.to_vec()).unwrap_or_else(|_| String::from(""));

// Init from configuration
let vars = &route.worker.config.vars;
let kv_namespace = route.worker.config.data_kv_namespace();
let vars = &worker.config.vars;
let kv_namespace = worker.config.data_kv_namespace();

let store = match &kv_namespace {
Some(namespace) => {
let connector = data_connectors.read().unwrap();
let connector = app_data
.data_connectors
.read()
.expect("error locking data connectors lock for reading");
let kv_store = connector.kv.find_store(namespace);

kv_store.map(|store| store.clone())
}
None => None,
};

let stderr_file = app_data
.stderr
.as_ref()
.map(|file| file.try_clone().expect("error setting up stderr"));

let (handler_result, handler_success) =
match route
.worker
.run(&req, &body_str, store, vars, stderr_file.get_ref())
{
match worker.run(&req, &body_str, store, vars, &stderr_file) {
Ok(output) => (output, true),
Err(error) => {
if let Some(stderr_file) = stderr_file.get_ref() {
if let Ok(mut stderr_file) = stderr_file.try_clone() {
stderr_file
.write_all(error.to_string().as_bytes())
.expect("Failed to write error to stderr_file");
} else {
eprintln!("{}", error);
}
if let Some(mut stderr_file) = stderr_file {
stderr_file
.write_all(error.to_string().as_bytes())
.expect("Failed to write error to stderr_file");
} else {
eprintln!("{}", error);
}
Expand All @@ -105,9 +109,10 @@ pub async fn handle_worker(req: HttpRequest, body: Bytes) -> HttpResponse {

// Write to the state if required
if handler_success && kv_namespace.is_some() {
data_connectors
app_data
.data_connectors
.write()
.unwrap()
.expect("error locking data connectors lock for writing")
.kv
.replace_store(&kv_namespace.unwrap(), &handler_result.kv)
}
Expand Down
Loading

0 comments on commit ce60065

Please sign in to comment.