Skip to content

Commit 0bf6ef6

Browse files
committed
release
0 parents  commit 0bf6ef6

File tree

8 files changed

+310
-0
lines changed

8 files changed

+310
-0
lines changed

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
9+
jobs:
10+
build:
11+
name: Build & Test
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Install Rust
18+
uses: dtolnay/rust-toolchain@stable
19+
20+
- name: Cache Cargo registry
21+
uses: actions/cache@v4
22+
with:
23+
path: |
24+
~/.cargo/registry
25+
~/.cargo/git
26+
target
27+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
28+
29+
- name: Build
30+
run: cargo build --release --verbose
31+
32+
- name: Run tests
33+
run: cargo test --verbose
34+
35+
- name: Clippy (Lint)
36+
run: cargo clippy -- -D warnings
37+
38+
- name: Rustfmt (Format check)
39+
run: cargo fmt -- --check

.github/workflows/release.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*"
7+
8+
jobs:
9+
build-release:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: dtolnay/rust-toolchain@stable
14+
- name: Build release
15+
run: cargo build --release
16+
- name: Upload artifact
17+
uses: actions/upload-artifact@v4
18+
with:
19+
name: hp-http-rs
20+
path: target/release/hp-http-rs

Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Makefile for hp-http-rs
2+
3+
BINARY=hp-http-rs
4+
5+
all: build
6+
7+
build:
8+
cargo build --release
9+
10+
run:
11+
cargo run --release
12+
13+
clean:
14+
cargo clean
15+
16+
test:
17+
cargo test
18+
19+
fmt:
20+
cargo fmt --all
21+
22+
check:
23+
cargo clippy -- -D warnings
24+
25+
install:
26+
cargo install --path .
27+
28+
.PHONY: all build run clean test fmt check install

README.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# High-Performance HTTP Server in Rust
2+
3+
This project is a lightweight and high-performance HTTP server written in **Rust**.
4+
It is designed with modular structure, concurrency, and real-time statistics tracking.
5+
6+
---
7+
8+
## Features
9+
10+
- Lightweight and minimal dependencies
11+
- Fast response for core endpoints:
12+
- `GET /` → returns `"OK"`
13+
- `GET /stats` → returns JSON with real-time statistics:
14+
- `total_connections`: number of requests since server start
15+
- `rps`: requests per second
16+
- Modular code structure:
17+
- `server.rs` → manages server lifecycle
18+
- `routes.rs` → defines HTTP routes
19+
- `stats.rs` → tracks server statistics
20+
- Built on **Tokio** and **Hyper** for asynchronous performance
21+
22+
---
23+
24+
## Project Structure
25+
26+
```
27+
src/
28+
├─ main.rs # Entry point
29+
├─ server.rs # HTTP server logic
30+
├─ routes.rs # Routing and responses
31+
└─ stats.rs # Real-time statistics
32+
Cargo.toml # Project configuration
33+
Makefile # Build and utility commands
34+
```
35+
36+
---
37+
38+
## Requirements
39+
40+
- Rust (latest stable version recommended)
41+
- Cargo build system
42+
- Linux, macOS, or Windows
43+
44+
---
45+
46+
## Build and Run
47+
48+
### Build
49+
```bash
50+
make build
51+
```
52+
53+
### Run
54+
```bash
55+
make run
56+
```
57+
58+
The server will start by default at: `http://127.0.0.1:8080`
59+
60+
---
61+
62+
## Usage
63+
64+
- Root endpoint:
65+
```bash
66+
curl http://127.0.0.1:8080/
67+
```
68+
Response:
69+
```
70+
OK
71+
```
72+
73+
- Statistics endpoint:
74+
```bash
75+
curl http://127.0.0.1:8080/stats
76+
```
77+
Example response:
78+
```json
79+
{
80+
"total_connections": 42,
81+
"rps": 7
82+
}
83+
```
84+
85+
---
86+
87+
## Development
88+
89+
- Format code:
90+
```bash
91+
make fmt
92+
```
93+
- Run linter:
94+
```bash
95+
make check
96+
```
97+
- Run tests:
98+
```bash
99+
make test
100+
```
101+
102+
---
103+
104+
## License
105+
106+
This project is licensed under the **MIT License**.
107+
You are free to use, modify, and distribute it under the terms of the license.

src/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
mod server;
2+
mod stats;
3+
mod routes;
4+
5+
use server::HttpServer;
6+
7+
#[tokio::main]
8+
async fn main() {
9+
let server = HttpServer::new("127.0.0.1:8080");
10+
println!("🚀 Server running on http://127.0.0.1:8080");
11+
server.run().await;
12+
}

src/routes.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::stats::ServerStats;
2+
use hyper::{Body, Request, Response};
3+
use serde_json::json;
4+
use std::sync::Arc;
5+
6+
pub async fn router(
7+
req: Request<Body>,
8+
stats: Arc<ServerStats>,
9+
) -> Result<Response<Body>, hyper::Error> {
10+
stats.increment();
11+
12+
match req.uri().path() {
13+
"/" => Ok(Response::new(Body::from("OK"))),
14+
"/stats" => {
15+
let data = json!({
16+
"total_connections": stats.total(),
17+
"rps": stats.rps(),
18+
});
19+
Ok(Response::new(Body::from(data.to_string())))
20+
}
21+
_ => Ok(Response::builder()
22+
.status(404)
23+
.body(Body::from("Not Found"))
24+
.unwrap()),
25+
}
26+
}

src/server.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use crate::routes::router;
2+
use crate::stats::ServerStats;
3+
use hyper::service::{make_service_fn, service_fn};
4+
use hyper::{Server, Request, Response, Body};
5+
use std::sync::Arc;
6+
7+
pub struct HttpServer {
8+
addr: String,
9+
stats: Arc<ServerStats>,
10+
}
11+
12+
impl HttpServer {
13+
pub fn new(addr: &str) -> Self {
14+
Self {
15+
addr: addr.to_string(),
16+
stats: Arc::new(ServerStats::new()),
17+
}
18+
}
19+
20+
pub async fn run(self) {
21+
let stats = self.stats.clone();
22+
23+
let make_svc = make_service_fn(move |_conn| {
24+
let stats = stats.clone();
25+
async move {
26+
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
27+
router(req, stats.clone())
28+
}))
29+
}
30+
});
31+
32+
let server = Server::bind(&self.addr.parse().unwrap()).serve(make_svc);
33+
34+
if let Err(e) = server.await {
35+
eprintln!("server error: {}", e);
36+
}
37+
}
38+
}

src/stats.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::sync::atomic::{AtomicUsize, Ordering};
2+
use std::time::{Instant, Duration};
3+
use std::sync::Mutex;
4+
5+
pub struct ServerStats {
6+
total: AtomicUsize,
7+
last_count: Mutex<(usize, Instant, usize)>, // (last_total, last_time, rps)
8+
}
9+
10+
impl ServerStats {
11+
pub fn new() -> Self {
12+
Self {
13+
total: AtomicUsize::new(0),
14+
last_count: Mutex::new((0, Instant::now(), 0)),
15+
}
16+
}
17+
18+
pub fn increment(&self) {
19+
self.total.fetch_add(1, Ordering::Relaxed);
20+
}
21+
22+
pub fn total(&self) -> usize {
23+
self.total.load(Ordering::Relaxed)
24+
}
25+
26+
pub fn rps(&self) -> usize {
27+
let mut last = self.last_count.lock().unwrap();
28+
let now = Instant::now();
29+
let elapsed = now.duration_since(last.1);
30+
31+
if elapsed >= Duration::from_secs(1) {
32+
let current_total = self.total();
33+
let diff = current_total - last.0;
34+
last.0 = current_total;
35+
last.1 = now;
36+
last.2 = diff;
37+
}
38+
last.2
39+
}
40+
}

0 commit comments

Comments
 (0)