Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: read static files from the public folder #29

Merged
merged 5 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ wasmtime-wasi = "2.0.2"
anyhow = "1.0.66"
wasi-common = "2.0.2"
actix-web = "4"
actix-files = "0.6.2"
lazy_static = "1.4.0"
env_logger = "0.9.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ docker run -v /path/to/handlers/on/host:/app -p 8080:8080 \

## Other usages

Wasm Workers Server is stateless as far as the loaded handers are stateless (i.e. when they don't use the [Key / Value store](../features/key-value.md)). This makes the image very useful if you want to setup your own auto-scaling deployment.
Wasm Workers Server is stateless as far as the loaded handers are stateless (i.e. when they don't use the [Key / Value store](./features/key-value.md)). This makes the image very useful if you want to setup your own auto-scaling deployment.

2 changes: 1 addition & 1 deletion docs/docs/features/key-value.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 1
---

# Key / Value Store
Expand Down
47 changes: 47 additions & 0 deletions docs/docs/features/static-assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
sidebar_position: 2
---

# Static assets

Wasm Workers Server allows you to serve any static asset required by your workers. For that, place any static asset in a `public` folder. It must be present in the root of the directory you're serving with `wws`.

Note that all static assets are mounted on `/`, so `public` won't be present in their paths.

For example, given the following folder structure:

```bash
.
├── index.js
└── public
└── images
└── intro.jpg
└── robots.txt
```

If you run `wws` in this folder, it will serve the following resources:

* `/`: the response will be generated by the `index.js` worker
* `/robots.txt`: the static `robots.txt` file
* `/images/intro.jpg`: the static `intro.jpg` file

## HTML files in public folder

HTML files are supported inside the `public` folder. `wws` will serve those without requiring the `html` extension.

For example, for the given folder structure:

```bash
.
├── index.js
└── public
└── images
└── intro.jpg
└── about.html
```

The `about.html` file will be served as `/about`.

## Index file in public

An `index.html` can be added to the `public` folder and it will be mounted in `/`. Note that workers have priority, so if there's an `/index.js` and `/public/index.html` files, the `/` route will be served by the former.
70 changes: 69 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
extern crate lazy_static;

mod config;
mod data;
mod router;
mod runner;

use actix_files::{Files, NamedFile};
use actix_web::dev::{fn_service, ServiceRequest, ServiceResponse};
use actix_web::{
http::StatusCode,
middleware,
Expand All @@ -15,8 +20,18 @@ use actix_web::{
use clap::Parser;
use data::kv::KV;
use runner::WasmOutput;
use std::path::Path;
use std::path::PathBuf;
use std::{collections::HashMap, sync::RwLock};
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};

// Provide a static root_path so it can be used in the default_handler to manage
// static assets.
lazy_static! {
static ref ROOT_PATH: Arc<RwLock<PathBuf>> = Arc::new(RwLock::new(PathBuf::new()));
}

// Arguments
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -120,6 +135,14 @@ async fn debug(req: HttpRequest) -> impl Responder {
async fn main() -> std::io::Result<()> {
let args = Args::parse();

// Initialize root path so it can be used later in the default_handler
// for static assets. I have to do it in a clojure so the root_path
// reference is drop and the RwLock is released automatically.
{
let mut root_path = ROOT_PATH.write().unwrap();
*root_path = args.path.clone();
}

std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();

Expand Down Expand Up @@ -169,6 +192,51 @@ async fn main() -> std::io::Result<()> {
}
}

// Serve static files from the static folder
app = app.service(
Files::new("/", &args.path.join("public"))
.index_file("index.html")
// This handler check if there's an HTML file in the public folder that
// can reply to the given request. For example, if someone request /about,
// this handler will look for a /public/about.html file.
.default_handler(fn_service(|req: ServiceRequest| async {
let (req, _) = req.into_parts();
// Avoid dots in the URI. If they are present, the extension
// was passed so the file should be properly rendered.
let uri_path = req.path().replace(".", "");
let file;

let rw_path = ROOT_PATH.read().unwrap();
let base_path = rw_path.as_os_str();

// Possible paths
let index_folder_path =
Path::new(base_path).join(format!("public{}/index.html", uri_path));
let html_ext_path =
Path::new(base_path).join(format!("public{}.html", uri_path));
let public_404_path = Path::new(base_path).join("public").join("404.html");

if uri_path.ends_with("/") && index_folder_path.exists() {
file = NamedFile::open_async(index_folder_path).await;
} else if !uri_path.ends_with("/") && html_ext_path.exists() {
file = NamedFile::open_async(html_ext_path).await;
} else {
file = NamedFile::open_async(public_404_path).await;
}
Angelmmiguel marked this conversation as resolved.
Show resolved Hide resolved

match file {
Ok(existing_file) => {
let res = existing_file.into_response(&req);
Ok(ServiceResponse::new(req, res))
}
Err(_) => {
let mut res = HttpResponse::NotFound();
Ok(ServiceResponse::new(req, res.body("")))
}
}
})),
);

app
})
.bind(format!("{}:{}", args.hostname.as_str(), args.port))?;
Expand Down