mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-06 12:41:55 +08:00
203 lines
4.6 KiB
Go
203 lines
4.6 KiB
Go
package assetserver
|
|
|
|
import (
|
|
"fmt"
|
|
"html"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
runtimePath = "/wails/runtime"
|
|
capabilitiesPath = "/wails/capabilities"
|
|
flagsPath = "/wails/flags"
|
|
|
|
webViewRequestHeaderWindowId = "x-wails-window-id"
|
|
webViewRequestHeaderWindowName = "x-wails-window-name"
|
|
)
|
|
|
|
type RuntimeHandler interface {
|
|
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
|
}
|
|
|
|
type AssetServer struct {
|
|
options *Options
|
|
|
|
handler http.Handler
|
|
wsHandler *httputil.ReverseProxy
|
|
|
|
pluginScripts map[string]string
|
|
|
|
devServerURL string
|
|
|
|
assetServerWebView
|
|
}
|
|
|
|
func NewAssetServer(options *Options) (*AssetServer, error) {
|
|
result := &AssetServer{
|
|
options: options,
|
|
}
|
|
|
|
var err error
|
|
result.handler, err = result.setupHandler()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (a *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
start := time.Now()
|
|
wrapped := &contentTypeSniffer{rw: rw}
|
|
a.serveHTTP(wrapped, req)
|
|
a.options.Logger.Info(
|
|
"Asset Request:",
|
|
"windowName", req.Header.Get(webViewRequestHeaderWindowName),
|
|
"windowID", req.Header.Get(webViewRequestHeaderWindowId),
|
|
"code", wrapped.status,
|
|
"method", req.Method,
|
|
"path", req.URL.EscapedPath(),
|
|
"duration", time.Since(start),
|
|
)
|
|
}
|
|
|
|
func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
if a.wsHandler != nil {
|
|
a.wsHandler.ServeHTTP(rw, req)
|
|
return
|
|
} else {
|
|
if isWebSocket(req) {
|
|
// WebSockets are not supported by the AssetServer
|
|
rw.WriteHeader(http.StatusNotImplemented)
|
|
return
|
|
}
|
|
}
|
|
|
|
header := rw.Header()
|
|
// TODO: I don't think this is needed now?
|
|
//if a.servingFromDisk {
|
|
// header.Add(HeaderCacheControl, "no-cache")
|
|
//}
|
|
|
|
path := req.URL.Path
|
|
switch path {
|
|
case "", "/", "/index.html":
|
|
recorder := httptest.NewRecorder()
|
|
a.handler.ServeHTTP(recorder, req)
|
|
for k, v := range recorder.Result().Header {
|
|
header[k] = v
|
|
}
|
|
|
|
switch recorder.Code {
|
|
case http.StatusOK:
|
|
a.writeBlob(rw, indexHTML, recorder.Body.Bytes())
|
|
|
|
case http.StatusNotFound:
|
|
a.writeBlob(rw, indexHTML, defaultIndexHTML())
|
|
|
|
default:
|
|
rw.WriteHeader(recorder.Code)
|
|
|
|
}
|
|
return
|
|
|
|
case capabilitiesPath:
|
|
var data = a.options.GetCapabilities()
|
|
a.writeBlob(rw, path, data)
|
|
|
|
case flagsPath:
|
|
var data = a.options.GetFlags()
|
|
a.writeBlob(rw, path, data)
|
|
|
|
case runtimePath:
|
|
a.options.RuntimeHandler.ServeHTTP(rw, req)
|
|
return
|
|
|
|
default:
|
|
// Check if this is a plugin script
|
|
if script, ok := a.pluginScripts[path]; ok {
|
|
a.writeBlob(rw, path, []byte(script))
|
|
} else {
|
|
a.handler.ServeHTTP(rw, req)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) {
|
|
err := serveFile(rw, filename, blob)
|
|
if err != nil {
|
|
a.serveError(rw, err, "Unable to write content %s", filename)
|
|
}
|
|
}
|
|
|
|
func (a *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, args ...interface{}) {
|
|
args = append(args, err)
|
|
a.options.Logger.Error(msg+":", args...)
|
|
rw.WriteHeader(http.StatusInternalServerError)
|
|
}
|
|
|
|
func (a *AssetServer) LogDetails() {
|
|
if a.options.IsDebug {
|
|
var info = []any{
|
|
"assetsFS", a.options.Assets != nil,
|
|
"middleware", a.options.Middleware != nil,
|
|
"handler", a.options.Handler != nil,
|
|
}
|
|
if a.devServerURL != "" {
|
|
info = append(info, "devServerURL", a.devServerURL)
|
|
}
|
|
a.options.Logger.Info("AssetServer Info:", info...)
|
|
}
|
|
}
|
|
|
|
func (a *AssetServer) AddPluginScript(pluginName string, script string) {
|
|
if a.pluginScripts == nil {
|
|
a.pluginScripts = make(map[string]string)
|
|
}
|
|
pluginName = strings.ReplaceAll(pluginName, "/", "_")
|
|
pluginName = html.EscapeString(pluginName)
|
|
pluginScriptName := fmt.Sprintf("/wails/plugin/%s.js", pluginName)
|
|
a.pluginScripts[pluginScriptName] = script
|
|
}
|
|
|
|
func GetStartURL(userURL string) (string, error) {
|
|
devServerURL := GetDevServerURL()
|
|
if devServerURL != "" {
|
|
// Parse the port
|
|
parsedURL, err := url.Parse(devServerURL)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Error parsing environment variable 'FRONTEND_DEVSERVER_URL`: " + err.Error() + ". Please check your `Taskfile.yml` file")
|
|
}
|
|
port := parsedURL.Port()
|
|
if port != "" {
|
|
startURL += ":" + port
|
|
}
|
|
} else {
|
|
if userURL != "" {
|
|
// parse the url
|
|
parsedURL, err := url.Parse(userURL)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Error parsing URL: " + err.Error())
|
|
}
|
|
if parsedURL.Scheme == "" {
|
|
startURL = path.Join(startURL, userURL)
|
|
// if the original URL had a trailing slash, add it back
|
|
if strings.HasSuffix(userURL, "/") {
|
|
startURL = startURL + "/"
|
|
}
|
|
} else {
|
|
startURL = userURL
|
|
}
|
|
}
|
|
}
|
|
return startURL, nil
|
|
}
|