349 lines
9.6 KiB
V
349 lines
9.6 KiB
V
// uSockets Server Backend for V-Hono
|
|
//
|
|
// 这个文件提供 V-Hono 的 uSockets 高性能服务器后端
|
|
//
|
|
// 安装步骤:
|
|
// 1. 将 usockets 目录复制到 V-Hono 项目目录
|
|
// 2. 将此文件复制到 V-Hono 项目根目录
|
|
// 3. 修改 usockets/usockets.v 中的路径为绝对路径
|
|
//
|
|
// 使用方法:
|
|
// app.listen_usockets(3000) // 使用 uSockets 后端
|
|
// app.listen(":3000") // 使用默认 picoev 后端
|
|
//
|
|
// 编译命令 (必须使用 -enable-globals):
|
|
// v -enable-globals -cc gcc -ldflags "-ldbghelp" your_app.v -o app.exe
|
|
//
|
|
// 性能对比 (200连接, 100K请求):
|
|
// uSockets: ~22,000 RPS (高并发优化)
|
|
// picoev: ~15,000 RPS
|
|
//
|
|
// 注意: uSockets 在高并发场景下优势明显 (~50% 提升)
|
|
// 低并发时两者性能相近
|
|
|
|
module hono
|
|
|
|
import usockets
|
|
import net.http
|
|
import strings
|
|
|
|
// uSockets 服务器配置
|
|
pub struct UsocketsConfig {
|
|
pub:
|
|
port int = 8080
|
|
host string = '0.0.0.0'
|
|
keepalive_timeout int = 30
|
|
max_keepalive_req int = 10000
|
|
}
|
|
|
|
// 全局变量存储应用引用 (需要 -enable-globals 编译选项)
|
|
__global g_usockets_app = &Hono(unsafe { nil })
|
|
__global g_usockets_config = UsocketsConfig{}
|
|
|
|
// 使用 uSockets 启动服务器
|
|
pub fn (mut app Hono) listen_usockets(port int) {
|
|
app.listen_usockets_with_config(UsocketsConfig{
|
|
port: port
|
|
})
|
|
}
|
|
|
|
// 使用 uSockets 启动服务器(带配置)
|
|
pub fn (mut app Hono) listen_usockets_with_config(config UsocketsConfig) {
|
|
// 优化:预计算中间件前缀排序
|
|
app.precompute_middleware_prefixes()
|
|
|
|
// 存储到全局变量
|
|
g_usockets_app = unsafe { &app }
|
|
g_usockets_config = config
|
|
|
|
// 创建 uSockets 事件循环
|
|
loop := usockets.create_loop()
|
|
ctx := usockets.create_socket_context(loop)
|
|
|
|
// 设置回调
|
|
ctx.on_open(usockets_on_open)
|
|
ctx.on_data(usockets_on_data)
|
|
ctx.on_close(usockets_on_close)
|
|
ctx.on_writable(usockets_on_writable)
|
|
ctx.on_timeout(usockets_on_timeout)
|
|
ctx.on_end(usockets_on_end)
|
|
|
|
// 开始监听
|
|
listener := ctx.listen(config.port)
|
|
if listener.is_valid() {
|
|
host_str := if config.host == '' { '127.0.0.1' } else { config.host }
|
|
println('[v-hono] Listening on http://${host_str}:${config.port}/ (uSockets)')
|
|
loop.run()
|
|
} else {
|
|
eprintln('[v-hono] Failed to listen on port ${config.port}')
|
|
}
|
|
}
|
|
|
|
// uSockets 回调函数
|
|
fn usockets_on_open(s usockets.Socket, is_client int, ip &char, ip_length int) usockets.Socket {
|
|
return s
|
|
}
|
|
|
|
fn usockets_on_data(s usockets.Socket, data &char, length int) usockets.Socket {
|
|
if g_usockets_app == unsafe { nil } {
|
|
s.write_bytes('HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\nInternal Server Error')
|
|
return s
|
|
}
|
|
|
|
// 解析 HTTP 请求
|
|
raw_data := unsafe { tos(&u8(data), length) }
|
|
method, path, query_map, body := parse_http_request_usockets(raw_data)
|
|
|
|
if method.len == 0 {
|
|
s.write_bytes('HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nBad Request')
|
|
return s
|
|
}
|
|
|
|
// 路由匹配 - 优先使用快速路由器
|
|
mut response_sent := false
|
|
|
|
if g_usockets_app.use_fast_router {
|
|
if route_match := g_usockets_app.fast_router.match_route(method, path) {
|
|
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
|
|
|
// 优化1:零中间件快速路径
|
|
if !g_usockets_app.has_middlewares {
|
|
response := route_match.handler.handle(mut hono_ctx)
|
|
send_usockets_response(s, hono_ctx, response)
|
|
} else {
|
|
middlewares := get_middlewares_for_path_usockets_optimized(g_usockets_app, path)
|
|
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
|
send_usockets_response(s, hono_ctx, response)
|
|
}
|
|
response_sent = true
|
|
}
|
|
}
|
|
|
|
// 回退到混合路由器
|
|
if !response_sent {
|
|
if route_match := g_usockets_app.context_hybrid_router.match_route(method, path) {
|
|
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
|
|
|
// 优化1:零中间件快速路径
|
|
if !g_usockets_app.has_middlewares {
|
|
response := route_match.handler.handle(mut hono_ctx)
|
|
send_usockets_response(s, hono_ctx, response)
|
|
} else {
|
|
middlewares := get_middlewares_for_path_usockets_optimized(g_usockets_app, path)
|
|
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
|
send_usockets_response(s, hono_ctx, response)
|
|
}
|
|
response_sent = true
|
|
}
|
|
}
|
|
|
|
// 404 Not Found
|
|
if !response_sent {
|
|
mut hono_ctx := create_usockets_context(method, path, map[string]string{}, query_map, body)
|
|
|
|
if handler := g_usockets_app.not_found_handler {
|
|
response := handler(mut hono_ctx)
|
|
send_usockets_response(s, hono_ctx, response)
|
|
} else {
|
|
s.write_bytes('HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 9\r\nConnection: keep-alive\r\n\r\nNot Found')
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
fn usockets_on_close(s usockets.Socket, code int, reason voidptr) usockets.Socket {
|
|
return s
|
|
}
|
|
|
|
fn usockets_on_writable(s usockets.Socket) usockets.Socket {
|
|
return s
|
|
}
|
|
|
|
fn usockets_on_timeout(s usockets.Socket) usockets.Socket {
|
|
return s.close()
|
|
}
|
|
|
|
fn usockets_on_end(s usockets.Socket) usockets.Socket {
|
|
s.shutdown()
|
|
return s.close()
|
|
}
|
|
|
|
// 解析 HTTP 请求
|
|
fn parse_http_request_usockets(raw string) (string, string, map[string]string, string) {
|
|
mut method := ''
|
|
mut path := ''
|
|
mut query_map := map[string]string{}
|
|
mut body := ''
|
|
|
|
line_end := raw.index('\r\n') or { return method, path, query_map, body }
|
|
request_line := raw[..line_end]
|
|
|
|
parts := request_line.split(' ')
|
|
if parts.len < 2 {
|
|
return method, path, query_map, body
|
|
}
|
|
|
|
method = parts[0]
|
|
full_path := parts[1]
|
|
|
|
if query_idx := full_path.index('?') {
|
|
path = full_path[..query_idx]
|
|
query_str := full_path[query_idx + 1..]
|
|
for part in query_str.split('&') {
|
|
if eq_idx := part.index('=') {
|
|
query_map[part[..eq_idx]] = part[eq_idx + 1..]
|
|
}
|
|
}
|
|
} else {
|
|
path = full_path
|
|
}
|
|
|
|
if body_start := raw.index('\r\n\r\n') {
|
|
body = raw[body_start + 4..]
|
|
}
|
|
|
|
return method, path, query_map, body
|
|
}
|
|
|
|
// 创建 uSockets 上下文
|
|
fn create_usockets_context(method string, path string, params map[string]string, query map[string]string, body string) Context {
|
|
return Context{
|
|
req: http.Request{
|
|
method: match 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: path
|
|
data: body
|
|
}
|
|
params: params
|
|
query: query
|
|
body: body
|
|
url: path
|
|
path: path
|
|
status_code: 200
|
|
headers: map[string]string{}
|
|
}
|
|
}
|
|
|
|
// 获取路径对应的所有中间件(优化版:使用预排序的前缀列表)
|
|
fn get_middlewares_for_path_usockets_optimized(app &Hono, path string) []ContextMiddleware {
|
|
// 优化2:只有全局中间件时,直接返回引用(避免克隆)
|
|
if app.route_middlewares.len == 0 {
|
|
return app.context_middlewares
|
|
}
|
|
|
|
mut middlewares := app.context_middlewares.clone()
|
|
|
|
// 优化3:使用预排序的前缀列表(启动时已排序,不需要每次请求都排序)
|
|
for prefix in app.sorted_middleware_prefixes {
|
|
if path.starts_with(prefix) || prefix == '/' {
|
|
if mws := app.route_middlewares[prefix] {
|
|
middlewares << mws
|
|
}
|
|
}
|
|
}
|
|
|
|
return middlewares
|
|
}
|
|
|
|
// 获取路径对应的所有中间件(保留旧版本兼容)
|
|
fn get_middlewares_for_path_usockets(app &Hono, path string) []ContextMiddleware {
|
|
return get_middlewares_for_path_usockets_optimized(app, path)
|
|
}
|
|
|
|
// 执行中间件链
|
|
fn exec_middlewares_usockets(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_usockets(idx + 1, middlewares, mut c, handler)
|
|
})
|
|
}
|
|
return handler.handle(mut ctx)
|
|
}
|
|
|
|
// 发送 uSockets 响应
|
|
fn send_usockets_response(s usockets.Socket, ctx Context, response http.Response) {
|
|
status_code := if ctx.status_code != 0 { ctx.status_code } else { response.status_code }
|
|
|
|
mut resp := strings.new_builder(512)
|
|
|
|
// 状态行
|
|
resp.write_string('HTTP/1.1 ')
|
|
resp.write_string(status_code.str())
|
|
resp.write_string(' ')
|
|
resp.write_string(get_status_text_usockets(status_code))
|
|
resp.write_string('\r\n')
|
|
|
|
// 头部
|
|
mut has_content_type := false
|
|
for key, value in ctx.headers {
|
|
if key.to_lower() == 'content-length' {
|
|
continue
|
|
}
|
|
resp.write_string(key)
|
|
resp.write_string(': ')
|
|
resp.write_string(value)
|
|
resp.write_string('\r\n')
|
|
if key.to_lower() == 'content-type' {
|
|
has_content_type = true
|
|
}
|
|
}
|
|
|
|
if content_type := response.header.get(.content_type) {
|
|
if !has_content_type {
|
|
resp.write_string('Content-Type: ')
|
|
resp.write_string(content_type)
|
|
resp.write_string('\r\n')
|
|
has_content_type = true
|
|
}
|
|
}
|
|
|
|
if !has_content_type {
|
|
resp.write_string('Content-Type: text/plain; charset=utf-8\r\n')
|
|
}
|
|
|
|
// Content-Length
|
|
resp.write_string('Content-Length: ')
|
|
resp.write_string(response.body.len.str())
|
|
resp.write_string('\r\n')
|
|
|
|
// Connection
|
|
resp.write_string('Connection: keep-alive\r\n')
|
|
|
|
// 空行 + body
|
|
resp.write_string('\r\n')
|
|
resp.write_string(response.body)
|
|
|
|
s.write_bytes(resp.str())
|
|
}
|
|
|
|
// 获取状态码文本
|
|
fn get_status_text_usockets(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' }
|
|
}
|
|
}
|