Skip to content

Commit a29ece2

Browse files
Merge pull request #9664 from ltpp-universe/master
[Rust] Add hyperlane
2 parents 6001198 + b6c3fc7 commit a29ece2

17 files changed

+2232
-0
lines changed

frameworks/Rust/hyperlane/Cargo.lock

+1,795
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frameworks/Rust/hyperlane/Cargo.toml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[package]
2+
name = "hyperlane_techempower"
3+
version = "0.0.1"
4+
edition = "2021"
5+
authors = ["ltpp-universe <[email protected]>"]
6+
license = "MIT"
7+
description = """Hyperlane is a lightweight and high-performance Rust HTTP server library designed to simplify network service development. It supports HTTP request parsing, response building, and TCP communication, making it ideal for building modern web services. Additionally, it provides support for request and response middleware, WebSocket, and Server-Sent Events (SSE), enabling flexible and efficient real-time communication."""
8+
keywords = ["http", "request", "response", "tcp", "redirect"]
9+
repository = "https://github.com/ltpp-universe/hyperlane.git"
10+
categories = ["network-programming", "web-programming"]
11+
exclude = [
12+
"target",
13+
"Cargo.lock",
14+
"sh",
15+
".github",
16+
"logs",
17+
"**/*.log"
18+
]
19+
20+
[dependencies]
21+
hyperlane = "4.31.1"
22+
bb8 = "0.9.0"
23+
bb8-postgres = "0.9.0"
24+
rand = "0.9.0"
25+
tokio-postgres = { version = "0.7.13", features = ["with-uuid-0_8"] }
26+
chrono = "0.4.40"
27+
serde = "1.0.219"
28+
29+
[profile.dev]
30+
incremental = false
31+
opt-level = 3
32+
lto = true
33+
panic = "unwind"
34+
debug = false
35+
codegen-units = 1
36+
strip = "debuginfo"
37+
38+
[profile.release]
39+
incremental = false
40+
opt-level = 3
41+
lto = true
42+
panic = "unwind"
43+
debug = false
44+
codegen-units = 1
45+
strip = "debuginfo"

frameworks/Rust/hyperlane/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# [hyperlane](https://github.com/ltpp-universe/hyperlane) web framework
2+
3+
## Description
4+
5+
Hyperlane is a lightweight and high-performance Rust HTTP server library designed to simplify network service development. It supports HTTP request parsing, response building, and TCP communication, making it ideal for building modern web services. Additionally, it provides support for request and response middleware, WebSocket, and Server-Sent Events (SSE), enabling flexible and efficient real-time communication.
6+
7+
## Database
8+
9+
PostgreSQL
10+
11+
## Test URLs
12+
13+
### Test 1: JSON Encoding
14+
15+
http://localhost:8080/json
16+
17+
### Test 2: Single Row Query
18+
19+
http://localhost:8080/db
20+
21+
### Test 3: Multi Row Query
22+
23+
http://localhost:8080/queries?q=20
24+
25+
### Test 4: Plaintext
26+
27+
http://localhost:8080/plaintext
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"framework": "hyperlane",
3+
"tests": [
4+
{
5+
"default": {
6+
"dockerfile": "hyperlane.dockerfile",
7+
"json_url": "/json",
8+
"plaintext_url": "/plaintext",
9+
"db_url": "/db",
10+
"query_url": "/queries?q=",
11+
"port": 8080,
12+
"approach": "Realistic",
13+
"classification": "Micro",
14+
"database": "Postgres",
15+
"framework": "hyperlane",
16+
"language": "Rust",
17+
"orm": "raw",
18+
"platform": "Rust",
19+
"webserver": "hyperlane",
20+
"os": "Linux",
21+
"database_os": "Linux",
22+
"display_name": "hyperlane"
23+
}
24+
}
25+
]
26+
}

frameworks/Rust/hyperlane/config.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[framework]
2+
name = "hyperlane"
3+
4+
[main]
5+
urls.plaintext = "/plaintext"
6+
urls.json = "/json"
7+
urls.db = "/db"
8+
urls.query = "/queries?q="
9+
approach = "Realistic"
10+
classification = "Micro"
11+
database = "Postgres"
12+
database_os = "Linux"
13+
os = "Linux"
14+
orm = "raw"
15+
platform = "Rust"
16+
webserver = "hyperlane"
17+
versus = "None"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM rust:1.85
2+
3+
ENV POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world
4+
5+
ADD ./ /hyperlane_techempower
6+
WORKDIR /hyperlane_techempower
7+
8+
RUN cargo clean
9+
RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
10+
11+
EXPOSE 8080
12+
13+
CMD ./target/release/hyperlane_techempower
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub static RESPONSEDATA: &str = "Hello, World!";
2+
pub static DATABASE_TYPE: &str = "postgres";
3+
pub static DATABASE_HOST: &str = "tfb-database";
4+
pub static DATABASE_USER_NAME: &str = "benchmarkdbuser";
5+
pub static DATABASE_USER_PASSWORD: &str = "benchmarkdbpass";
6+
pub static DATABASE_PORT: usize = 5432;
7+
pub static DATABASE_NAME: &str = "hello_world";
8+
pub static TABLE_NAME: &str = "World";
9+
pub static ROW_LIMIT: i32 = 500;
10+
pub static HYPERLANE: &str = "hyperlane";

frameworks/Rust/hyperlane/src/db.rs

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use crate::*;
2+
3+
pub async fn get_db_connection() -> DbPoolConnection {
4+
let db_pool: DbPoolConnection = DB.read().await.clone().unwrap();
5+
db_pool
6+
}
7+
8+
pub async fn create_batabase() {
9+
let db_pool: DbPoolConnection = get_db_connection().await;
10+
let connection: DbConnection = db_pool.get().await.unwrap();
11+
let db_exists: bool = connection
12+
.query_one(
13+
"SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1);",
14+
&[&DATABASE_NAME],
15+
)
16+
.await
17+
.unwrap()
18+
.get(0);
19+
if !db_exists {
20+
println_warning!(
21+
"database `",
22+
DATABASE_NAME,
23+
"` not found. Creating database..."
24+
);
25+
connection
26+
.batch_execute(&format!("CREATE DATABASE {};", DATABASE_NAME))
27+
.await
28+
.unwrap();
29+
println_success!("database `", DATABASE_NAME, "` created successfully");
30+
}
31+
println_success!("database `", DATABASE_NAME, "` ready");
32+
}
33+
34+
pub async fn create_table() {
35+
let db_pool: DbPoolConnection = get_db_connection().await;
36+
let connection: DbConnection = db_pool.get().await.unwrap();
37+
connection
38+
.batch_execute(&format!(
39+
"CREATE TABLE IF NOT EXISTS {} (
40+
id SERIAL PRIMARY KEY,
41+
randomNumber INTEGER NOT NULL
42+
);",
43+
TABLE_NAME
44+
))
45+
.await
46+
.unwrap();
47+
println_success!("table `", TABLE_NAME, "` ready");
48+
}
49+
50+
pub async fn insert_records() {
51+
let db_pool: DbPoolConnection = get_db_connection().await;
52+
let connection: DbConnection = db_pool.get().await.unwrap();
53+
let row: Row = connection
54+
.query_one(&format!("SELECT COUNT(*) FROM {}", TABLE_NAME), &[])
55+
.await
56+
.unwrap();
57+
let count: i64 = row.get(0);
58+
let limit: i64 = ROW_LIMIT as i64;
59+
if count >= limit {
60+
println_warning!(format!(
61+
"table '{}' already has {} records. No need to insert.",
62+
TABLE_NAME, count
63+
));
64+
return;
65+
}
66+
let missing_count: i64 = limit - count;
67+
println_warning!(format!(
68+
"table '{}' has {} records. Inserting {} missing records...",
69+
TABLE_NAME, count, missing_count
70+
));
71+
let mut rng: rand::prelude::ThreadRng = rand::rng();
72+
let mut values: Vec<String> = Vec::new();
73+
for _ in 0..missing_count {
74+
let random_number: i32 = rng.random_range(1..=10000);
75+
values.push(format!("(DEFAULT, {})", random_number));
76+
}
77+
let query: String = format!(
78+
"INSERT INTO {} (id, randomNumber) VALUES {}",
79+
TABLE_NAME,
80+
values.join(",")
81+
);
82+
connection.batch_execute(&query).await.unwrap();
83+
println_success!(format!(
84+
"successfully inserted {} missing records into '{}' table.",
85+
TABLE_NAME, missing_count
86+
));
87+
}
88+
89+
pub async fn init_db() {
90+
let db_url: &str = match option_env!("POSTGRES_URL") {
91+
Some(it) => it,
92+
_ => &format!(
93+
"{}://{}:{}@{}:{}/{}",
94+
DATABASE_TYPE,
95+
DATABASE_USER_NAME,
96+
DATABASE_USER_PASSWORD,
97+
DATABASE_HOST,
98+
DATABASE_PORT,
99+
DATABASE_NAME
100+
),
101+
};
102+
println_warning!("db url: ", db_url);
103+
let config: Config = db_url.parse::<Config>().unwrap();
104+
let db_manager: PostgresConnectionManager<NoTls> =
105+
PostgresConnectionManager::new(config, NoTls);
106+
let db_pool: DbPoolConnection = Pool::builder().build(db_manager).await.unwrap();
107+
{
108+
let mut db_pool_lock: RwLockWriteGuard<'_, Option<DbPoolConnection>> = DB.write().await;
109+
*db_pool_lock = Some(db_pool.clone());
110+
}
111+
create_batabase().await;
112+
create_table().await;
113+
insert_records().await;
114+
}
115+
116+
pub async fn random_world_row() -> Result<QueryRow, Box<dyn std::error::Error>> {
117+
let random_id: i32 = rand::rng().random_range(1..ROW_LIMIT);
118+
let db_pool: DbPoolConnection = get_db_connection().await;
119+
let connection: DbConnection = db_pool
120+
.get()
121+
.await
122+
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("timeout: {}", e)))?;
123+
let stmt: Statement = connection
124+
.prepare(&format!(
125+
"SELECT id, randomNumber FROM {} WHERE id = $1",
126+
TABLE_NAME
127+
))
128+
.await?;
129+
if let Some(rows) = connection.query_opt(&stmt, &[&random_id]).await? {
130+
let id: i32 = rows.get(0);
131+
let random_number: i32 = rows.get(1);
132+
return Ok(QueryRow::new(id, random_number));
133+
}
134+
return Ok(QueryRow::new(0, 0));
135+
}

frameworks/Rust/hyperlane/src/lazy.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
use crate::*;
2+
3+
pub static DB: Lazy<ArcRwLock<Option<DbPoolConnection>>> =
4+
Lazy::new(|| Arc::new(RwLock::new(None)));

frameworks/Rust/hyperlane/src/main.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
pub(crate) mod constant;
2+
pub(crate) mod db;
3+
pub(crate) mod lazy;
4+
pub(crate) mod request_middleware;
5+
pub(crate) mod response_middleware;
6+
pub(crate) mod route;
7+
pub(crate) mod server;
8+
pub(crate) mod r#type;
9+
pub(crate) mod utils;
10+
11+
pub(crate) use bb8::{Pool, PooledConnection};
12+
pub(crate) use bb8_postgres::PostgresConnectionManager;
13+
pub(crate) use chrono::{DateTime, Utc};
14+
pub(crate) use constant::*;
15+
pub(crate) use db::*;
16+
pub(crate) use hyperlane::{
17+
once_cell::sync::Lazy,
18+
serde::*,
19+
serde_json::json,
20+
tokio::sync::{RwLock, RwLockWriteGuard},
21+
*,
22+
};
23+
pub(crate) use lazy::*;
24+
pub(crate) use r#type::*;
25+
pub(crate) use rand::Rng;
26+
pub(crate) use request_middleware::*;
27+
pub(crate) use response_middleware::*;
28+
pub(crate) use route::*;
29+
30+
pub(crate) use server::*;
31+
pub(crate) use std::time::SystemTime;
32+
pub(crate) use std::{io, sync::Arc};
33+
pub(crate) use tokio_postgres::{Config, NoTls, Row, Statement};
34+
pub(crate) use utils::*;
35+
36+
#[tokio::main]
37+
async fn main() {
38+
println_warning!("start connect db");
39+
init_db().await;
40+
println_success!("connect db finish");
41+
println_warning!("start init server");
42+
run_server().await;
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::*;
2+
3+
pub async fn request(controller_data: ControllerData) {
4+
let _ = controller_data
5+
.set_response_header(CONNECTION, CONNECTION_KEEP_ALIVE)
6+
.await
7+
.set_response_header(
8+
CONTENT_TYPE,
9+
format!("{}; {}", APPLICATION_JSON, CHARSET_UTF_8),
10+
)
11+
.await
12+
.set_response_header(SERVER, HYPERLANE)
13+
.await
14+
.set_response_header(DATE, generate_rfc1123_timestamp())
15+
.await
16+
.set_response_status_code(200)
17+
.await;
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use crate::*;
2+
3+
pub async fn response(controller_data: ControllerData) {
4+
let _ = controller_data.send().await;
5+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::*;
2+
3+
pub async fn json(controller_data: ControllerData) {
4+
let json: serde_json::Value = json!({
5+
"message": RESPONSEDATA
6+
});
7+
let _ = controller_data
8+
.set_response_body(serde_json::to_string(&json).unwrap_or_default())
9+
.await;
10+
}
11+
12+
pub async fn plaintext(controller_data: ControllerData) {
13+
let _ = controller_data
14+
.set_response_header(CONTENT_TYPE, TEXT_PLAIN)
15+
.await
16+
.set_response_body(RESPONSEDATA)
17+
.await;
18+
}
19+
20+
pub async fn db(controller_data: ControllerData) {
21+
let query_row: QueryRow = random_world_row().await.unwrap();
22+
let _ = controller_data
23+
.set_response_body(serde_json::to_string(&query_row).unwrap_or_default())
24+
.await;
25+
}
26+
27+
pub async fn queries(controller_data: ControllerData) {
28+
let queries: Queries = controller_data
29+
.get_request_query("q")
30+
.await
31+
.and_then(|queries| queries.parse::<Queries>().ok())
32+
.unwrap_or_default()
33+
.min(ROW_LIMIT as usize)
34+
.max(1);
35+
let mut data: Vec<QueryRow> = Vec::with_capacity(queries);
36+
for _ in 0..queries {
37+
let _ = random_world_row().await.map(|row| {
38+
data.push(row);
39+
});
40+
}
41+
let _ = controller_data
42+
.set_response_body(serde_json::to_string(&data).unwrap_or_default())
43+
.await;
44+
}

0 commit comments

Comments
 (0)