5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 03:40:12 +08:00

[assetserver] Inject runtime/IPC into all index html files (#2203)

It will also be injected into all html files returned when requesting a folder path.
This commit is contained in:
stffabi 2023-10-23 11:35:18 +02:00 committed by GitHub
parent 6c46f6b41c
commit 30d17a760d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 36 deletions

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"net/http" "net/http"
"net/http/httptest"
"strings" "strings"
"golang.org/x/net/html" "golang.org/x/net/html"
@ -111,23 +110,60 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return return
} }
header := rw.Header()
if d.servingFromDisk { if d.servingFromDisk {
header.Add(HeaderCacheControl, "no-cache") rw.Header().Add(HeaderCacheControl, "no-cache")
}
handler := d.handler
if req.Method != http.MethodGet {
handler.ServeHTTP(rw, req)
return
} }
path := req.URL.Path path := req.URL.Path
switch path { if path == runtimeJSPath {
case "", "/", "/index.html": d.writeBlob(rw, path, d.runtimeJS)
recorder := httptest.NewRecorder()
d.handler.ServeHTTP(recorder, req) } else if path == runtimePath && d.runtimeHandler != nil {
for k, v := range recorder.HeaderMap { d.runtimeHandler.HandleRuntimeCall(rw, req)
header[k] = v
} else if path == ipcJSPath {
content := d.runtime.DesktopIPC()
if d.ipcJS != nil {
content = d.ipcJS(req)
}
d.writeBlob(rw, path, content)
} else if script, ok := d.pluginScripts[path]; ok {
d.writeBlob(rw, path, []byte(script))
} else if d.isRuntimeInjectionMatch(path) {
recorder := &bodyRecorder{
ResponseWriter: rw,
doRecord: func(code int, h http.Header) bool {
if code == http.StatusNotFound {
return true
} }
switch recorder.Code { if code != http.StatusOK {
return false
}
return strings.Contains(h.Get(HeaderContentType), "text/html")
}}
handler.ServeHTTP(recorder, req)
body := recorder.Body()
if body == nil {
// The body has been streamed and not recorded, we are finished
return
}
code := recorder.Code()
switch code {
case http.StatusOK: case http.StatusOK:
content, err := d.processIndexHTML(recorder.Body.Bytes()) content, err := d.processIndexHTML(body.Bytes())
if err != nil { if err != nil {
d.serveError(rw, err, "Unable to processIndexHTML") d.serveError(rw, err, "Unable to processIndexHTML")
return return
@ -138,34 +174,12 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
d.writeBlob(rw, indexHTML, defaultHTML) d.writeBlob(rw, indexHTML, defaultHTML)
default: default:
rw.WriteHeader(recorder.Code) rw.WriteHeader(code)
} }
case runtimeJSPath:
d.writeBlob(rw, path, d.runtimeJS)
case runtimePath:
if d.runtimeHandler != nil {
d.runtimeHandler.HandleRuntimeCall(rw, req)
} else { } else {
d.handler.ServeHTTP(rw, req) handler.ServeHTTP(rw, req)
}
case ipcJSPath:
content := d.runtime.DesktopIPC()
if d.ipcJS != nil {
content = d.ipcJS(req)
}
d.writeBlob(rw, path, content)
default:
// Check if this is a plugin script
if script, ok := d.pluginScripts[path]; ok {
d.writeBlob(rw, path, []byte(script))
return
}
d.handler.ServeHTTP(rw, req)
} }
} }
@ -229,3 +243,12 @@ func (d *AssetServer) logError(message string, args ...interface{}) {
d.logger.Error("[AssetServer] "+message, args...) d.logger.Error("[AssetServer] "+message, args...)
} }
} }
func (AssetServer) isRuntimeInjectionMatch(path string) bool {
if path == "" {
path = "/"
}
return strings.HasSuffix(path, "/") ||
strings.HasSuffix(path, "/"+indexHTML)
}

View File

@ -0,0 +1,61 @@
package assetserver
import (
"bytes"
"net/http"
)
type bodyRecorder struct {
http.ResponseWriter
doRecord func(code int, header http.Header) bool
body *bytes.Buffer
code int
wroteHeader bool
}
func (rw *bodyRecorder) Write(buf []byte) (int, error) {
rw.writeHeader(buf, http.StatusOK)
if rw.body != nil {
return rw.body.Write(buf)
}
return rw.ResponseWriter.Write(buf)
}
func (rw *bodyRecorder) WriteHeader(code int) {
rw.writeHeader(nil, code)
}
func (rw *bodyRecorder) Code() int {
return rw.code
}
func (rw *bodyRecorder) Body() *bytes.Buffer {
return rw.body
}
func (rw *bodyRecorder) writeHeader(buf []byte, code int) {
if rw.wroteHeader {
return
}
if rw.doRecord != nil {
header := rw.Header()
if len(buf) != 0 {
if _, hasType := header[HeaderContentType]; !hasType {
header.Set(HeaderContentType, http.DetectContentType(buf))
}
}
if rw.doRecord(code, header) {
rw.body = bytes.NewBuffer(nil)
}
}
if rw.body == nil {
rw.ResponseWriter.WriteHeader(code)
}
rw.code = code
rw.wroteHeader = true
}

View File

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New task created for linting v2 `task v2:lint`. Workflow updated to run the task. Added by @mikeee in [PR](https://github.com/wailsapp/wails/pull/2957) - New task created for linting v2 `task v2:lint`. Workflow updated to run the task. Added by @mikeee in [PR](https://github.com/wailsapp/wails/pull/2957)
- Added new community template wails-htmx-templ-chi-tailwind. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/2984) - Added new community template wails-htmx-templ-chi-tailwind. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/2984)
- Added CPU/GPU/Memory detection for `wails doctor`. Added by @leaanthony in #d51268b8d0680430f3a614775b13e6cd2b906d1c - Added CPU/GPU/Memory detection for `wails doctor`. Added by @leaanthony in #d51268b8d0680430f3a614775b13e6cd2b906d1c
- The [AssetServer](/docs/reference/options#assetserver) now injects the runtime/IPC into all index html files and into all html files returned when requesting a folder path. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2203)
### Changed ### Changed