Browse Source

gempress: add API documentation, readme

main
Garrit Franke 3 years ago
parent
commit
3c02a1f070
Signed by: garrit
GPG Key ID: 65586C4DDA55EA2C
  1. 46
      lib/gempress/README.md
  2. 2
      lib/gempress/examples/simple_server.rs
  3. 80
      lib/gempress/src/lib.rs
  4. 2
      src/main.rs

46
lib/gempress/README.md

@ -0,0 +1,46 @@
# Gempress
An Express.js inspired server framework for the [Gemini
protocol](https://gemini.circumlunar.space/).
The goal of Gempress is to provide a minimal yet powerful API to build dynamic
gemini applications.
## A simple server
Setting up a Gempress server with a single route looks like this:
```rs
fn index_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
res.send("Hello from index route!".as_bytes());
}
fn main() {
let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
let mut app = Gempress::new(config);
app.on("/", &index_handler);
app.listen(1965, || {
println!("Listening on port 1965");
})
.unwrap();
}
```
See the [examples](./examples) directory for more elaborate examples.
## Generating a self-signed certificate
To use a Gempress server, you will need a TLS certificate bundled in an
identity. To generate a certificate, simply execute the following snippet.
Substitute the `localhost` with your own hostname to generate a certificate
exposed to the public.
```sh
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem
# Cleanup old files
rm key.pem cert.pem
```

2
lib/gempress/examples/simple_server.rs

@ -10,7 +10,7 @@ fn index_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
fn main() {
// Run make_cert.sh to generate a certificate
let config = gempress::Config::with_identity(PathBuf::from("identity.pfx"), "qqqq".into());
let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
let mut app = Gempress::new(config);

80
lib/gempress/src/lib.rs

@ -14,6 +14,7 @@ use std::sync::Arc;
use error::{GempressError, GempressResult};
use native_tls::{TlsAcceptor, TlsStream};
/// Configuration for a Gempress server.
#[derive(Clone, Debug)]
pub struct Config {
// Path to the identity file
@ -22,7 +23,26 @@ pub struct Config {
}
impl Config {
pub fn with_identity(identityPath: PathBuf, password: String) -> Self {
/// Create a new Gempress config by loading a TLS certificate at the given file path.
///
/// To generate a self-signed certificate, you can execute the following commands (substitute
/// `localhost` with your hostname, if applicable):
///
/// ```
/// openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
/// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem
///
/// rm key.pem cert.pem # Cleanup
/// ```
///
///
/// # Examples
///
/// ```
/// let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
/// let mut app = Gempress::new(config);
/// ```
pub fn from_identity(identityPath: PathBuf, password: String) -> Self {
Self {
identityPath,
password,
@ -30,7 +50,28 @@ impl Config {
}
}
type Handler = dyn Fn(Box<gemini::Request>, Box<gemini::Response>);
/// A function handler
///
/// # Examples
///
/// ```
/// let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
/// let mut app = Gempress::new(config);
///
/// // Define a function handler
/// fn index_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
/// res.send("Hello from index route!".as_bytes());
/// }
///
/// // Apply function handler to path
/// app.on("/foo", &foo_handler);
///
/// app.listen(1965, || {
/// println!("Listening on port 1965");
/// })
/// .unwrap();
/// ```
pub type Handler = dyn Fn(Box<gemini::Request>, Box<gemini::Response>);
struct Layer {
handler: Box<Handler>,
@ -43,12 +84,22 @@ impl std::fmt::Debug for Layer {
}
}
/// A Gempress server
pub struct Gempress {
pub config: Config,
stack: Vec<Layer>,
}
impl Gempress {
/// Create a new Gempress server with the given config
///
/// # Examples
///
/// ```
/// let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
/// let mut app = Gempress::new(config);
/// ```
pub fn new(config: Config) -> Self {
Gempress {
config,
@ -56,6 +107,30 @@ impl Gempress {
}
}
/// Registers a new route handler to a given path
///
/// # Examples
///
/// ```
/// let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "password".into());
/// let mut app = Gempress::new(config);
///
/// fn index_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
/// res.send("Hello from index route!".as_bytes());
/// }
///
/// fn foo_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
/// res.send("This is the /foo route".as_bytes());
/// }
///
/// app.on("/", &index_handler);
/// app.on("/foo", &foo_handler);
///
/// app.listen(1965, || {
/// println!("Listening on port 1965");
/// })
/// .unwrap();
/// ```
pub fn on(&mut self, path: &str, handler: &'static Handler) {
let layer = Layer {
path: path.to_string(),
@ -64,6 +139,7 @@ impl Gempress {
self.stack.push(layer);
}
/// Bind the server to a network port, then execute the callback
pub fn listen<F: Fn()>(self, port: u16, callback: F) -> GempressResult<()> {
// Read certificate
// TODO: Can a password be optional?

2
src/main.rs

@ -5,7 +5,7 @@ use std::path::PathBuf;
fn main() {
// Run make_cert.sh to generate a certificate
let config = gempress::Config::with_identity(PathBuf::from("identity.pfx"), "qqqq".into());
let config = gempress::Config::from_identity(PathBuf::from("identity.pfx"), "qqqq".into());
let app = Gempress::new(config);

Loading…
Cancel
Save