perf: use picoev as default Server for improve response concurrency

This commit is contained in:
meiseayoung
2025-12-30 17:31:13 +08:00
parent ee2818782b
commit a38aeea614
23 changed files with 2034 additions and 36 deletions
+291
View File
@@ -0,0 +1,291 @@
module hono
import net
import net.http
import picoev
import picohttpparser
// picoev 服务器配置
pub struct PicoevConfig {
pub:
port int = 8080
host string
family net.AddrFamily = .ip6
timeout_secs int = 30
max_headers int = 100
max_read int = 8192
max_write int = 8192
}
// picoev 请求上下文
struct PicoevRequestContext {
mut:
app &Hono = unsafe { nil }
}
// 使用 picoev 启动服务器
pub fn (mut app Hono) listen_picoev(port int) {
app.listen_picoev_with_config(PicoevConfig{
port: port
})
}
// 使用 picoev 启动服务器(带配置)
pub fn (mut app Hono) listen_picoev_with_config(config PicoevConfig) {
mut ctx := &PicoevRequestContext{
app: unsafe { &app }
}
mut pico := picoev.new(
port: config.port
host: config.host
family: config.family
timeout_secs: config.timeout_secs
max_headers: config.max_headers
max_read: config.max_read
max_write: config.max_write
cb: picoev_callback
user_data: ctx
) or {
eprintln('[v-hono] Failed to create picoev server: ${err}')
return
}
host_str := if config.host == '' { '127.0.0.1' } else { config.host }
println('[v-hono] Listening on http://${host_str}:${config.port}/ (picoev)')
pico.serve()
}
// picoev 回调函数
fn picoev_callback(user_data voidptr, req picohttpparser.Request, mut res picohttpparser.Response) {
mut ctx := unsafe { &PicoevRequestContext(user_data) }
if ctx.app == unsafe { nil } {
res.http_500()
res.end()
return
}
// 解析路径和查询参数
path, query_map := parse_path_and_query(req.path)
// 路由匹配
method_str := req.method
// 优先使用快速路由器
if ctx.app.use_fast_router {
if route_match := ctx.app.fast_router.match_route(method_str, path) {
// 构造 Context
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
// 获取中间件
middlewares := get_middlewares_for_path_picoev(ctx.app, path)
// 执行中间件和处理器
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
// 发送响应
send_picoev_response(mut res, hono_ctx, response)
return
}
}
// 回退到混合路由器
if route_match := ctx.app.context_hybrid_router.match_route(method_str, path) {
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
// 获取中间件
middlewares := get_middlewares_for_path_picoev(ctx.app, path)
// 执行中间件和处理器
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
send_picoev_response(mut res, hono_ctx, response)
return
}
// 404 Not Found - 检查自定义 notFound 处理器
mut hono_ctx := create_picoev_context(req, map[string]string{}, query_map)
if handler := ctx.app.not_found_handler {
response := handler(mut hono_ctx)
send_picoev_response(mut res, hono_ctx, response)
return
}
// 默认 404 响应
hono_ctx.status(404)
res.raw('HTTP/1.1 404 Not Found\r\n')
res.header('Content-Type', 'text/plain')
res.header('Connection', 'keep-alive')
res.body('Not Found')
res.end()
}
// 获取路径对应的所有中间件(全局 + 路由前缀匹配的)
fn get_middlewares_for_path_picoev(app &Hono, path string) []ContextMiddleware {
mut middlewares := app.context_middlewares.clone()
// 按前缀长度排序,确保更具体的前缀后执行
mut prefixes := app.route_middlewares.keys()
prefixes.sort(a.len < b.len)
for prefix in prefixes {
if path.starts_with(prefix) || prefix == '/' {
if mws := app.route_middlewares[prefix] {
middlewares << mws
}
}
}
return middlewares
}
// 执行中间件链
fn exec_middlewares_picoev(idx int, middlewares []ContextMiddleware, mut ctx Context, handler IHandler) http.Response {
if idx < middlewares.len {
mw := middlewares[idx]
return mw(mut ctx, fn [idx, middlewares, handler] (mut c Context) http.Response {
return exec_middlewares_picoev(idx + 1, middlewares, mut c, handler)
})
} else {
return handler.handle(mut ctx)
}
}
// 解析路径和查询参数
fn parse_path_and_query(full_path string) (string, map[string]string) {
mut query_map := map[string]string{}
if idx := full_path.index('?') {
path := full_path[..idx]
query_str := full_path[idx + 1..]
// 解析查询参数
for part in query_str.split('&') {
if eq_idx := part.index('=') {
key := part[..eq_idx]
value := part[eq_idx + 1..]
query_map[key] = value
}
}
return path, query_map
}
return full_path, query_map
}
// 创建 picoev 上下文
fn create_picoev_context(req picohttpparser.Request, params map[string]string, query map[string]string) Context {
// 将 picohttpparser.Request 转换为需要的格式
return Context{
req: convert_picoev_request(req)
params: params
query: query
body: req.body
url: req.path
path: req.path
status_code: 200
headers: map[string]string{}
}
}
// 转换 picoev 请求
fn convert_picoev_request(req picohttpparser.Request) http.Request {
mut headers := http.new_header()
for i in 0 .. req.num_headers {
h := req.headers[i]
headers.add_custom(h.name, h.value) or { continue }
}
return http.Request{
method: match req.method {
'GET' { http.Method.get }
'POST' { http.Method.post }
'PUT' { http.Method.put }
'DELETE' { http.Method.delete }
'PATCH' { http.Method.patch }
'HEAD' { http.Method.head }
'OPTIONS' { http.Method.options }
else { http.Method.get }
}
url: req.path
data: req.body
header: headers
}
}
// 发送 picoev 响应
fn send_picoev_response(mut res picohttpparser.Response, ctx Context, response http.Response) {
// 设置状态码
status_code := if ctx.status_code != 0 { ctx.status_code } else { response.status_code }
if status_code == 200 {
res.http_ok()
} else {
// 手动构建状态行
status_text := get_status_text(status_code)
res.raw('HTTP/1.1 ${status_code} ${status_text}\r\n')
}
// 设置响应头
mut has_content_type := false
mut has_connection := false
// 从 Context 的 headers 中获取
for key, value in ctx.headers {
res.header(key, value)
if key.to_lower() == 'content-type' {
has_content_type = true
}
if key.to_lower() == 'connection' {
has_connection = true
}
}
// 从 http.Response 的 header 中获取
if content_type := response.header.get(.content_type) {
if !has_content_type {
res.header('Content-Type', content_type)
has_content_type = true
}
}
// 默认 Content-Type
if !has_content_type {
res.header('Content-Type', 'text/plain')
}
// Keep-Alive
if !has_connection {
res.header('Connection', 'keep-alive')
}
// 设置响应体并发送
res.body(response.body)
res.end() // 关键:必须调用 end() 来实际发送响应
}
// 获取状态码文本
fn get_status_text(code int) string {
return match code {
200 { 'OK' }
201 { 'Created' }
204 { 'No Content' }
301 { 'Moved Permanently' }
302 { 'Found' }
304 { 'Not Modified' }
400 { 'Bad Request' }
401 { 'Unauthorized' }
403 { 'Forbidden' }
404 { 'Not Found' }
405 { 'Method Not Allowed' }
500 { 'Internal Server Error' }
502 { 'Bad Gateway' }
503 { 'Service Unavailable' }
else { 'Unknown' }
}
}