Skip to content

Commit

Permalink
feat: add a .static built-in command to facilitate static file serving (
Browse files Browse the repository at this point in the history
#6)

### Serving Static Files

You can serve static files from a directory using the `.static` command. This
command takes two arguments: the root directory path and the request path.

When you call `.static`, it sets the response to serve the specified file, and
any subsequent output in the closure will be ignored. The content type is
automatically inferred based on the file extension (e.g., `text/css` for `.css`
files).

Here's an example:

```bash
$ http-nu :3001 '{|req| .static "/path/to/static/dir" $req.path}'
```
  • Loading branch information
cablehead authored Feb 20, 2025
1 parent dae7db1 commit 8c4d791
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 136 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
store
27 changes: 27 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 @@ -27,6 +27,7 @@ hyper = { version = "1", features = ["full"] }
hyper-util = { version = "0.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1"
tokio-util = { version = "0.7", features = ["io"] }

bytes = "1.6.0"
url = "2.5.0"
Expand All @@ -41,6 +42,7 @@ nu-protocol = "0.101.0"
tokio-rustls = "0.26.1"
rustls = "0.23.23"
rustls-pemfile = "2.2.0"
hyper-staticfile = "0.10.1"

[dev-dependencies]
tokio-test = "0.4"
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ $ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -no
$ cat cert.pem key.pem > combined.pem
```

### Serving Static Files

You can serve static files from a directory using the `.static` command. This
command takes two arguments: the root directory path and the request path.

When you call `.static`, it sets the response to serve the specified file, and
any subsequent output in the closure will be ignored. The content type is
automatically inferred based on the file extension (e.g., `text/css` for `.css`
files).

Here's an example:

```bash
$ http-nu :3001 '{|req| .static "/path/to/static/dir" $req.path}'
```

### POST: echo

```bash
Expand Down
56 changes: 7 additions & 49 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use nu_protocol::format_shell_error;
use nu_protocol::{
debugger::WithoutDebug,
engine::{Closure, EngineState, Redirection, Stack, StateWorkingSet},
OutDest, PipelineData, Record, ShellError, Span, Value,
OutDest, PipelineData, ShellError, Span, Value,
};

use crate::{Error, Request};
use crate::Error;

#[derive(Clone)]
pub struct Engine {
Expand Down Expand Up @@ -115,65 +115,23 @@ impl Engine {
Ok(())
}

pub fn eval(&self, request: Request, input: PipelineData) -> Result<PipelineData, Error> {
pub fn eval(&self, input: Value, pipeline_data: PipelineData) -> Result<PipelineData, Error> {
let closure = self.closure.as_ref().ok_or("Closure not parsed")?;

let mut stack = Stack::new();
// we need to push a redirection to ensure that the output of the closure is captured
let mut stack =
stack.push_redirection(Some(Redirection::Pipe(OutDest::PipeSeparate)), None);
let block = self.state.get_block(closure.block_id);

stack.add_var(
block.signature.required_positional[0].var_id.unwrap(),
request_to_value(&request, Span::unknown()),
input,
);

eval_block_with_early_return::<WithoutDebug>(&self.state, &mut stack, block, input).map_err(
|err| {
eval_block_with_early_return::<WithoutDebug>(&self.state, &mut stack, block, pipeline_data)
.map_err(|err| {
let working_set = StateWorkingSet::new(&self.state);
Error::from(format_shell_error(&working_set, &err))
},
)
}
}

pub fn request_to_value(request: &Request, span: Span) -> Value {
let mut record = Record::new();

record.push("proto", Value::string(request.proto.clone(), span));
record.push("method", Value::string(request.method.to_string(), span));
record.push("uri", Value::string(request.uri.to_string(), span));
record.push("path", Value::string(request.path.clone(), span));

if let Some(authority) = &request.authority {
record.push("authority", Value::string(authority.clone(), span));
}

if let Some(remote_ip) = &request.remote_ip {
record.push("remote_ip", Value::string(remote_ip.to_string(), span));
})
}

if let Some(remote_port) = &request.remote_port {
record.push("remote_port", Value::int(*remote_port as i64, span));
}

// Convert headers to a record
let mut headers_record = Record::new();
for (key, value) in request.headers.iter() {
headers_record.push(
key.to_string(),
Value::string(value.to_str().unwrap_or_default().to_string(), span),
);
}
record.push("headers", Value::record(headers_record, span));

// Convert query parameters to a record
let mut query_record = Record::new();
for (key, value) in &request.query {
query_record.push(key.clone(), Value::string(value.clone(), span));
}
record.push("query", Value::record(query_record, span));

Value::record(record, span)
}
Loading

0 comments on commit 8c4d791

Please sign in to comment.