Skip to content

Commit 5fbc179

Browse files
committed
refactor(server): split server creation and startup logic
Extract websocket accept key generation into separate function Move server creation and startup into distinct operations Update dependencies to support new crypto functionality
1 parent 2a83cc2 commit 5fbc179

File tree

3 files changed

+98
-83
lines changed

3 files changed

+98
-83
lines changed

moon.mod.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
{
22
"name": "oboard/mocket",
3-
"version": "0.6.1",
3+
"version": "0.6.2",
44
"deps": {
55
"illusory0x0/native": "0.2.1",
6-
"moonbitlang/x": "0.4.34",
6+
"moonbitlang/x": "0.4.37",
77
"tonyfettes/uri": "0.1.0"
88
},
99
"readme": "README.md",
1010
"repository": "https://github.com/oboard/mocket",
1111
"license": "Apache-2.0",
12-
"keywords": ["http", "server"],
12+
"keywords": [
13+
"http",
14+
"server"
15+
],
1316
"description": "A web framework for MoonBit.",
1417
"source": "src",
1518
"preferred-target": "native"
16-
}
19+
}

src/mocket.js.mbt

Lines changed: 88 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,24 @@ extern "js" fn HttpResponseInternal::write_head(
6262
) -> Unit = "(s, statusCode, headers) => s.writeHead(statusCode, headers)"
6363

6464
///|
65-
pub extern "js" fn create_server(
65+
type NodeServerInternal
66+
67+
///|
68+
#module("node:http")
69+
extern "js" fn create_server(
6670
handler : (HttpRequestInternal, HttpResponseInternal, () -> Unit) -> Unit,
71+
) -> NodeServerInternal = "createServer"
72+
73+
///|
74+
pub extern "js" fn start_server(
75+
server : NodeServerInternal,
6776
port : Int,
77+
accept_key : (String) -> String,
6878
) -> Unit =
69-
#|(handler, port) => {
70-
#| const server = require('node:http').createServer(handler);
79+
#|(server, port, acceptKey) => {
7180
#| function start(server, port) {
72-
#| const http = require('node:http');
73-
#| const crypto = require('node:crypto');
74-
#| const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
7581
#| const clientsById = new Map();
7682
#|
77-
#| function acceptKey(key) {
78-
#| return crypto.createHash('sha1').update(String(key) + GUID).digest('base64');
79-
#| }
80-
#|
8183
#| // 供 FFI 调用的发送函数
8284
#| function sendText(socket, str) {
8385
#| const payload = Buffer.from(String(str), 'utf8');
@@ -254,77 +256,75 @@ pub extern "js" fn create_server(
254256

255257
///|
256258
pub fn serve_ffi(mocket : Mocket, port~ : Int) -> Unit {
257-
create_server(
258-
fn(req, res, _) {
259-
// 优化:简化 headers 转换逻辑
260-
let string_headers : Map[StringView, StringView] = {}
261-
guard (try? req.headers().to_value().to_json()) is Ok(Object(headers)) else {
262-
res.write_head(400, @js.Object::new().to_value())
263-
res.end(@js.Value::cast_from("Invalid headers"))
264-
return
265-
}
259+
let server = create_server(fn(req, res, _) {
260+
// 优化:简化 headers 转换逻辑
261+
let string_headers : Map[StringView, StringView] = {}
262+
guard (try? req.headers().to_value().to_json()) is Ok(Object(headers)) else {
263+
res.write_head(400, @js.Object::new().to_value())
264+
res.end(@js.Value::cast_from("Invalid headers"))
265+
return
266+
}
266267

267-
// 批量转换 headers,减少单个处理的开销
268-
headers.each(fn(key, value) {
269-
if value is String(v) {
270-
string_headers.set(key, v.to_string_view())
271-
}
272-
})
273-
let (params, handler) = match
274-
mocket.find_route(req.req_method(), req.url()) {
275-
Some((h, p)) => (p, h)
276-
_ => ({}, handle_not_found)
268+
// 批量转换 headers,减少单个处理的开销
269+
headers.each(fn(key, value) {
270+
if value is String(v) {
271+
string_headers.set(key, v.to_string_view())
277272
}
278-
let event = {
279-
req: {
280-
http_method: req.req_method(),
281-
url: req.url(),
282-
headers: string_headers,
283-
raw_body: "",
284-
},
285-
res: HttpResponse::new(OK),
286-
params,
273+
})
274+
let (params, handler) = match
275+
mocket.find_route(req.req_method(), req.url()) {
276+
Some((h, p)) => (p, h)
277+
_ => ({}, handle_not_found)
278+
}
279+
let event = {
280+
req: {
281+
http_method: req.req_method(),
282+
url: req.url(),
283+
headers: string_headers,
284+
raw_body: "",
285+
},
286+
res: HttpResponse::new(OK),
287+
params,
288+
}
289+
async_run(() => {
290+
// 如果是 post,先等待 data 事件
291+
if event.req.http_method == "POST" {
292+
let buffer = @buffer.new()
293+
(try? suspend(fn(res, _) {
294+
req.on("data", data => buffer.write_string(data.to_string()))
295+
req.on("end", _ => res(buffer.to_string()))
296+
}))
297+
|> ignore
298+
event.req.raw_body = buffer.to_bytes()
287299
}
288-
async_run(() => {
289-
// 如果是 post,先等待 data 事件
290-
if event.req.http_method == "POST" {
291-
let buffer = @buffer.new()
292-
(try? suspend(fn(res, _) {
293-
req.on("data", data => buffer.write_string(data.to_string()))
294-
req.on("end", _ => res(buffer.to_string()))
295-
}))
296-
|> ignore
297-
event.req.raw_body = buffer.to_bytes()
298-
}
299-
300-
// 执行中间件链和处理器
301-
let responder = execute_middlewares(mocket.middlewares, event, handler)
302-
// let boundary = "----------------moonbit-" + port.to_string()
303-
responder.options(event.res)
304-
res.write_head(
305-
event.res.status_code.to_int(),
306-
{
307-
let headers_obj = (try? @js.Value::from_json(
308-
event.res.headers.to_json(),
309-
)).or(@js.Object::new().to_value())
310-
if not(event.res.cookies.is_empty()) {
311-
let cookies = event.res.cookies
312-
.values()
313-
.map(fn(c) { c.to_string() })
314-
.to_array()
315-
set_js_property(headers_obj, "Set-Cookie", array_to_js(cookies))
316-
}
317-
headers_obj
318-
},
319-
)
320-
let buf = @buffer.new()
321-
responder.output(buf)
322-
event.res.raw_body = buf.to_bytes()
323-
res.end(@js.Value::cast_from(event.res.raw_body))
324-
})
325-
},
326-
port,
327-
)
300+
301+
// 执行中间件链和处理器
302+
let responder = execute_middlewares(mocket.middlewares, event, handler)
303+
// let boundary = "----------------moonbit-" + port.to_string()
304+
responder.options(event.res)
305+
res.write_head(
306+
event.res.status_code.to_int(),
307+
{
308+
let headers_obj = (try? @js.Value::from_json(
309+
event.res.headers.to_json(),
310+
)).or(@js.Object::new().to_value())
311+
if not(event.res.cookies.is_empty()) {
312+
let cookies = event.res.cookies
313+
.values()
314+
.map(fn(c) { c.to_string() })
315+
.to_array()
316+
set_js_property(headers_obj, "Set-Cookie", array_to_js(cookies))
317+
}
318+
headers_obj
319+
},
320+
)
321+
let buf = @buffer.new()
322+
responder.output(buf)
323+
event.res.raw_body = buf.to_bytes()
324+
res.end(@js.Value::cast_from(event.res.raw_body))
325+
})
326+
})
327+
start_server(server, port, websocket_accept_key)
328328
register_ws_handler(mocket, port)
329329
__ws_emit_js_export(__ws_emit_js_port)
330330
__ws_state_js_export(
@@ -333,6 +333,16 @@ pub fn serve_ffi(mocket : Mocket, port~ : Int) -> Unit {
333333
__get_port_by_connection_js_export(__get_port_by_connection_js)
334334
}
335335

336+
///|
337+
fn websocket_accept_key(key : String) -> String {
338+
let guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
339+
(key + guid)
340+
|> @encoding/utf8.encode
341+
|> @crypto.sha1
342+
|> Bytes::from_array
343+
|> @base64.encode
344+
}
345+
336346
///|
337347
/// 全局 handler 映射:port -> WebSocketHandler
338348
let ws_handler_map : Map[Int, WebSocketHandler] = Map::new()

src/moon.pkg.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"oboard/mocket/js",
44
"illusory0x0/native",
55
"tonyfettes/uri",
6-
"moonbitlang/x/fs"
6+
"moonbitlang/x/fs",
7+
"moonbitlang/x/crypto",
8+
"moonbitlang/x/codec/base64"
79
],
810
"expose": [
911
"__ws_emit_js",

0 commit comments

Comments
 (0)