Skip to content

Commit

Permalink
Merge pull request #25 from w-henderson/book
Browse files Browse the repository at this point in the history
Write a user guide for all of Core, Server, WS and Auth
  • Loading branch information
w-henderson authored Jan 20, 2022
2 parents d4e0931 + be97df8 commit 90c78bf
Show file tree
Hide file tree
Showing 31 changed files with 1,159 additions and 370 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pkg/
bench/
database.jdb
keys/
docs/book/

*.conf
!/humphrey-server/src/tests/testcases/*.conf
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

<p align="center">
A Performance-Focused, Dependency-Free Web Server.<br>
<a href="#"><strong>Getting Started »</strong></a><br><br>
<a href="https://github.com/w-henderson/Humphrey/blob/master/humphrey/README.md">Core Quickstart</a> ·
<a href="https://github.com/w-henderson/Humphrey/blob/master/humphrey-server/README.md">Server Quickstart</a> ·
<a href="https://github.com/w-henderson/Humphrey/blob/master/humphrey-ws/README.md">WebSocket Quickstart</a> ·
<a href="https://github.com/w-henderson/Humphrey/blob/master/humphrey-auth/README.md">Auth Quickstart</a><br>
<a href=https://humphrey.whenderson.dev"><strong>Getting Started »</strong></a><br><br>
<a href="https://humphrey.whenderson.dev/core/index.html">Core Guide</a> ·
<a href="https://humphrey.whenderson.dev/server/index.html">Server Guide</a> ·
<a href="https://humphrey.whenderson.dev/websocket/index.html">WebSocket Guide</a> ·
<a href="https://humphrey.whenderson.dev/auth/index.html">Auth Guide</a><br>
<a href="https://docs.rs/humphrey">Core API Reference</a> ·
<a href="https://docs.rs/humphrey-server">Server API Reference</a> ·
<a href="https://docs.rs/humphrey-ws">WebSocket API Reference</a> ·
Expand All @@ -29,9 +29,9 @@

Humphrey is a very fast, robust and flexible HTTP/1.1 web server, with support for static and dynamic content through its Rust crate and plugin system. It has no dependencies when only using default features, and the binary is easily extensible with a flexible configuration file and dynamically-loaded plugins. It also provides a WebSocket API for the easy integration of WebSockets into your application, and a simple authentication system for authenticating users and managing sessions.

## Contents
- [Use Humphrey as a crate](humphrey/README.md)
- [Use Humphrey as a standalone application](humphrey-server/README.md)
- [Use WebSockets with Humphrey](humphrey-ws/README.md)
- [Use authentication with Humphrey](humphrey-auth/README.md)
- [Use HTTPS/TLS with Humphrey](humphrey/TLS.md)
## Quick Links
- [Use Humphrey as a crate](https://humphrey.whenderson.dev/core/index.html)
- [Use Humphrey as a standalone application](https://humphrey.whenderson.dev/server/index.html)
- [Use WebSockets with Humphrey](https://humphrey.whenderson.dev/websocket/index.html)
- [Use authentication with Humphrey](https://humphrey.whenderson.dev/auth/index.html)
- Use HTTPS/TLS with Humphrey [Core](https://humphrey.whenderson.dev/core/https.html)/[Server](https://humphrey.whenderson.dev/server/https.html)
6 changes: 6 additions & 0 deletions docs/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[book]
authors = ["William Henderson"]
language = "en"
multilingual = false
src = "src"
title = "Humphrey"
18 changes: 18 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Summary

- [Introduction](introduction.md)
- [Humphrey Core](core/index.md)
- [Getting Started](core/getting-started.md)
- [Using State](core/state.md)
- [Static Content](core/static-content.md)
- [Using HTTPS](core/https.md)
- [Humphrey Server](server/index.md)
- [Getting Started](server/getting-started.md)
- [Configuration](server/configuration.md)
- [Using PHP](server/using-php.md)
- [Using HTTPS](server/https.md)
- [Creating a Plugin](server/creating-a-plugin.md)
- [Humphrey WebSocket](websocket/index.md)
- [Getting Started](websocket/getting-started.md)
- [Broadcasting Messages](websocket/broadcasting-messages.md)
- [Humphrey Auth](auth/index.md)
9 changes: 9 additions & 0 deletions docs/src/auth/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Humphrey Auth
Humphrey Auth is simple authentication crate for Humphrey applications. It provides a simple and database-agnostic way to authenticate users and handle sessions and tokens. It does depend upon the [`argon2`](https://docs.rs/argon2), [`uuid`](https://docs.rs/uuid) and [`rand_core`](https://docs.rs/rand_core) crates to ensure that it is secure.

Humphrey Auth needs to be integrated into a full-stack Humphrey application with endpoints for all the authentication-related methods, such as signing in and out. Therefore, this guide does not provide step-by-step instructions on how to use it.

It is easiest to learn how to use Humphrey Auth from the [full example](https://github.com/w-henderson/Humphrey/blob/master/examples/auth/src/main.rs). Alongside this, it may be useful to refer to the [API reference](https://docs.rs/humphrey_auth) for more information.

### Note for Contributors
If you would like to add a step-by-step guide for Humphrey Auth, please [open an issue](https://github.com/w-henderson/Humphrey/issues/new). Your help would be greatly appreciated!
132 changes: 132 additions & 0 deletions docs/src/core/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<script>
window.setInterval(() => {
document.querySelector("#datetime-example").innerHTML = new Date().toUTCString();
}, 1000);
</script>

# Getting Started
This chapter will walk you through the steps to get started with Humphrey Core. It assumes that you already have Rust and Cargo installed, but installation steps for these can be found in the [Rust book](https://doc.rust-lang.org/book/ch01-01-installation.html).

## Creating a New Project
A Humphrey Core web application is a Rust binary crate, so to begin we'll need to create a new project using Cargo.

```bash
$ cargo new my-app
```

Next, we need to add the `humphrey` crate as a dependency of our project, which can be done by editing the `Cargo.toml` file within the project directory as follows. Please note that it is not good practice to use the `*` version number for a real project, as if a new version of Humphrey adds breaking changes, this could cause your application to stop working properly. In that case, you should check the latest Humphrey version on [crates.io](https://crates.io/crates/humphrey) and use that version number.

```toml
[dependencies]
humphrey = "*"
```

With that, our project will compile and run, but at the moment it doesn't do anything.

## Creating a Humphrey App
We can now initialise a new Humphrey application in the `main.rs` file of the `src` directory.

```rs
use humphrey::http::{Response, StatusCode};
use humphrey::App;

fn main() {
let app: App = App::new().with_stateless_route("/", |_| {
Response::new(StatusCode::OK, "Hello, Humphrey!")
});

app.run("0.0.0.0:80").unwrap();
}
```

If we now run `cargo run`, our application will successfully compile and start the server, which you can access at [http://localhost](http://localhost). You should see the text "Hello, Humphrey!" in your browser. If so, congratulations - you've successfully created a Humphrey web application! Let's go into a little more detail about what this code actually does.

First, we create a new `App` instance, which is the core of every Humphrey application. We need to specify the type `App` as well, since the app is generic over a state type, which we'll cover in the [Using State](state.md) chapter. This shouldn't be necessary since the default state type is the Rust empty type `()`, but it must be done due to current technical limitations of Rust (see [rust-lang/rust issue #36887](https://github.com/rust-lang/rust/issues/36887)).

We then call `with_stateless_route` on the `App` instance, passing in the path of the route and a closure that will be called when the route is matched. The closure takes one argument, the request, which we ignore with the `_`. It returns a `Response` object with the success status code (200), containing the text "Hello, Humphrey!".

Finally, we call `run` on the `App` instance, passing in the address and port to listen on. This will start the server and block the main thread until the server is shut down.

## Adding Multiple Routes
At the moment, our app only shows a message for the root path, but we can add more routes by calling `with_stateless_route` again with different handlers. In most cases, these would not be passed in as closures, but rather as functions that return a `Response` object. Let's add another route called `/api/time` that shows the current time.

We'll start by creating a handler function and adding it to the app by calling `with_stateless_route` again. We'll also move the root handler to a function as well to improve the code readability. We also need to import the `Arc` type.

```rs
use humphrey::http::{Request, Response, StatusCode};
use humphrey::App;
use std::sync::Arc;

fn main() {
let app: App = App::new()
.with_stateless_route("/", root_handler)
.with_stateless_route("/api/time", time_handler);

app.run("0.0.0.0:80").unwrap();
}

fn root_handler(_: Request) -> Response {
Response::new(StatusCode::OK, "Hello, Humphrey!")
}

fn time_handler(_: Request) -> Response {
// todo: get the current time
}
```

This code won't compile, as the `time_handler` function does not yet return a `Response` object. Let's use the built-in `DateTime` type from `humphrey::http::date` to get the current time in the HTTP date format, which looks like <code class="hljs" id="datetime-example"><script>document.write(new Date().toUTCString());</script></code>.

```rs
use humphrey::http::date::DateTime;

// --snip--

fn time_handler(_: Request) -> Response {
let time = DateTime::now();
let time_http = time.to_string();

Response::new(StatusCode::OK, time_http)
}
```

If we now run `cargo run` again, and go to [http://localhost/api/time](http://localhost/api/time) in the browser, we should see the current time in the format described earlier.

## Wildcard Routes
Humphrey supports the `*` wildcard character in route paths, so one handler can handle many paths. Let's add another route called `/api/greeting/*` which will greet the user with the name they provide. Again, we'll need to create another route handler function and add it to the app:

```rs
// --snip--

fn main() {
let app: App = App::new()
.with_stateless_route("/", root_handler)
.with_stateless_route("/api/time", time_handler);
.with_stateless_route("/api/greeting/*", greeting_handler);

app.run("0.0.0.0:80").unwrap();
}

// --snip--

fn greeting_handler(request: Request) -> Response {
// todo: greet the user
}
```

In our newly-created greeting handler, we want to extract the name from the path and return a response depending on the name provided. We can do that with the Rust standard library's `strip_prefix` function for strings. You'll notice that we haven't ignored the request argument as we did previously, and this is so we can access the path of the request.

```rs
// --snip--

fn greeting_handler(request: Request) -> Response {
let name = request.uri.strip_prefix("/api/greeting/").unwrap();
let greeting = format!("Hello, {}!", name);

Response::new(StatusCode::OK, greeting)
}
```

If we now visit [http://localhost/api/greeting/Humphrey](http://localhost/api/greeting/Humphrey) in the browser, we should see the text "Hello, Humphrey!". You can replace the name Humphrey with your own name or any other name you want, and you should see the greeting change accordingly.

## Conclusion
As you can see, Humphrey provides an intuitive and easy-to-use API to create web applications. Next, let's look at the [Using State](state.md) chapter, which will cover how to safely share state between routes and requests.
93 changes: 93 additions & 0 deletions docs/src/core/https.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Using HTTPS
This chapter explains how we can access our Humphrey application using HTTPS, improving the security of the application and allowing our client-side code access to more advanced browser features.

**Note:** While Humphrey's core features are dependency-free, using TLS requires the `rustls` crate to ensure that the cryptography used is secure.

## Enabling the TLS Feature
To use HTTPS with Humphrey, you must first enable the `tls` feature in your `Cargo.toml` file:

```toml
[dependencies]
humphrey = { version = "*", features = ["tls"] }
```

## Setting up the TLS Certificate
TLS requires a certificate and a private key, which must be supplied to the Humphrey app. In production, these would be generated by a certificate authority like [Let's Encrypt](https://letsencrypt.org/), but when developing an HTTPS application, it's often easier to use a self-signed certificate.

The [`mkcert`](https://github.com/FiloSottile/mkcert) command-line tool can be used to generate a trusted certificate for local development.


### Installing `mkcert`
`mkcert` can be installed as follows (or downloaded from the aforementioned link):

**Windows:**
```sh
$ choco install mkcert
```

**MacOS:**
```sh
$ brew install mkcert
```

**Linux:**
```sh
$ sudo apt install libnss3-tools
$ brew install mkcert
```

### Generating the Certificate
Once installed, the `mkcert` certificate authority must be trusted by the operating system, which can be done by running the following command.

```sh
$ mkcert -install
```

Finally, to generate a certificate for local development, run this command, which will create two files, `localhost.pem` and `localhost-key.pem`.

```sh
$ mkcert localhost
```

## Using the Certificate with Humphrey
When creating your Humphrey application, the certificate and key must be provided to the `App` using the `with_cert` method, which is only available when the `tls` feature is enabled. A very simple example using TLS is shown below. Notice that we use `run_tls` instead of `run` to start the application.

```rs
use humphrey::http::{Request, Response, StatusCode};
use humphrey::App;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
let app: App<()> = App::new()
.with_stateless_route("/", home)
.with_cert("path/to/localhost.pem", "path/to/localhost-key.pem");

app.run_tls("0.0.0.0:443")?;

Ok(())
}

fn home(_: Request) -> Response {
Response::new(
StatusCode::OK,
"<html><body><h1>This is served over HTTPS!</h1></body></html>",
)
}
```

## Forcing HTTPS
By default, when you call `app.run_tls("0.0.0.0:443")`, the application will only accept connections on the HTTPS port (443). Typically, web applications will automatically redirect requests the HTTP port 80 to the HTTPS endpoint. To enable this in Humphrey, you can use the `with_forced_https` method on the `App` struct, as follows:

```rs
// --snip--
let app: App<()> = App::new()
.with_stateless_route("/", home)
.with_cert("path/to/localhost.pem", "path/to/localhost-key.pem")
.with_forced_https(true);
// --snip--
```

This starts a background thread which simply redirects HTTP requests to the corresponding HTTPS URL.

## Conclusion
In this section, we've covered how to use the TLS feature of Humphrey, and how to use it to serve HTTPS applications. If you want to learn more about Humphrey, consider exploring the [API reference](https://docs.rs/humphrey) or reading the [WebSocket guide](../websocket/index.md)
10 changes: 10 additions & 0 deletions docs/src/core/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Humphrey Core
Humphrey Core is a high-performance web server crate which allows you to develop web applications in Rust. With no dependencies by default, it is very quick to compile and produces very small binaries, as well as being very resource-efficient.

This section of the guide will cover the following topics:
1. [Creating and running a basic Humphrey Core web application](getting-started.md)
2. [Handling state between requests](state.md)
3. [Integrating static and dynamic content](static-content.md)
4. [Serving applications over HTTPS](https.md)

It's recommended that you have basic familiarity with Rust before reading this section, as only Humphrey-specific concepts are explained, and knowledge of the Rust language is required to understand many of them.
Loading

0 comments on commit 90c78bf

Please sign in to comment.