-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from w-henderson/book
Write a user guide for all of Core, Server, WS and Auth
- Loading branch information
Showing
31 changed files
with
1,159 additions
and
370 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ pkg/ | |
bench/ | ||
database.jdb | ||
keys/ | ||
docs/book/ | ||
|
||
*.conf | ||
!/humphrey-server/src/tests/testcases/*.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.