diff --git a/LICENSE b/LICENSE index ff42636..a90a021 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Nicolas Lepage + Copyright 2021 Nicolas Lepage Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/api.go b/docs/api.go index def96b7..ebdf1e9 100644 --- a/docs/api.go +++ b/docs/api.go @@ -4,39 +4,23 @@ import ( "encoding/json" "fmt" "net/http" - "time" wasmhttp "github.com/nlepage/go-wasm-http-server" ) -var no = 1 - func main() { http.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) { - h, m, s := time.Now().Clock() - fmt.Printf("hello at %02d:%02d:%02d\n", h, m, s) - params := make(map[string]string) if err := json.NewDecoder(req.Body).Decode(¶ms); err != nil { panic(err) } if err := json.NewEncoder(res).Encode(map[string]string{ - "message": fmt.Sprintf("Hello %s! (%d)", params["name"], no), + "message": fmt.Sprintf("Hello %s!", params["name"]), }); err != nil { panic(err) } - no++ }) - wasmhttp.Serve(nil) - - go func() { - for range time.Tick(time.Second) { - h, m, s := time.Now().Clock() - fmt.Printf("tick at %02d:%02d:%02d\n", h, m, s) - } - }() - - select {} + wasmhttp.ServeOnce(nil) } diff --git a/docs/api.wasm b/docs/api.wasm index 34918f3..bde8613 100755 Binary files a/docs/api.wasm and b/docs/api.wasm differ diff --git a/docs/index.html b/docs/index.html index f01df0e..cf9b3ea 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,8 +2,9 @@ go-wasm-http-server demo - \ No newline at end of file diff --git a/docs/sw.js b/docs/sw.js index 1c1fe78..7d6493f 100644 --- a/docs/sw.js +++ b/docs/sw.js @@ -1 +1,3 @@ importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@latest/sw.js') + +registerWasmHTTPListener('api.wasm') diff --git a/serve.go b/serve.go index c424565..0b62083 100644 --- a/serve.go +++ b/serve.go @@ -1,6 +1,7 @@ package wasmhttp import ( + "context" "fmt" "net/http" "os" @@ -12,35 +13,34 @@ import ( // Serve serves HTTP requests using handler or http.DefaultServeMux if handler is nil. func Serve(handler http.Handler) func() { - h := handler + var h = handler if h == nil { h = http.DefaultServeMux } - path := os.Getenv("WASMHTTP_PATH") + var path = os.Getenv("WASMHTTP_PATH") if !strings.HasSuffix(path, "/") { path = path + "/" } if path != "" { // FIXME always true since / suffix is added to path - prefix := os.Getenv("WASMHTTP_PATH") + var prefix = os.Getenv("WASMHTTP_PATH") for strings.HasSuffix(prefix, "/") { prefix = strings.TrimSuffix(prefix, "/") } - mux := http.NewServeMux() + var mux = http.NewServeMux() mux.Handle(path, http.StripPrefix(prefix, h)) h = mux } - cb := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { - jsReq := whutil.Request{args[0]} + var cb = js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + var jsReq = whutil.Request{args[0]} var resPromise = whutil.NewPromise(func(resolve whutil.PromiseResolve, reject whutil.PromiseReject) { go func() { defer func() { - r := recover() - if r != nil { + if r := recover(); r != nil { if err, ok := r.(error); ok { reject(fmt.Sprintf("wasmhttp: panic: %+v\n", err)) } else { @@ -49,12 +49,12 @@ func Serve(handler http.Handler) func() { } }() - req, err := jsReq.HTTPRequest() + var req, err = jsReq.HTTPRequest() if err != nil { panic(err) } - res := whutil.NewResponseWriter() + var res = whutil.NewResponseWriter() h.ServeHTTP(res, req) @@ -69,3 +69,65 @@ func Serve(handler http.Handler) func() { return cb.Release } + +func ServeOnce(handler http.Handler) { + var ctx, cancel = context.WithCancel(context.Background()) + + var h = handler + if h == nil { + h = http.DefaultServeMux + } + + var path = os.Getenv("WASMHTTP_PATH") + if !strings.HasSuffix(path, "/") { + path = path + "/" + } + + if path != "" { // FIXME always true since / suffix is added to path + var prefix = os.Getenv("WASMHTTP_PATH") + for strings.HasSuffix(prefix, "/") { + prefix = strings.TrimSuffix(prefix, "/") + } + + var mux = http.NewServeMux() + mux.Handle(path, http.StripPrefix(prefix, h)) + h = mux + } + + var cb = js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + var jsReq = whutil.Request{args[0]} + + var resPromise = whutil.NewPromise(func(resolve whutil.PromiseResolve, reject whutil.PromiseReject) { + go func() { + defer cancel() + + defer func() { + if r := recover(); r != nil { + if err, ok := r.(error); ok { + reject(fmt.Sprintf("wasmhttp: panic: %+v\n", err)) + } else { + reject(fmt.Sprintf("wasmhttp: panic: %v\n", r)) + } + } + }() + + var req, err = jsReq.HTTPRequest() + if err != nil { + panic(err) + } + + var res = whutil.NewResponseWriter() + + h.ServeHTTP(res, req) + + resolve(res) + }() + }) + + return resPromise + }) + + js.Global().Get("wasmhttp").Call("registerHandler", os.Getenv("WASMHTTP_HANDLER_ID"), cb) + + <-ctx.Done() +} diff --git a/sw.js b/sw.js index a4638d2..f6b1645 100644 --- a/sw.js +++ b/sw.js @@ -76,3 +76,23 @@ addEventListener('fetch', e => { e.respondWith(handler(e.request)) }) + +function registerWasmHTTPListener(wasm, base, args) { + let path = new URL(registration.scope).pathname + if (base && base !== '') path = `${trimEnd(path, '/')}/${trimStart(base, '/')}` + + addEventListener('fetch', async e => { + const { pathname } = new URL(e.request.url) + if (!pathname.startsWith(path)) return + + const handlerId = `${nextHandlerId++}` + const handlerPromise = new Promise(resolve => handlerResolvers[handlerId] = resolve) + + // FIXME await ? catch ? + startWasm(wasm, { env: { WASMHTTP_HANDLER_ID: handlerId, WASMHTTP_PATH: path }, args }) + + const handler = await handlerPromise + + e.respondWith(handler(e.request)) + }) +} \ No newline at end of file