1- package filters
1+ package quickjs
22
33import (
44 "context"
5+ "fmt"
56 "io"
67 "log"
78 "net/http"
@@ -15,7 +16,8 @@ import (
1516
1617var FilterInstQuickJS core.FilterInstance = func (config core.FilterParams ) (core.FilterCall , error ) {
1718 var param struct {
18- Exec string `json:"exec"`
19+ Exec string `json:"exec"`
20+ Debug bool `json:"debug"`
1921 }
2022 if err := config .Unmarshal (& param ); err != nil {
2123 return nil , err
@@ -29,45 +31,176 @@ var FilterInstQuickJS core.FilterInstance = func(config core.FilterParams) (core
2931 return err
3032 }
3133
32- var rt = quickjs .NewRuntime ()
34+ rt := quickjs .NewRuntime ()
35+ rt .SetExecuteTimeout (5 )
3336 defer rt .Close ()
3437
3538 jsCtx := rt .NewContext ()
3639 defer jsCtx .Close ()
3740
41+ // 在 debug 模式下,我们需要拦截输出
42+ var (
43+ outputBuffer strings.Builder
44+ logBuffer strings.Builder
45+ jsError error
46+ )
47+
3848 global := jsCtx .Globals ()
39- global .Set ("request" , createRequestObject (jsCtx , request ))
40- global .Set ("response" , createResponseObject (jsCtx , writer , request ))
41- global .Set ("console" , createConsoleObject (jsCtx ))
49+ global .Set ("request" , createRequestObject (jsCtx , request , metadata ))
50+
51+ // 根据是否 debug 模式创建不同的 response 对象
52+ if param .Debug {
53+ // debug 模式下使用虚假的 writer 来捕获输出
54+ global .Set ("response" , createResponseObject (jsCtx , & debugResponseWriter {
55+ buffer : & outputBuffer ,
56+ header : make (http.Header ),
57+ }, request ))
58+ global .Set ("console" , createConsoleObject (jsCtx , & logBuffer ))
59+ } else {
60+ global .Set ("response" , createResponseObject (jsCtx , writer , request ))
61+ global .Set ("console" , createConsoleObject (jsCtx , nil ))
62+ }
4263
4364 ret := jsCtx .Eval (js )
4465 defer ret .Free ()
66+ jsCtx .Loop ()
4567
4668 if ret .IsException () {
4769 err := jsCtx .Exception ()
48- return err
70+ jsError = err
4971 }
50- return nil
72+
73+ // 如果在 debug 模式下,返回 HTML 调试页面
74+ if param .Debug {
75+ return renderDebugPage (writer , & outputBuffer , & logBuffer , jsError )
76+ }
77+
78+ return jsError
5179 }, nil
5280}
5381
82+ // debugResponseWriter 用于在 debug 模式下捕获响应输出
83+ type debugResponseWriter struct {
84+ buffer * strings.Builder
85+ header http.Header
86+ status int
87+ }
88+
89+ func (w * debugResponseWriter ) Header () http.Header {
90+ return w .header
91+ }
92+
93+ func (w * debugResponseWriter ) Write (data []byte ) (int , error ) {
94+ return w .buffer .Write (data )
95+ }
96+
97+ func (w * debugResponseWriter ) WriteHeader (statusCode int ) {
98+ w .status = statusCode
99+ }
100+
101+ // renderDebugPage 渲染调试页面
102+ func renderDebugPage (writer http.ResponseWriter , outputBuffer , logBuffer * strings.Builder , jsError error ) error {
103+ writer .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
104+
105+ html := `<!DOCTYPE html>
106+ <html>
107+ <head>
108+ <title>QuickJS Debug</title>
109+ <style>
110+ body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
111+ .section { margin-bottom: 30px; border: 1px solid #ddd; border-radius: 5px; }
112+ .section-header { background: #f5f5f5; padding: 10px 15px; border-bottom: 1px solid #ddd; font-weight: bold; }
113+ .section-content { padding: 15px; background: white; }
114+ .output { white-space: pre-wrap; font-family: monospace; }
115+ .log { white-space: pre-wrap; font-family: monospace; background: #f8f8f8; }
116+ .error { color: #d00; background: #fee; padding: 10px; border-radius: 3px; }
117+ .success { color: #080; background: #efe; padding: 10px; border-radius: 3px; }
118+ </style>
119+ </head>
120+ <body>
121+ <h1>QuickJS Debug Output</h1>
122+
123+ <div class="section">
124+ <div class="section-header">执行结果</div>
125+ <div class="section-content">
126+ <div class="output">`
127+
128+ // 转义输出内容
129+ output := outputBuffer .String ()
130+ if output == "" {
131+ output = "(无输出)"
132+ }
133+ html += htmlEscape (output )
134+
135+ html += `</div>
136+ </div>
137+ </div>
138+
139+ <div class="section">
140+ <div class="section-header">控制台日志</div>
141+ <div class="section-content">
142+ <div class="log">`
143+
144+ // 转义日志内容
145+ logs := logBuffer .String ()
146+ if logs == "" {
147+ logs = "(无日志)"
148+ }
149+ html += htmlEscape (logs )
150+
151+ html += `</div>
152+ </div>
153+ </div>
154+
155+ <div class="section">
156+ <div class="section-header">执行状态</div>
157+ <div class="section-content">`
158+
159+ if jsError != nil {
160+ html += `<div class="error"><strong>错误:</strong> ` + htmlEscape (jsError .Error ()) + `</div>`
161+ } else {
162+ html += `<div class="success">执行成功</div>`
163+ }
164+
165+ html += `</div>
166+ </div>
167+ </body>
168+ </html>`
169+
170+ _ , err := writer .Write ([]byte (html ))
171+ return err
172+ }
173+
174+ // htmlEscape 转义 HTML 特殊字符
175+ func htmlEscape (s string ) string {
176+ return strings .NewReplacer (
177+ "&" , "&" ,
178+ "<" , "<" ,
179+ ">" , ">" ,
180+ `"` , """ ,
181+ "'" , "'" ,
182+ ).Replace (s )
183+ }
184+
54185// createRequestObject 创建表示 HTTP 请求的 JavaScript 对象
55- func createRequestObject (ctx * quickjs.Context , req * http.Request ) * quickjs.Value {
186+ func createRequestObject (ctx * quickjs.Context , req * http.Request , metadata * core. PageContent ) * quickjs.Value {
56187 obj := ctx .NewObject ()
57-
58188 // 基本属性
59189 obj .Set ("method" , ctx .NewString (req .Method ))
60- obj .Set ("url" , ctx .NewString (req .URL .String ()))
61- obj .Set ("path" , ctx .NewString (req .URL .Path ))
62- obj .Set ("query" , ctx .NewString (req .URL .RawQuery ))
190+ url := * req .URL
191+ url .Path = metadata .Path
192+ obj .Set ("url" , ctx .NewString (url .String ()))
193+ obj .Set ("path" , ctx .NewString (url .Path ))
194+ obj .Set ("rawPath" , ctx .NewString (req .URL .Path ))
195+ obj .Set ("query" , ctx .NewString (url .RawQuery ))
63196 obj .Set ("host" , ctx .NewString (req .Host ))
64197 obj .Set ("remoteAddr" , ctx .NewString (req .RemoteAddr ))
65198 obj .Set ("proto" , ctx .NewString (req .Proto ))
66199 obj .Set ("httpVersion" , ctx .NewString (req .Proto ))
67200
68201 // 解析查询参数
69202 queryObj := ctx .NewObject ()
70- for key , values := range req . URL .Query () {
203+ for key , values := range url .Query () {
71204 if len (values ) > 0 {
72205 queryObj .Set (key , ctx .NewString (values [0 ]))
73206 }
@@ -169,20 +302,20 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
169302 }
170303 return ctx .NewNull ()
171304 }))
172-
173305 obj .Set ("writeHead" , ctx .NewFunction (func (c * quickjs.Context , value * quickjs.Value , args []* quickjs.Value ) * quickjs.Value {
174306 if len (args ) >= 1 {
175307 statusCode := int (args [0 ].ToInt32 ())
176-
177308 // 处理可选的 headers 参数
178309 if len (args ) >= 2 && args [1 ].IsObject () {
179310 headersObj := args [1 ]
180- headersObj .Properties ().ForEach (func (key string , value * quickjs.Value ) bool {
181- writer .Header ().Set (key , value .String ())
182- return true
183- })
311+ names , err := headersObj .PropertyNames ()
312+ if err != nil {
313+ return ctx .NewError (err )
314+ }
315+ for _ , key := range names {
316+ writer .Header ().Set (key , headersObj .Get (key ).String ())
317+ }
184318 }
185-
186319 writer .WriteHeader (statusCode )
187320 }
188321 return ctx .NewNull ()
@@ -261,11 +394,11 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
261394 }
262395
263396 if secure := options .Get ("secure" ); ! secure .IsNull () {
264- cookie .Secure = secure .Bool ()
397+ cookie .Secure = secure .ToBool ()
265398 }
266399
267400 if httpOnly := options .Get ("httpOnly" ); ! httpOnly .IsNull () {
268- cookie .HttpOnly = httpOnly .Bool ()
401+ cookie .HttpOnly = httpOnly .ToBool ()
269402 }
270403 }
271404
@@ -278,48 +411,32 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
278411}
279412
280413// createConsoleObject 创建 console 对象用于日志输出
281- func createConsoleObject (ctx * quickjs.Context ) * quickjs.Value {
414+ func createConsoleObject (ctx * quickjs.Context , buf * strings. Builder ) * quickjs.Value {
282415 console := ctx .NewObject ()
283416
284- logFunc := func (level string ) func (* quickjs.Context , * quickjs.Value , []* quickjs.Value ) * quickjs.Value {
417+ logFunc := func (level string , buffer * strings. Builder ) func (* quickjs.Context , * quickjs.Value , []* quickjs.Value ) * quickjs.Value {
285418 return func (q * quickjs.Context , value * quickjs.Value , args []* quickjs.Value ) * quickjs.Value {
286419 var messages []string
287420 for _ , arg := range args {
288421 messages = append (messages , arg .String ())
289422 }
290- log .Printf ("[" + level + "] " + strings .Join (messages , " " ))
291- return ctx .NewNull ()
292- }
293- }
294-
295- console .Set ("log" , ctx .NewFunction (logFunc ("INFO" )))
296- console .Set ("info" , ctx .NewFunction (logFunc ("INFO" )))
297- console .Set ("warn" , ctx .NewFunction (logFunc ("WARN" )))
298- console .Set ("error" , ctx .NewFunction (logFunc ("ERROR" )))
299- console .Set ("debug" , ctx .NewFunction (logFunc ("DEBUG" )))
423+ message := fmt .Sprintf ("[%s] %s" , level , strings .Join (messages , " " ))
300424
301- // 添加 time 和 timeEnd 方法用于性能测量
302- timers := make ( map [ string ]time. Time )
425+ // 总是输出到系统日志
426+ log . Print ( message )
303427
304- console .Set ("time" , ctx .NewFunction (func (c * quickjs.Context , value * quickjs.Value , args []* quickjs.Value ) * quickjs.Value {
305- if len (args ) > 0 {
306- label := args [0 ].String ()
307- timers [label ] = time .Now ()
308- }
309- return ctx .NewNull ()
310- }))
311-
312- console .Set ("timeEnd" , ctx .NewFunction (func (c * quickjs.Context , value * quickjs.Value , args []* quickjs.Value ) * quickjs.Value {
313- if len (args ) > 0 {
314- label := args [0 ].String ()
315- if start , exists := timers [label ]; exists {
316- elapsed := time .Since (start )
317- log .Printf ("[TIMER] %s: %v" , label , elapsed )
318- delete (timers , label )
428+ // 如果有缓冲区,也写入缓冲区
429+ if buffer != nil {
430+ buffer .WriteString (message + "\n " )
319431 }
432+ return ctx .NewNull ()
320433 }
321- return ctx .NewNull ()
322- }))
434+ }
323435
436+ console .Set ("log" , ctx .NewFunction (logFunc ("INFO" , buf )))
437+ console .Set ("info" , ctx .NewFunction (logFunc ("INFO" , buf )))
438+ console .Set ("warn" , ctx .NewFunction (logFunc ("WARN" , buf )))
439+ console .Set ("error" , ctx .NewFunction (logFunc ("ERROR" , buf )))
440+ console .Set ("debug" , ctx .NewFunction (logFunc ("DEBUG" , buf )))
324441 return console
325442}
0 commit comments