Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

@@ -0,0 +1,136 @@
type Content {
body: Bytes
kind: Text
}
listening(result: dever.net.ListenResult.Listening(listener)) (
value: dever.system.Listener
) {
value = listener
}
listening(result: dever.net.ListenResult.Failed(message)) (value: dever.system.Listener) {
fail(app.WorkerFailure.Failed(message))
}
bound(result: dever.net.PortResult.Bound(port)) (value: Int) {
value = port
}
bound(result: dever.net.PortResult.Failed(message)) (value: Int) {
fail(app.WorkerFailure.Failed(message))
}
paused(result: dever.time.SleepResult.Done) () {}
paused(result: dever.time.SleepResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
closed(result: dever.io.CloseResult.Closed) () {}
closed(result: dever.io.CloseResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
serverTls(result: dever.tls.ServerResult.Ready(tls)) (value: dever.system.ServerTls) {
value = tls
}
serverTls(result: dever.tls.ServerResult.Failed(message)) (
value: dever.system.ServerTls
) {
fail(app.WorkerFailure.Failed(message))
}
responseValue(content: Content) (result: dever.http.Response) {
result = dever.http.Response {
status = 200
headers = [dever.http.header("Content-Type", content.kind)]
body = content.body
}
}
route(request: dever.http.Request, large: Bytes) (response: dever.http.Response) {
response = responseValue(select(request.target, large))
}
select(path: "/plain", large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("hello")
kind = "text/plain"
}
}
select(path: "/json", large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("{\"ok\":true}")
kind = "application/json"
}
}
select(path: "/bytes", large: Bytes) (result: Content) {
result = Content {
body = large
kind = "application/octet-stream"
}
}
select(path: other, large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("unknown benchmark path")
kind = "text/plain"
}
}
serve(
listener: dever.system.Listener,
tls: null,
limits: dever.http.Limits,
large: Bytes
) () {
dever.system.httpServe(route, listener, limits, large)
}
serve(
listener: dever.system.Listener,
tls: dever.system.ServerTls,
limits: dever.http.Limits,
large: Bytes
) () {
dever.system.httpServeTls(route, listener, limits, tls, large)
}
worker(
tls: dever.system.ServerTls?,
limits: dever.http.Limits,
large: Bytes,
lifetime: Int
) () {
listener = listening(result(dever.net.listen("127.0.0.1", 0)))
port = bound(result(dever.net.port(listener)))
server = run(serve(listener, tls, limits, large))
blocking(dever.io.println("READY|" + int.toText(port)))
paused(result(dever.task.sleep(lifetime)))
closed(result(dever.net.closeListener(listener)))
stop(server)
}
port.plain(limits: dever.http.Limits, large: Bytes, lifetime: Int) () {
worker(null, limits, large, lifetime)
}
port.secure(
limits: dever.http.Limits,
large: Bytes,
lifetime: Int,
certificate: Bytes,
key: Bytes
) () {
worker(
serverTls(result(dever.tls.server(certificate, key))),
limits,
large,
lifetime
)
}
+7
View File
@@ -0,0 +1,7 @@
cmd http = app.http
cmd https = app.https
cmd http2 = app.http2
cmd https2 = app.https2
+35
View File
@@ -0,0 +1,35 @@
type WorkerFailure {
error Failed(message: Text)
}
http() (result: Bool) {
port.plain(config.limits(), config.largeBody(), config.lifetime())
result = true
}
https() (result: Bool) {
port.secure(
config.limits(),
config.largeBody(),
config.lifetime(),
config.certificate(),
config.key()
)
result = true
}
http2() (result: Bool) {
port.plain(config.http2Limits(), config.largeBody(), config.lifetime())
result = true
}
https2() (result: Bool) {
port.secure(
config.http2Limits(),
config.largeBody(),
config.lifetime(),
config.certificate(),
config.key()
)
result = true
}
+66
View File
@@ -0,0 +1,66 @@
//! 独立 Axum 路由;用官方 Hyper 入口统一连接、报文和超时上限。
use axum::{
Router,
body::Bytes,
extract::DefaultBodyLimit,
http::header,
routing::{get, post},
};
use hyper_util::{
rt::{TokioIo, TokioTimer},
service::TowerToHyperService,
};
use std::{sync::Arc, time::Duration};
use tokio::{net::TcpListener, sync::Semaphore};
async fn serve(capacity: usize) -> Result<(), Box<dyn std::error::Error>> {
let router = Router::new()
.route(
"/json",
get(async |_: Bytes| {
(
[(header::CONTENT_TYPE, "application/json")],
"{\"ok\":true}",
)
}),
)
.route(
"/echo",
post(async |body: Bytes| ([(header::CONTENT_TYPE, "application/octet-stream")], body)),
)
.layer(DefaultBodyLimit::max(65536));
let listener = TcpListener::bind("127.0.0.1:0").await?;
let slots = Arc::new(Semaphore::new(capacity));
println!("READY|{}", listener.local_addr()?.port());
loop {
let permit = slots.clone().acquire_owned().await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
let service = TowerToHyperService::new(router.clone());
tokio::spawn(async move {
let _permit = permit;
let mut builder = hyper::server::conn::http1::Builder::new();
builder
.timer(TokioTimer::new())
.header_read_timeout(Duration::from_secs(5))
.max_buf_size(8192);
if let Err(error) = builder
.serve_connection(TokioIo::new(stream), service)
.await
{
if !error.is_incomplete_message() && !error.is_closed() {
eprintln!("connection: {error}");
}
}
});
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let capacity = std::env::args().nth(1).unwrap().parse()?;
tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()?
.block_on(serve(capacity))
}
@@ -0,0 +1,31 @@
route(request: dever.http.Request) (response: dever.http.Response) {
response = selected(request.target, request.body)
}
selected(path: "/json", body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 200, headers = [dever.http.header("content-type", "application/json")], body = dever.bytes.fromText("{\"ok\":true}") }
}
selected(path: "/echo", body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 200, headers = [dever.http.header("content-type", "application/octet-stream")], body = body }
}
selected(path: other, body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 404, headers = [], body = dever.bytes.fromText("") }
}
serve(connections: Int) () {
listener = dever.net.listen("127.0.0.1", 0)
port = dever.net.port(listener)
limits = dever.http.defaultLimits()
limits.connections = connections
limits.body_bytes = 65536
blocking(dever.io.println("READY|" + int.toText(port)))
dever.http.serve(route, listener, limits)
}
main() () {
serve(2048)
}
overload() () {
serve(8192)
}
@@ -0,0 +1,78 @@
package main
import (
"fmt"
"io"
"net"
"net/http"
"os"
"runtime"
"strconv"
"sync"
"time"
)
type listener struct {
net.Listener
slots chan struct{}
}
type connection struct {
net.Conn
slots chan struct{}
once sync.Once
}
func (value *connection) Close() error {
err := value.Conn.Close()
value.once.Do(func() { <-value.slots })
return err
}
func (value *listener) Accept() (net.Conn, error) {
value.slots <- struct{}{}
stream, err := value.Listener.Accept()
if err != nil {
<-value.slots
return nil, err
}
return &connection{Conn: stream, slots: value.slots}, nil
}
func main() {
capacity, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
runtime.GOMAXPROCS(2)
bound, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
server := http.Server{
ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Second, MaxHeaderBytes: 8192,
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
body, err := io.ReadAll(http.MaxBytesReader(writer, request.Body, 65536))
if err != nil {
http.Error(writer, err.Error(), http.StatusBadRequest)
return
}
switch request.URL.RequestURI() {
case "/json":
writer.Header().Set("Content-Type", "application/json")
writer.Write([]byte(`{"ok":true}`))
case "/echo":
writer.Header().Set("Content-Type", "application/octet-stream")
writer.Write(body)
default:
writer.WriteHeader(http.StatusNotFound)
}
}),
}
fmt.Printf("READY|%d\n", bound.Addr().(*net.TCPAddr).Port)
if err := server.Serve(&listener{Listener: bound, slots: make(chan struct{}, capacity)}); err != nil {
panic(err)
}
}
@@ -0,0 +1,121 @@
//! 独立 Hyper 服务及三种语言共用的 HTTP/1.1 客户端,不链接 Dever 运行库。
use bytes::Bytes;
use http_body_util::{BodyExt, Full, Limited};
use hyper::{Request, Response, body::Incoming, service::service_fn};
use hyper_util::rt::{TokioIo, TokioTimer};
use std::{convert::Infallible, sync::Arc, time::Duration};
use tokio::{net::TcpListener, sync::{Barrier, Semaphore}, time::Instant};
async fn route(request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
let path = request.uri().path().to_owned();
let body = match Limited::new(request.into_body(), 65536).collect().await {
Ok(body) => body.to_bytes(),
Err(_) => return Ok(Response::builder().status(400).body(Full::new(Bytes::new())).unwrap()),
};
let (status, kind, body) = match path.as_str() {
"/json" => (200, "application/json", Bytes::from_static(b"{\"ok\":true}")),
"/echo" => (200, "application/octet-stream", body),
_ => (404, "text/plain", Bytes::new()),
};
Ok(Response::builder().status(status).header("content-type", kind).body(Full::new(body)).unwrap())
}
async fn serve(capacity: usize) -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:0").await?;
let slots = Arc::new(Semaphore::new(capacity));
println!("READY|{}", listener.local_addr()?.port());
loop {
let permit = slots.clone().acquire_owned().await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
tokio::spawn(async move {
let _permit = permit;
let mut builder = hyper::server::conn::http1::Builder::new();
builder.timer(TokioTimer::new()).header_read_timeout(Duration::from_secs(5)).max_buf_size(8192);
if let Err(error) = builder.serve_connection(TokioIo::new(stream), service_fn(route)).await {
if !error.is_incomplete_message() && !error.is_closed() {
eprintln!("connection: {error}");
}
}
});
}
}
async fn load(port: u16, path: String, concurrency: usize, seconds: f64) -> Result<(), Box<dyn std::error::Error>> {
let payload = if path == "/echo" { Bytes::from(vec![b'x'; 65536]) } else { Bytes::new() };
let expected = if path == "/echo" { payload.clone() } else { Bytes::from_static(b"{\"ok\":true}") };
let kind = if path == "/echo" { "application/octet-stream" } else { "application/json" };
let barrier = Arc::new(Barrier::new(concurrency + 1));
let mut workers = Vec::new();
for _ in 0..concurrency {
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port)).await?;
stream.set_nodelay(true)?;
let (mut sender, connection) = hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
let driver = tokio::spawn(connection);
let barrier = barrier.clone();
let payload = payload.clone();
let expected = expected.clone();
let path = path.clone();
workers.push(tokio::spawn(async move {
barrier.wait().await;
let deadline = Instant::now() + Duration::from_secs_f64(seconds);
let mut samples = Vec::new();
let mut errors = 0_u64;
while Instant::now() < deadline {
let request = Request::builder().method(if path == "/echo" { "POST" } else { "GET" })
.uri(&path).header("host", "localhost").body(Full::new(payload.clone())).unwrap();
let started = Instant::now();
let exchange = async {
let response = sender.send_request(request).await.map_err(|error| error.to_string())?;
if response.status() != 200 || response.headers().get("content-type").and_then(|value| value.to_str().ok()) != Some(kind) {
return Err("status or content type differs".to_owned());
}
let body = response.into_body().collect().await.map_err(|error| error.to_string())?.to_bytes();
if body != expected { return Err("response body differs".to_owned()); }
Ok(())
};
match tokio::time::timeout(Duration::from_secs(5), exchange).await {
Ok(Ok(())) => samples.push(started.elapsed().as_micros() as u64),
result => {
eprintln!("request: {result:?}");
errors += 1;
break;
}
}
}
drop(sender);
driver.abort();
let _ = driver.await;
(samples, errors)
}));
}
let started = Instant::now();
barrier.wait().await;
let mut samples = Vec::new();
let mut errors = 0;
for worker in workers {
let (mut values, failed) = worker.await?;
samples.append(&mut values);
errors += failed;
}
let elapsed = started.elapsed().as_secs_f64();
samples.sort_unstable();
let percentile = |percent: usize| -> Option<f64> {
if samples.is_empty() { None } else { Some(samples[(samples.len() * percent).div_ceil(100) - 1] as f64 / 1000.0) }
};
println!("{}", serde_json::json!({"concurrency":concurrency,"seconds":seconds,"elapsed_seconds":elapsed,
"succeeded":samples.len(),"errors":errors,"qps":samples.len() as f64 / elapsed,
"p50_ms":percentile(50),"p95_ms":percentile(95),"p99_ms":percentile(99)}));
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arguments = std::env::args().skip(1).collect::<Vec<_>>();
let server = arguments[0] == "serve";
let runtime = tokio::runtime::Builder::new_multi_thread().worker_threads(if server { 2 } else { 4 }).enable_all().build()?;
if server {
runtime.block_on(serve(arguments[1].parse()?))
} else {
runtime.block_on(load(arguments[1].parse()?, arguments[2].clone(), arguments[3].parse()?, arguments[4].parse()?))
}
}
+46
View File
@@ -0,0 +1,46 @@
package main
import (
"fmt"
"net"
"os"
"runtime"
"strconv"
"time"
"github.com/gofiber/fiber/v3"
)
func main() {
capacity, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
runtime.GOMAXPROCS(2)
app := fiber.New(fiber.Config{
Concurrency: capacity,
BodyLimit: 65536,
ReadBufferSize: 8192,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Second,
CaseSensitive: true,
StrictRouting: true,
})
app.Get("/json", func(context fiber.Ctx) error {
context.Set("Content-Type", "application/json")
return context.SendString(`{"ok":true}`)
})
app.Post("/echo", func(context fiber.Ctx) error {
context.Set("Content-Type", "application/octet-stream")
return context.Send(context.BodyRaw())
})
bound, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
fmt.Printf("READY|%d\n", bound.Addr().(*net.TCPAddr).Port)
if err := app.Listener(bound, fiber.ListenConfig{DisableStartupMessage: true}); err != nil {
panic(err)
}
}
+142
View File
@@ -0,0 +1,142 @@
package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"runtime"
"sync"
"time"
)
type settings struct {
Workers int `json:"workers"`
Connections int `json:"connections"`
TimeoutMS int `json:"timeout_ms"`
LifetimeMS int `json:"lifetime_ms"`
Certificate string `json:"cert_path"`
Key string `json:"key_path"`
}
type limitedListener struct {
net.Listener
slots chan struct{}
closed chan struct{}
once sync.Once
}
type limitedConnection struct {
net.Conn
slots chan struct{}
once sync.Once
}
func (connection *limitedConnection) Close() error {
err := connection.Conn.Close()
connection.once.Do(func() { <-connection.slots })
return err
}
func (listener *limitedListener) Accept() (net.Conn, error) {
connection, err := listener.Listener.Accept()
if err != nil {
return nil, err
}
select {
case listener.slots <- struct{}{}:
return &limitedConnection{Conn: connection, slots: listener.slots}, nil
case <-listener.closed:
connection.Close()
return nil, net.ErrClosed
}
}
func (listener *limitedListener) Close() error {
listener.once.Do(func() { close(listener.closed) })
return listener.Listener.Close()
}
func run() error {
if len(os.Args) != 2 || (os.Args[1] != "http" && os.Args[1] != "https") {
return errors.New("usage: go_http http|https")
}
executable, err := os.Executable()
if err != nil {
return err
}
content, err := os.ReadFile(filepath.Join(filepath.Dir(executable), "config", "setting.json"))
if err != nil {
return err
}
var document struct {
Benchmark settings `json:"benchmark"`
}
if err := json.Unmarshal(content, &document); err != nil {
return err
}
config := document.Benchmark
if config.Workers < 1 || config.Connections < 1 || config.TimeoutMS < 1 || config.LifetimeMS < 1 {
return errors.New("benchmark requires positive workers, connections, timeout_ms and lifetime_ms")
}
runtime.GOMAXPROCS(config.Workers)
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return err
}
bounded := &limitedListener{Listener: listener, slots: make(chan struct{}, config.Connections), closed: make(chan struct{})}
defer bounded.Close()
large := bytes.Repeat([]byte{'x'}, 65536)
deadline := time.Duration(config.TimeoutMS) * time.Millisecond
server := &http.Server{
ReadTimeout: deadline, ReadHeaderTimeout: deadline, WriteTimeout: deadline,
IdleTimeout: deadline, MaxHeaderBytes: 8192,
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
body := http.MaxBytesReader(writer, request.Body, 65536)
defer body.Close()
if _, err := io.Copy(io.Discard, body); err != nil {
http.Error(writer, "invalid request body", http.StatusBadRequest)
return
}
switch request.URL.RequestURI() {
case "/plain":
writer.Header().Set("Content-Type", "text/plain")
io.WriteString(writer, "hello")
case "/json":
writer.Header().Set("Content-Type", "application/json")
io.WriteString(writer, `{"ok":true}`)
case "/bytes":
writer.Header().Set("Content-Type", "application/octet-stream")
writer.Write(large)
default:
http.NotFound(writer, request)
}
}),
}
timer := time.AfterFunc(time.Duration(config.LifetimeMS)*time.Millisecond, func() { server.Close() })
defer timer.Stop()
fmt.Printf("READY|%d\n", listener.Addr().(*net.TCPAddr).Port)
if os.Args[1] == "https" {
err = server.ServeTLS(bounded, config.Certificate, config.Key)
} else {
err = server.Serve(bounded)
}
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
@@ -0,0 +1,9 @@
plain(limits: dever.http.Limits, large: Bytes, lifetime: Int) () fails app.WorkerFailure
secure(
limits: dever.http.Limits,
large: Bytes,
lifetime: Int,
certificate: Bytes,
key: Bytes
) () fails app.WorkerFailure