Skip to content

Commit

Permalink
Merge pull request #11 from Basicprogrammer10/1.0.0
Browse files Browse the repository at this point in the history
🥧 Version 1.0.0
  • Loading branch information
connorslade authored Mar 15, 2022
2 parents 9488df9 + 26324ef commit 7fe8406
Show file tree
Hide file tree
Showing 20 changed files with 462 additions and 226 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "afire"
version = "0.4.0"
version = "0.4.1"
authors = ["Connor Slade <connor@connorcode.com>"]
edition = "2018"

repository = "https://github.com/Basicprogrammer10/afire"
documentation = "https://docs.rs/afire"
homepage = "https://connorcode.com/writing/afire"
description = "🔥 A blazing fast web framework for Rust"
keywords = ["http", "WebFramework", "WebServer"]
keywords = ["afire", "http", "WebFramework", "WebServer"]
categories = ["network-programming", "web-programming::http-server"]
exclude = [".github/", "SocialShare.*"]
license = "GPL-3.0"
Expand Down Expand Up @@ -41,6 +41,9 @@ dynamic_resize = []
path_decode_url = []
ignore_trailing_path_slash = []

# Other
tracing = []

[dev-dependencies]
# Enable rate_limit and logging features for examples
afire = { path = ".", features = ["rate_limit", "logging", "serve_static"] }
afire = { path = ".", features = ["rate_limit", "logging", "serve_static", "tracing"] }
11 changes: 10 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# 0.4.1

- Add ThreadPool Back!
- Tracing Feature
- Remove Middleware Interior Mutability by Default
- Make remove_address_port usable without Feature
- Add *end* middleware to run after sending a request
- Make use of end middleware on logger

# 0.4.0

- Make serve static types public
Expand Down Expand Up @@ -32,7 +41,7 @@
- Remove thread pool from project (for now)
- Make Custom Content Type use &str not String
- Make VERSION Public
- Add External Unit Tests
- Add External Unit Testsremove_address_port
- Use the built-in `IpAddr` enum for server Ip
- Remove `.ip_string()` for `.ip.to_string()`
- Add `Response.close()` for closing a stream with no response
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Just add the following to your `Cargo.toml`:

```toml
[dependencies]
afire = "0.4.0"
afire = "0.4.1"
```

## 📄 Info
Expand Down Expand Up @@ -57,7 +57,7 @@ For these you will need to enable the features.
To use these extra features enable them like this:

```toml
afire = { version = "0.4.0", features = ["rate_limit", "logging", "serve_static"] }
afire = { version = "0.4.1", features = ["rate_limit", "logging", "serve_static"] }
```

- Content Types
Expand Down
10 changes: 6 additions & 4 deletions examples/06_error_handling.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::RwLock;

use afire::{Method, Response, Server};

// Don't crash thread from a panic in a route
Expand All @@ -24,16 +26,16 @@ fn main() {
// You can optionally define a custom error handler
// This can be defined anywhere in the server and will take affect for all routes
// Its like a normal route, but it will only be called if the route panics
let errors = std::cell::RefCell::new(0);
let errors = RwLock::new(0);
server.error_handler(move |_req, err| {
errors.replace_with(|&mut x| x + 1);
let mut errors = errors.write().unwrap();
*errors += 1;

Response::new()
.status(500)
.text(format!(
"<h1>Internal Server Error #{}</h1><br>Panicked at '{}'",
errors.borrow(),
err
errors, err
))
.header("Content-Type", "text/html")
});
Expand Down
2 changes: 1 addition & 1 deletion examples/08_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Log;
impl Middleware for Log {
// Redefine the `pre` function
// (Runs before Routes)
fn pre(&mut self, req: Request) -> MiddleRequest {
fn pre(&self, req: Request) -> MiddleRequest {
// Print some info
println!(
"[{}] {} {}",
Expand Down
28 changes: 28 additions & 0 deletions examples/13_threading.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use afire::{Method, Response, Server};

// Create a new basic server like in example 01
// However, we want to use a thread pool to handle the requests
// This is incredibly simple in afire

// In production, you would probably want to use a reverse proxy like nginx
// or something similar to split the load across multiple servers
// But just a thread pool is a good way to get started

fn main() {
// Create a new Server instance on localhost port 8080
let mut server: Server = Server::new("localhost", 8080);

// Define a handler for GET "/"
server.route(Method::GET, "/", |_req| {
Response::new()
.status(200)
.text("Hello from ThreadPool!")
.header("Content-Type", "text/plain")
});

println!("[13] Listening on http://{}:{}", server.ip, server.port);

// Start the server with 8 threads
// This will block the current thread
server.start_threaded(8);
}
56 changes: 1 addition & 55 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,61 +14,7 @@
| 10_logging | Log requests to a file / console |
| 11_rate_limit | Add a rate limit to your server |
| 12_path_params | Use Path Parameters on a route |

## 01 - Basic

Create a basic web server that can serve some static text.

## 02 - Serve File

Read and server binary files from disk.

In the example a html text file is served but this code would work with images, videos, etc.

## 03 - Routing

Learn about routing priority and add a 404 page.

## 04 - Data

Use Query Strings, Path params and HTML forms to send data to the server from a webpage.

## 05 - Headers

Add response headers to the response to redirect to another page or send extra data.

Also read and echo client headers as a response.

## 06 - Error Handling

Learn about afire's automatic route error handling and add your own error handler.

## 07 - Serve Static

Serve all static files from a directory.

Makes use of one of afire's built in extensions

## 08 - Middleware

Learn about Middleware and how to use it to log requests.

## 09 - Cookies

Get cookies from the client and make a HTML table.
Also set new cookies with extra options.

## 10 - Logging

Using afire's built in middleware make and attach a logger to a server.

## 11 - Rate Limit

Add a rate limit to your server with some built in middleware.

## 12 - Path params

Use Path params to send data through a route path.
| 13_threading | Use a thread pool to handle requests |

## To add

Expand Down
42 changes: 42 additions & 0 deletions examples/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::sync::atomic::{AtomicU32, Ordering};

use afire::prelude::*;

struct Log(AtomicU32);

impl Middleware for Log {
fn pre(&self, _req: Request) -> MiddleRequest {
let i = self.0.fetch_add(1, Ordering::Release);
println!("{}", i);

std::thread::sleep(std::time::Duration::from_secs(10));

MiddleRequest::Continue
}

fn end(&self, _req: Request, _res: Response) {
std::thread::sleep(std::time::Duration::from_secs(10));
}
}

impl Log {
fn new() -> Self {
Self(AtomicU32::new(0))
}
}

fn main() {
let mut server: Server = Server::new("localhost", 8818);

Log::new().attach(&mut server);

server.route(Method::GET, "/", |_req| {
Response::new()
.status(200)
.reason("OK!")
.text("Hi :P")
.header("Content-Type", "text/plain")
});

server.start_threaded(4).unwrap();
}
7 changes: 3 additions & 4 deletions lib/extensions/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use std::io::prelude::*;
use std::path::PathBuf;

use crate::common::remove_address_port;
use crate::middleware::{MiddleRequest, Middleware};
use crate::middleware::Middleware;
use crate::Request;
use crate::Response;

/// Define Log Levels
#[derive(Debug)]
Expand Down Expand Up @@ -200,10 +201,8 @@ impl Logger {
}

impl Middleware for Logger {
fn pre(&mut self, req: Request) -> MiddleRequest {
fn end(&self, req: Request, _res: Response) {
self.log(&req);

MiddleRequest::Continue
}
}

Expand Down
38 changes: 22 additions & 16 deletions lib/extensions/ratelimit.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
use std::collections::HashMap;
use std::fmt;
use std::sync::{
atomic::{AtomicU64, Ordering},
Mutex,
};
use std::time::{SystemTime, UNIX_EPOCH};

use crate::common::remove_address_port;
use crate::middleware::{MiddleRequest, Middleware};
use crate::{Request, Response};
use crate::{Content, Request, Response};

// Handler Type
type Handler = Box<dyn Fn(&Request) -> Option<Response>>;
type Handler = Box<dyn Fn(&Request) -> Option<Response> + Send + Sync>;

/// Limit the amount of requests handled by the server.
pub struct RateLimiter {
/// Requests Per Req_Timeout
req_limit: u64,

/// Time of last reset
last_reset: u64,
last_reset: AtomicU64,

/// How often to reset the counters (sec)
req_timeout: u64,

/// Table of requests per IP
requests: HashMap<String, u64>,
requests: Mutex<HashMap<String, u64>>,

/// Handler for when the limit is reached
handler: Handler,
Expand All @@ -33,16 +37,16 @@ impl RateLimiter {
/// Default limit is 10 and timeout is 60
pub fn new() -> RateLimiter {
RateLimiter {
last_reset: 0,
last_reset: AtomicU64::new(0),
req_limit: 10,
req_timeout: 60,
requests: HashMap::new(),
requests: Mutex::new(HashMap::new()),
handler: Box::new(|_| {
Some(
Response::new()
.status(429)
.text("Too Many Requests")
.header("Content-Type", "text/plain"),
.content(Content::TXT),
)
}),
}
Expand Down Expand Up @@ -131,31 +135,33 @@ impl RateLimiter {
}

/// Count a request.
fn add_request(&mut self, ip: String) {
self.requests
.insert(ip.clone(), self.requests.get(&ip).unwrap_or(&0) + 1);
fn add_request(&self, ip: String) {
let mut req = self.requests.lock().unwrap();
let count = req.get(&ip).unwrap_or(&0) + 1;
req.insert(ip, count);
}

/// Check if request table needs to be cleared.
fn check_reset(&mut self) {
fn check_reset(&self) {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if self.last_reset + self.req_timeout <= time {
self.requests = HashMap::new();
self.last_reset = time;

if self.last_reset.load(Ordering::Acquire) + self.req_timeout <= time {
self.requests.lock().unwrap().clear();
self.last_reset.store(time, Ordering::Release);
}
}

/// Check if the request limit has been reached for an ip.
fn is_over_limit(&self, ip: String) -> bool {
self.requests.get(&ip).unwrap_or(&0) >= &self.req_limit
self.requests.lock().unwrap().get(&ip).unwrap_or(&0) >= &self.req_limit
}
}

impl Middleware for RateLimiter {
fn pre(&mut self, req: Request) -> MiddleRequest {
fn pre(&self, req: Request) -> MiddleRequest {
let ip = remove_address_port(&req.address);

self.check_reset();
Expand Down
Loading

0 comments on commit 7fe8406

Please sign in to comment.