Switch remote deploy to vendored source builds
Move remote deployment to a vendored source bundle built on the target host via Docker so redeploys no longer require local cross-compilation or host Go installation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
205
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assethandler.go
generated
vendored
Normal file
205
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assethandler.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Debug(message string, args ...interface{})
|
||||
Error(message string, args ...interface{})
|
||||
}
|
||||
|
||||
//go:embed defaultindex.html
|
||||
var defaultHTML []byte
|
||||
|
||||
const (
|
||||
indexHTML = "index.html"
|
||||
)
|
||||
|
||||
type assetHandler struct {
|
||||
fs iofs.FS
|
||||
handler http.Handler
|
||||
|
||||
logger Logger
|
||||
|
||||
retryMissingFiles bool
|
||||
}
|
||||
|
||||
func NewAssetHandler(options assetserver.Options, log Logger) (http.Handler, error) {
|
||||
vfs := options.Assets
|
||||
if vfs != nil {
|
||||
if _, err := vfs.Open("."); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subDir, err := FindPathToFile(vfs, indexHTML)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
msg := "no `index.html` could be found in your Assets fs.FS"
|
||||
if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs {
|
||||
rootFolder, _ := FindEmbedRootPath(embedFs)
|
||||
msg += fmt.Sprintf(", please make sure the embedded directory '%s' is correct and contains your assets", rootFolder)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vfs, err = iofs.Sub(vfs, path.Clean(subDir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result http.Handler = &assetHandler{
|
||||
fs: vfs,
|
||||
handler: options.Handler,
|
||||
logger: log,
|
||||
}
|
||||
|
||||
if middleware := options.Middleware; middleware != nil {
|
||||
result = middleware(result)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *assetHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
url := req.URL.Path
|
||||
handler := d.handler
|
||||
if strings.EqualFold(req.Method, http.MethodGet) {
|
||||
filename := path.Clean(strings.TrimPrefix(url, "/"))
|
||||
|
||||
d.logDebug("Handling request '%s' (file='%s')", url, filename)
|
||||
if err := d.serveFSFile(rw, req, filename); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if handler != nil {
|
||||
d.logDebug("File '%s' not found, serving '%s' by AssetHandler", filename, url)
|
||||
handler.ServeHTTP(rw, req)
|
||||
err = nil
|
||||
} else {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
d.logError("Unable to handle request '%s': %s", url, err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
} else if handler != nil {
|
||||
d.logDebug("No GET request, serving '%s' by AssetHandler", url)
|
||||
handler.ServeHTTP(rw, req)
|
||||
} else {
|
||||
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
// serveFSFile will try to load the file from the fs.FS and write it to the response
|
||||
func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, filename string) error {
|
||||
if d.fs == nil {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
file, err := d.fs.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
statInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := req.URL.Path
|
||||
isDirectoryPath := url == "" || url[len(url)-1] == '/'
|
||||
if statInfo.IsDir() {
|
||||
if !isDirectoryPath {
|
||||
// If the URL doesn't end in a slash normally a http.redirect should be done, but that currently doesn't work on
|
||||
// WebKit WebViews (macOS/Linux).
|
||||
// So we handle this as a specific error
|
||||
return fmt.Errorf("a directory has been requested without a trailing slash, please add a trailing slash to your request")
|
||||
}
|
||||
|
||||
filename = path.Join(filename, indexHTML)
|
||||
|
||||
file, err = d.fs.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
statInfo, err = file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if isDirectoryPath {
|
||||
return fmt.Errorf("a file has been requested with a trailing slash, please remove the trailing slash from your request")
|
||||
}
|
||||
|
||||
var buf [512]byte
|
||||
var n int
|
||||
if _, haveType := rw.Header()[HeaderContentType]; !haveType {
|
||||
// Detect MimeType by sniffing the first 512 bytes
|
||||
n, err = file.Read(buf[:])
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do the custom MimeType sniffing even though http.ServeContent would do it in case
|
||||
// of an io.ReadSeeker. We would like to have a consistent behaviour in both cases.
|
||||
if contentType := GetMimetype(filename, buf[:n]); contentType != "" {
|
||||
rw.Header().Set(HeaderContentType, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
if fileSeeker, _ := file.(io.ReadSeeker); fileSeeker != nil {
|
||||
if _, err := fileSeeker.Seek(0, io.SeekStart); err != nil {
|
||||
return fmt.Errorf("seeker can't seek")
|
||||
}
|
||||
|
||||
http.ServeContent(rw, req, statInfo.Name(), statInfo.ModTime(), fileSeeker)
|
||||
return nil
|
||||
}
|
||||
|
||||
size := strconv.FormatInt(statInfo.Size(), 10)
|
||||
rw.Header().Set(HeaderContentLength, size)
|
||||
|
||||
// Write the first 512 bytes used for MimeType sniffing
|
||||
_, err = io.Copy(rw, bytes.NewReader(buf[:n]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the remaining content of the file
|
||||
_, err = io.Copy(rw, file)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *assetHandler) logDebug(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[AssetHandler] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *assetHandler) logError(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Error("[AssetHandler] "+message, args...)
|
||||
}
|
||||
}
|
||||
84
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assethandler_external.go
generated
vendored
Normal file
84
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assethandler_external.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func NewProxyServer(proxyURL string) http.Handler {
|
||||
parsedURL, err := url.Parse(proxyURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return httputil.NewSingleHostReverseProxy(parsedURL)
|
||||
}
|
||||
|
||||
func NewExternalAssetsHandler(logger Logger, options assetserver.Options, url *url.URL) http.Handler {
|
||||
baseHandler := options.Handler
|
||||
|
||||
errSkipProxy := fmt.Errorf("skip proxying")
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
baseDirector := proxy.Director
|
||||
proxy.Director = func(r *http.Request) {
|
||||
baseDirector(r)
|
||||
if logger != nil {
|
||||
logger.Debug("[ExternalAssetHandler] Loading '%s'", r.URL)
|
||||
}
|
||||
}
|
||||
|
||||
proxy.ModifyResponse = func(res *http.Response) error {
|
||||
if baseHandler == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if res.StatusCode == http.StatusSwitchingProtocols {
|
||||
return nil
|
||||
}
|
||||
|
||||
if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusMethodNotAllowed {
|
||||
return errSkipProxy
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) {
|
||||
if baseHandler != nil && errors.Is(err, errSkipProxy) {
|
||||
if logger != nil {
|
||||
logger.Debug("[ExternalAssetHandler] '%s' returned not found, using AssetHandler", r.URL)
|
||||
}
|
||||
baseHandler.ServeHTTP(rw, r)
|
||||
} else {
|
||||
if logger != nil {
|
||||
logger.Error("[ExternalAssetHandler] Proxy error: %v", err)
|
||||
}
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
}
|
||||
}
|
||||
|
||||
var result http.Handler = http.HandlerFunc(
|
||||
func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == http.MethodGet {
|
||||
proxy.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
if baseHandler != nil {
|
||||
baseHandler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||
})
|
||||
|
||||
if middleware := options.Middleware; middleware != nil {
|
||||
result = middleware(result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
255
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver.go
generated
vendored
Normal file
255
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver.go
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"html/template"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
||||
const (
|
||||
runtimeJSPath = "/wails/runtime.js"
|
||||
ipcJSPath = "/wails/ipc.js"
|
||||
runtimePath = "/wails/runtime"
|
||||
)
|
||||
|
||||
type RuntimeAssets interface {
|
||||
DesktopIPC() []byte
|
||||
WebsocketIPC() []byte
|
||||
RuntimeDesktopJS() []byte
|
||||
}
|
||||
|
||||
type RuntimeHandler interface {
|
||||
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type AssetServer struct {
|
||||
handler http.Handler
|
||||
runtimeJS []byte
|
||||
ipcJS func(*http.Request) []byte
|
||||
|
||||
logger Logger
|
||||
runtime RuntimeAssets
|
||||
|
||||
servingFromDisk bool
|
||||
appendSpinnerToBody bool
|
||||
|
||||
// Use http based runtime
|
||||
runtimeHandler RuntimeHandler
|
||||
|
||||
// plugin scripts
|
||||
pluginScripts map[string]string
|
||||
|
||||
assetServerWebView
|
||||
}
|
||||
|
||||
func NewAssetServerMainPage(bindingsJSON string, options *options.App, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
assetOptions, err := BuildAssetServerConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAssetServer(bindingsJSON, assetOptions, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServer(bindingsJSON string, options assetserver.Options, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
handler, err := NewAssetHandler(options, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewAssetServerWithHandler(handler, bindingsJSON, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServerWithHandler(handler http.Handler, bindingsJSON string, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
|
||||
var buffer bytes.Buffer
|
||||
if bindingsJSON != "" {
|
||||
escapedBindingsJSON := template.JSEscapeString(bindingsJSON)
|
||||
buffer.WriteString(`window.wailsbindings='` + escapedBindingsJSON + `';` + "\n")
|
||||
}
|
||||
buffer.Write(runtime.RuntimeDesktopJS())
|
||||
|
||||
result := &AssetServer{
|
||||
handler: handler,
|
||||
runtimeJS: buffer.Bytes(),
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached in dev mode.
|
||||
servingFromDisk: servingFromDisk,
|
||||
logger: logger,
|
||||
runtime: runtime,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) UseRuntimeHandler(handler RuntimeHandler) {
|
||||
d.runtimeHandler = handler
|
||||
}
|
||||
|
||||
func (d *AssetServer) AddPluginScript(pluginName string, script string) {
|
||||
if d.pluginScripts == nil {
|
||||
d.pluginScripts = make(map[string]string)
|
||||
}
|
||||
pluginName = strings.ReplaceAll(pluginName, "/", "_")
|
||||
pluginName = html.EscapeString(pluginName)
|
||||
pluginScriptName := fmt.Sprintf("/plugin_%s_%d.js", pluginName, rand.Intn(100000))
|
||||
d.pluginScripts[pluginScriptName] = script
|
||||
}
|
||||
|
||||
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if isWebSocket(req) {
|
||||
// WebSockets are not supported by the AssetServer
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if d.servingFromDisk {
|
||||
rw.Header().Add(HeaderCacheControl, "no-cache")
|
||||
}
|
||||
|
||||
handler := d.handler
|
||||
if req.Method != http.MethodGet {
|
||||
handler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
path := req.URL.Path
|
||||
if path == runtimeJSPath {
|
||||
d.writeBlob(rw, path, d.runtimeJS)
|
||||
} else if path == runtimePath && d.runtimeHandler != nil {
|
||||
d.runtimeHandler.HandleRuntimeCall(rw, req)
|
||||
} 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
|
||||
}
|
||||
|
||||
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:
|
||||
content, err := d.processIndexHTML(body.Bytes())
|
||||
if err != nil {
|
||||
d.serveError(rw, err, "Unable to processIndexHTML")
|
||||
return
|
||||
}
|
||||
d.writeBlob(rw, indexHTML, content)
|
||||
|
||||
case http.StatusNotFound:
|
||||
d.writeBlob(rw, indexHTML, defaultHTML)
|
||||
|
||||
default:
|
||||
rw.WriteHeader(code)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
handler.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) processIndexHTML(indexHTML []byte) ([]byte, error) {
|
||||
htmlNode, err := getHTMLNode(indexHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.appendSpinnerToBody {
|
||||
err = appendSpinnerToBody(htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := insertScriptInHead(htmlNode, runtimeJSPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := insertScriptInHead(htmlNode, ipcJSPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Inject plugins
|
||||
for scriptName := range d.pluginScripts {
|
||||
if err := insertScriptInHead(htmlNode, scriptName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err = html.Render(&buffer, htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) {
|
||||
err := serveFile(rw, filename, blob)
|
||||
if err != nil {
|
||||
d.serveError(rw, err, "Unable to write content %s", filename)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, args ...interface{}) {
|
||||
args = append(args, err)
|
||||
d.logError(msg+": %s", args...)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func (d *AssetServer) logDebug(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[AssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) logError(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Error("[AssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (AssetServer) isRuntimeInjectionMatch(path string) bool {
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
|
||||
return strings.HasSuffix(path, "/") ||
|
||||
strings.HasSuffix(path, "/"+indexHTML)
|
||||
}
|
||||
31
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver_dev.go
generated
vendored
Normal file
31
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver_dev.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build dev
|
||||
// +build dev
|
||||
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
The assetserver for the dev mode.
|
||||
Depending on the UserAgent it injects a websocket based IPC script into `index.html` or the default desktop IPC. The
|
||||
default desktop IPC is injected when the webview accesses the devserver.
|
||||
*/
|
||||
func NewDevAssetServer(handler http.Handler, bindingsJSON string, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
result, err := NewAssetServerWithHandler(handler, bindingsJSON, servingFromDisk, logger, runtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.appendSpinnerToBody = true
|
||||
result.ipcJS = func(req *http.Request) []byte {
|
||||
if strings.Contains(req.UserAgent(), WailsUserAgentValue) {
|
||||
return runtime.DesktopIPC()
|
||||
}
|
||||
return runtime.WebsocketIPC()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
185
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver_webview.go
generated
vendored
Normal file
185
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver_webview.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
||||
)
|
||||
|
||||
type assetServerWebView struct {
|
||||
// ExpectedWebViewHost is checked against the Request Host of every WebViewRequest, other hosts won't be processed.
|
||||
ExpectedWebViewHost string
|
||||
|
||||
dispatchInit sync.Once
|
||||
dispatchReqC chan<- webview.Request
|
||||
dispatchWorkers int
|
||||
}
|
||||
|
||||
// ServeWebViewRequest processes the HTTP Request asynchronously by faking a golang HTTP Server.
|
||||
// The request will be finished with a StatusNotImplemented code if no handler has written to the response.
|
||||
// The AssetServer takes ownership of the request and the caller mustn't close it or access it in any other way.
|
||||
func (d *AssetServer) ServeWebViewRequest(req webview.Request) {
|
||||
d.dispatchInit.Do(func() {
|
||||
workers := d.dispatchWorkers
|
||||
if workers <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
workerC := make(chan webview.Request, workers*2)
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
for req := range workerC {
|
||||
d.processWebViewRequest(req)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
dispatchC := make(chan webview.Request)
|
||||
go queueingDispatcher(50, dispatchC, workerC)
|
||||
|
||||
d.dispatchReqC = dispatchC
|
||||
})
|
||||
|
||||
if d.dispatchReqC == nil {
|
||||
go d.processWebViewRequest(req)
|
||||
} else {
|
||||
d.dispatchReqC <- req
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) processWebViewRequest(r webview.Request) {
|
||||
uri, _ := r.URL()
|
||||
d.processWebViewRequestInternal(r)
|
||||
if err := r.Close(); err != nil {
|
||||
d.logError("Unable to call close for request for uri '%s'", uri)
|
||||
}
|
||||
}
|
||||
|
||||
// processWebViewRequestInternal processes the HTTP Request by faking a golang HTTP Server.
|
||||
// The request will be finished with a StatusNotImplemented code if no handler has written to the response.
|
||||
func (d *AssetServer) processWebViewRequestInternal(r webview.Request) {
|
||||
uri := "unknown"
|
||||
var err error
|
||||
|
||||
wrw := r.Response()
|
||||
defer func() {
|
||||
if err := wrw.Finish(); err != nil {
|
||||
d.logError("Error finishing request '%s': %s", uri, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var rw http.ResponseWriter = &contentTypeSniffer{rw: wrw} // Make sure we have a Content-Type sniffer
|
||||
defer rw.WriteHeader(http.StatusNotImplemented) // This is a NOP when a handler has already written and set the status
|
||||
|
||||
uri, err = r.URL()
|
||||
if err != nil {
|
||||
d.logError("Error processing request, unable to get URL: %s (HttpResponse=500)", err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
method, err := r.Method()
|
||||
if err != nil {
|
||||
d.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Method: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
header, err := r.Header()
|
||||
if err != nil {
|
||||
d.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Header: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := r.Body()
|
||||
if err != nil {
|
||||
d.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Body: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
if body == nil {
|
||||
body = http.NoBody
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
req, err := http.NewRequest(method, uri, body)
|
||||
if err != nil {
|
||||
d.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// For server requests, the URL is parsed from the URI supplied on the Request-Line as stored in RequestURI. For
|
||||
// most requests, fields other than Path and RawQuery will be empty. (See RFC 7230, Section 5.3)
|
||||
req.URL.Scheme = ""
|
||||
req.URL.Host = ""
|
||||
req.URL.Fragment = ""
|
||||
req.URL.RawFragment = ""
|
||||
|
||||
if url := req.URL; req.RequestURI == "" && url != nil {
|
||||
req.RequestURI = url.String()
|
||||
}
|
||||
|
||||
req.Header = header
|
||||
|
||||
if req.RemoteAddr == "" {
|
||||
// 192.0.2.0/24 is "TEST-NET" in RFC 5737
|
||||
req.RemoteAddr = "192.0.2.1:1234"
|
||||
}
|
||||
|
||||
if req.ContentLength == 0 {
|
||||
req.ContentLength = -1
|
||||
} else {
|
||||
size := strconv.FormatInt(req.ContentLength, 10)
|
||||
req.Header.Set(HeaderContentLength, size)
|
||||
}
|
||||
|
||||
if host := req.Header.Get(HeaderHost); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
if expectedHost := d.ExpectedWebViewHost; expectedHost != "" && expectedHost != req.Host {
|
||||
d.webviewRequestErrorHandler(uri, rw, fmt.Errorf("expected host '%s' in request, but was '%s'", expectedHost, req.Host))
|
||||
return
|
||||
}
|
||||
|
||||
d.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func (d *AssetServer) webviewRequestErrorHandler(uri string, rw http.ResponseWriter, err error) {
|
||||
logInfo := uri
|
||||
if uri, err := url.ParseRequestURI(uri); err == nil {
|
||||
logInfo = strings.Replace(logInfo, fmt.Sprintf("%s://%s", uri.Scheme, uri.Host), "", 1)
|
||||
}
|
||||
|
||||
d.logError("Error processing request '%s': %s (HttpResponse=500)", logInfo, err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func queueingDispatcher[T any](minQueueSize uint, inC <-chan T, outC chan<- T) {
|
||||
q := newRingqueue[T](minQueueSize)
|
||||
for {
|
||||
in, ok := <-inC
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
q.Add(in)
|
||||
for q.Len() != 0 {
|
||||
out, _ := q.Peek()
|
||||
select {
|
||||
case outC <- out:
|
||||
q.Remove()
|
||||
case in, ok := <-inC:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
q.Add(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/body_recorder.go
generated
vendored
Normal file
61
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/body_recorder.go
generated
vendored
Normal 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
|
||||
}
|
||||
135
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/common.go
generated
vendored
Normal file
135
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/common.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func BuildAssetServerConfig(appOptions *options.App) (assetserver.Options, error) {
|
||||
var options assetserver.Options
|
||||
if opt := appOptions.AssetServer; opt != nil {
|
||||
if appOptions.Assets != nil || appOptions.AssetsHandler != nil {
|
||||
panic("It's not possible to use the deprecated Assets and AssetsHandler options and the new AssetServer option at the same time. Please migrate all your Assets options to the AssetServer option.")
|
||||
}
|
||||
|
||||
options = *opt
|
||||
} else {
|
||||
options = assetserver.Options{
|
||||
Assets: appOptions.Assets,
|
||||
Handler: appOptions.AssetsHandler,
|
||||
}
|
||||
}
|
||||
|
||||
return options, options.Validate()
|
||||
}
|
||||
|
||||
const (
|
||||
HeaderHost = "Host"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
HeaderCacheControl = "Cache-Control"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
|
||||
WailsUserAgentValue = "wails.io"
|
||||
)
|
||||
|
||||
func serveFile(rw http.ResponseWriter, filename string, blob []byte) error {
|
||||
header := rw.Header()
|
||||
header.Set(HeaderContentLength, strconv.Itoa(len(blob)))
|
||||
if mimeType := header.Get(HeaderContentType); mimeType == "" {
|
||||
mimeType = GetMimetype(filename, blob)
|
||||
header.Set(HeaderContentType, mimeType)
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err := io.Copy(rw, bytes.NewReader(blob))
|
||||
return err
|
||||
}
|
||||
|
||||
func createScriptNode(scriptName string) *html.Node {
|
||||
return &html.Node{
|
||||
Type: html.ElementNode,
|
||||
Data: "script",
|
||||
Attr: []html.Attribute{
|
||||
{
|
||||
Key: "src",
|
||||
Val: scriptName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createDivNode(id string) *html.Node {
|
||||
return &html.Node{
|
||||
Type: html.ElementNode,
|
||||
Data: "div",
|
||||
Attr: []html.Attribute{
|
||||
{
|
||||
Namespace: "",
|
||||
Key: "id",
|
||||
Val: id,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func insertScriptInHead(htmlNode *html.Node, scriptName string) error {
|
||||
headNode := findFirstTag(htmlNode, "head")
|
||||
if headNode == nil {
|
||||
return errors.New("cannot find head in HTML")
|
||||
}
|
||||
scriptNode := createScriptNode(scriptName)
|
||||
if headNode.FirstChild != nil {
|
||||
headNode.InsertBefore(scriptNode, headNode.FirstChild)
|
||||
} else {
|
||||
headNode.AppendChild(scriptNode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendSpinnerToBody(htmlNode *html.Node) error {
|
||||
bodyNode := findFirstTag(htmlNode, "body")
|
||||
if bodyNode == nil {
|
||||
return errors.New("cannot find body in HTML")
|
||||
}
|
||||
scriptNode := createDivNode("wails-spinner")
|
||||
bodyNode.AppendChild(scriptNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHTMLNode(htmldata []byte) (*html.Node, error) {
|
||||
return html.Parse(bytes.NewReader(htmldata))
|
||||
}
|
||||
|
||||
func findFirstTag(htmlnode *html.Node, tagName string) *html.Node {
|
||||
var extractor func(*html.Node) *html.Node
|
||||
var result *html.Node
|
||||
extractor = func(node *html.Node) *html.Node {
|
||||
if node.Type == html.ElementNode && node.Data == tagName {
|
||||
return node
|
||||
}
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
result := extractor(child)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result = extractor(htmlnode)
|
||||
return result
|
||||
}
|
||||
|
||||
func isWebSocket(req *http.Request) bool {
|
||||
upgrade := req.Header.Get(HeaderUpgrade)
|
||||
return strings.EqualFold(upgrade, "websocket")
|
||||
}
|
||||
42
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/content_type_sniffer.go
generated
vendored
Normal file
42
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/content_type_sniffer.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type contentTypeSniffer struct {
|
||||
rw http.ResponseWriter
|
||||
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) Header() http.Header {
|
||||
return rw.rw.Header()
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) Write(buf []byte) (int, error) {
|
||||
rw.writeHeader(buf)
|
||||
return rw.rw.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) WriteHeader(code int) {
|
||||
if rw.wroteHeader {
|
||||
return
|
||||
}
|
||||
|
||||
rw.rw.WriteHeader(code)
|
||||
rw.wroteHeader = true
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) writeHeader(b []byte) {
|
||||
if rw.wroteHeader {
|
||||
return
|
||||
}
|
||||
|
||||
m := rw.rw.Header()
|
||||
if _, hasType := m[HeaderContentType]; !hasType {
|
||||
m.Set(HeaderContentType, http.DetectContentType(b))
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
39
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/defaultindex.html
generated
vendored
Normal file
39
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/defaultindex.html
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
75
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/fs.go
generated
vendored
Normal file
75
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/fs.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FindEmbedRootPath finds the root path in the embed FS. It's the directory which contains all the files.
|
||||
func FindEmbedRootPath(fsys embed.FS) (string, error) {
|
||||
stopErr := fmt.Errorf("files or multiple dirs found")
|
||||
|
||||
fPath := ""
|
||||
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
fPath = path
|
||||
if entries, dErr := fs.ReadDir(fsys, path); dErr != nil {
|
||||
return dErr
|
||||
} else if len(entries) <= 1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return stopErr
|
||||
})
|
||||
|
||||
if err != nil && err != stopErr {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fPath, nil
|
||||
}
|
||||
|
||||
func FindPathToFile(fsys fs.FS, file string) (string, error) {
|
||||
stat, _ := fs.Stat(fsys, file)
|
||||
if stat != nil {
|
||||
return ".", nil
|
||||
}
|
||||
var indexFiles []string
|
||||
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(path, file) {
|
||||
indexFiles = append(indexFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(indexFiles) > 1 {
|
||||
selected := indexFiles[0]
|
||||
for _, f := range indexFiles {
|
||||
if len(f) < len(selected) {
|
||||
selected = f
|
||||
}
|
||||
}
|
||||
path, _ := filepath.Split(selected)
|
||||
return path, nil
|
||||
}
|
||||
if len(indexFiles) > 0 {
|
||||
path, _ := filepath.Split(indexFiles[0])
|
||||
return path, nil
|
||||
}
|
||||
return "", fmt.Errorf("%s: %w", file, os.ErrNotExist)
|
||||
}
|
||||
67
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/mimecache.go
generated
vendored
Normal file
67
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/mimecache.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/mimetype"
|
||||
)
|
||||
|
||||
var (
|
||||
mimeCache = map[string]string{}
|
||||
mimeMutex sync.Mutex
|
||||
|
||||
// The list of builtin mime-types by extension as defined by
|
||||
// the golang standard lib package "mime"
|
||||
// The standard lib also takes into account mime type definitions from
|
||||
// etc files like '/etc/apache2/mime.types' but we want to have the
|
||||
// same behavivour on all platforms and not depend on some external file.
|
||||
mimeTypesByExt = map[string]string{
|
||||
".avif": "image/avif",
|
||||
".css": "text/css; charset=utf-8",
|
||||
".gif": "image/gif",
|
||||
".htm": "text/html; charset=utf-8",
|
||||
".html": "text/html; charset=utf-8",
|
||||
".jpeg": "image/jpeg",
|
||||
".jpg": "image/jpeg",
|
||||
".js": "text/javascript; charset=utf-8",
|
||||
".json": "application/json",
|
||||
".mjs": "text/javascript; charset=utf-8",
|
||||
".pdf": "application/pdf",
|
||||
".png": "image/png",
|
||||
".svg": "image/svg+xml",
|
||||
".wasm": "application/wasm",
|
||||
".webp": "image/webp",
|
||||
".xml": "text/xml; charset=utf-8",
|
||||
}
|
||||
)
|
||||
|
||||
func GetMimetype(filename string, data []byte) string {
|
||||
mimeMutex.Lock()
|
||||
defer mimeMutex.Unlock()
|
||||
|
||||
result := mimeTypesByExt[filepath.Ext(filename)]
|
||||
if result != "" {
|
||||
return result
|
||||
}
|
||||
|
||||
result = mimeCache[filename]
|
||||
if result != "" {
|
||||
return result
|
||||
}
|
||||
|
||||
detect := mimetype.Detect(data)
|
||||
if detect == nil {
|
||||
result = http.DetectContentType(data)
|
||||
} else {
|
||||
result = detect.String()
|
||||
}
|
||||
|
||||
if result == "" {
|
||||
result = "application/octet-stream"
|
||||
}
|
||||
|
||||
mimeCache[filename] = result
|
||||
return result
|
||||
}
|
||||
101
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/ringqueue.go
generated
vendored
Normal file
101
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/ringqueue.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// Code from https://github.com/erikdubbelboer/ringqueue
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Erik Dubbelboer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package assetserver
|
||||
|
||||
type ringqueue[T any] struct {
|
||||
nodes []T
|
||||
head int
|
||||
tail int
|
||||
cnt int
|
||||
|
||||
minSize int
|
||||
}
|
||||
|
||||
func newRingqueue[T any](minSize uint) *ringqueue[T] {
|
||||
if minSize < 2 {
|
||||
minSize = 2
|
||||
}
|
||||
return &ringqueue[T]{
|
||||
nodes: make([]T, minSize),
|
||||
minSize: int(minSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) resize(n int) {
|
||||
nodes := make([]T, n)
|
||||
if q.head < q.tail {
|
||||
copy(nodes, q.nodes[q.head:q.tail])
|
||||
} else {
|
||||
copy(nodes, q.nodes[q.head:])
|
||||
copy(nodes[len(q.nodes)-q.head:], q.nodes[:q.tail])
|
||||
}
|
||||
|
||||
q.tail = q.cnt % n
|
||||
q.head = 0
|
||||
q.nodes = nodes
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Add(i T) {
|
||||
if q.cnt == len(q.nodes) {
|
||||
// Also tested a grow rate of 1.5, see: http://stackoverflow.com/questions/2269063/buffer-growth-strategy
|
||||
// In Go this resulted in a higher memory usage.
|
||||
q.resize(q.cnt * 2)
|
||||
}
|
||||
q.nodes[q.tail] = i
|
||||
q.tail = (q.tail + 1) % len(q.nodes)
|
||||
q.cnt++
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Peek() (T, bool) {
|
||||
if q.cnt == 0 {
|
||||
var none T
|
||||
return none, false
|
||||
}
|
||||
return q.nodes[q.head], true
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Remove() (T, bool) {
|
||||
if q.cnt == 0 {
|
||||
var none T
|
||||
return none, false
|
||||
}
|
||||
i := q.nodes[q.head]
|
||||
q.head = (q.head + 1) % len(q.nodes)
|
||||
q.cnt--
|
||||
|
||||
if n := len(q.nodes) / 2; n > q.minSize && q.cnt <= n {
|
||||
q.resize(n)
|
||||
}
|
||||
|
||||
return i, true
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Cap() int {
|
||||
return cap(q.nodes)
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Len() int {
|
||||
return q.cnt
|
||||
}
|
||||
17
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request.go
generated
vendored
Normal file
17
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Request interface {
|
||||
URL() (string, error)
|
||||
Method() (string, error)
|
||||
Header() (http.Header, error)
|
||||
Body() (io.ReadCloser, error)
|
||||
|
||||
Response() ResponseWriter
|
||||
|
||||
Close() error
|
||||
}
|
||||
251
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_darwin.go
generated
vendored
Normal file
251
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
//go:build darwin
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#include <string.h>
|
||||
|
||||
static void URLSchemeTaskRetain(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
[urlSchemeTask retain];
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRelease(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
[urlSchemeTask release];
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestURL(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.URL.absoluteString UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestMethod(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.HTTPMethod UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestHeadersJSON(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
NSData *headerData = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil];
|
||||
if (!headerData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString* headerString = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease];
|
||||
const char * headerJSON = [headerString UTF8String];
|
||||
|
||||
return strdup(headerJSON);
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyBytes(void *wkUrlSchemeTask, const void **body, int *bodyLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBody) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*body = urlSchemeTask.request.HTTPBody.bytes;
|
||||
*bodyLen = urlSchemeTask.request.HTTPBody.length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyStreamOpen(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream open];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRequestBodyStreamClose(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream close];
|
||||
}
|
||||
}
|
||||
|
||||
static int URLSchemeTaskRequestBodyStreamRead(void *wkUrlSchemeTask, void *buf, int bufLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
|
||||
@autoreleasepool {
|
||||
NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream;
|
||||
if (!stream) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
NSStreamStatus status = stream.streamStatus;
|
||||
if (status == NSStreamStatusAtEnd || !stream.hasBytesAvailable) {
|
||||
return 0;
|
||||
} else if (status != NSStreamStatusOpen) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return [stream read:buf maxLength:bufLen];
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest based on a pointer to an `id<WKURLSchemeTask>`
|
||||
func NewRequest(wkURLSchemeTask unsafe.Pointer) Request {
|
||||
C.URLSchemeTaskRetain(wkURLSchemeTask)
|
||||
return newRequestFinalizer(&request{task: wkURLSchemeTask})
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
task unsafe.Pointer
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestMethod(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
if cHeaders := C.URLSchemeTaskRequestHeadersJSON(r.task); cHeaders != nil {
|
||||
if headers := C.GoString(cHeaders); headers != "" {
|
||||
var h map[string]string
|
||||
if err := json.Unmarshal([]byte(headers), &h); err != nil {
|
||||
return nil, fmt.Errorf("unable to unmarshal request headers: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range h {
|
||||
header.Add(k, v)
|
||||
}
|
||||
}
|
||||
C.free(unsafe.Pointer(cHeaders))
|
||||
}
|
||||
r.header = header
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
var body unsafe.Pointer
|
||||
var bodyLen C.int
|
||||
if C.URLSchemeTaskRequestBodyBytes(r.task, &body, &bodyLen) {
|
||||
if body != nil && bodyLen > 0 {
|
||||
r.body = io.NopCloser(bytes.NewReader(C.GoBytes(body, bodyLen)))
|
||||
} else {
|
||||
r.body = http.NoBody
|
||||
}
|
||||
} else if C.URLSchemeTaskRequestBodyStreamOpen(r.task) {
|
||||
r.body = &requestBodyStreamReader{task: r.task}
|
||||
}
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{r: r}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
err = r.Response().Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.URLSchemeTaskRelease(r.task)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = &requestBodyStreamReader{}
|
||||
|
||||
type requestBodyStreamReader struct {
|
||||
task unsafe.Pointer
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *requestBodyStreamReader) Read(p []byte) (n int, err error) {
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if p != nil {
|
||||
content = unsafe.Pointer(&p[0])
|
||||
contentLen = len(p)
|
||||
}
|
||||
|
||||
res := C.URLSchemeTaskRequestBodyStreamRead(r.task, content, C.int(contentLen))
|
||||
if res > 0 {
|
||||
return int(res), nil
|
||||
}
|
||||
|
||||
switch res {
|
||||
case 0:
|
||||
return 0, io.EOF
|
||||
case -1:
|
||||
return 0, fmt.Errorf("body: stream error")
|
||||
case -2:
|
||||
return 0, fmt.Errorf("body: no stream defined")
|
||||
case -3:
|
||||
return 0, io.ErrClosedPipe
|
||||
default:
|
||||
return 0, fmt.Errorf("body: unknown error %d", res)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *requestBodyStreamReader) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
C.URLSchemeTaskRequestBodyStreamClose(r.task)
|
||||
return nil
|
||||
}
|
||||
40
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_finalizer.go
generated
vendored
Normal file
40
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_finalizer.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var _ Request = &requestFinalizer{}
|
||||
|
||||
type requestFinalizer struct {
|
||||
Request
|
||||
closed int32
|
||||
}
|
||||
|
||||
// newRequestFinalizer returns a request with a runtime finalizer to make sure it will be closed from the finalizer
|
||||
// if it has not been already closed.
|
||||
// It also makes sure Close() of the wrapping request is only called once.
|
||||
func newRequestFinalizer(r Request) Request {
|
||||
rf := &requestFinalizer{Request: r}
|
||||
// Make sure to async release since it might block the finalizer goroutine for a longer period
|
||||
runtime.SetFinalizer(rf, func(obj *requestFinalizer) { rf.close(true) })
|
||||
return rf
|
||||
}
|
||||
|
||||
func (r *requestFinalizer) Close() error {
|
||||
return r.close(false)
|
||||
}
|
||||
|
||||
func (r *requestFinalizer) close(asyncRelease bool) error {
|
||||
if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
|
||||
runtime.SetFinalizer(r, nil)
|
||||
if asyncRelease {
|
||||
go r.Request.Close()
|
||||
return nil
|
||||
} else {
|
||||
return r.Request.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
85
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_linux.go
generated
vendored
Normal file
85
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_linux.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 gio-unix-2.0
|
||||
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
|
||||
#cgo webkit2_41 pkg-config: webkit2gtk-4.1
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest`
|
||||
func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request {
|
||||
webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest)
|
||||
C.g_object_ref(C.gpointer(webkitReq))
|
||||
|
||||
req := &request{req: webkitReq}
|
||||
return newRequestFinalizer(req)
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return C.GoString(C.webkit_uri_scheme_request_get_uri(r.req)), nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return webkit_uri_scheme_request_get_http_method(r.req), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
r.header = webkit_uri_scheme_request_get_http_headers(r.req)
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
r.body = webkit_uri_scheme_request_get_http_body(r.req)
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r.req}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
r.Response().Finish()
|
||||
C.g_object_unref(C.gpointer(r.req))
|
||||
return err
|
||||
}
|
||||
217
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_windows.go
generated
vendored
Normal file
217
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/request_windows.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/go-webview2/pkg/edge"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest for chromium. This Method must be called from the Main-Thread!
|
||||
func NewRequest(env *edge.ICoreWebView2Environment, args *edge.ICoreWebView2WebResourceRequestedEventArgs, invokeSync func(fn func())) (Request, error) {
|
||||
req, err := args.GetRequest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRequest failed: %s", err)
|
||||
}
|
||||
defer req.Release()
|
||||
|
||||
r := &request{
|
||||
invokeSync: invokeSync,
|
||||
}
|
||||
|
||||
code := http.StatusInternalServerError
|
||||
r.response, err = env.CreateWebResourceResponse(nil, code, http.StatusText(code), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CreateWebResourceResponse failed: %s", err)
|
||||
}
|
||||
|
||||
if err := args.PutResponse(r.response); err != nil {
|
||||
r.finishResponse()
|
||||
return nil, fmt.Errorf("PutResponse failed: %s", err)
|
||||
}
|
||||
|
||||
r.deferral, err = args.GetDeferral()
|
||||
if err != nil {
|
||||
r.finishResponse()
|
||||
return nil, fmt.Errorf("GetDeferral failed: %s", err)
|
||||
}
|
||||
|
||||
r.url, r.urlErr = req.GetUri()
|
||||
r.method, r.methodErr = req.GetMethod()
|
||||
r.header, r.headerErr = getHeaders(req)
|
||||
|
||||
if content, err := req.GetContent(); err != nil {
|
||||
r.bodyErr = err
|
||||
} else if content != nil {
|
||||
// It is safe to access Content from another Thread: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#thread-safety
|
||||
r.body = &iStreamReleaseCloser{stream: content}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
response *edge.ICoreWebView2WebResourceResponse
|
||||
deferral *edge.ICoreWebView2Deferral
|
||||
|
||||
url string
|
||||
urlErr error
|
||||
|
||||
method string
|
||||
methodErr error
|
||||
|
||||
header http.Header
|
||||
headerErr error
|
||||
|
||||
body io.ReadCloser
|
||||
bodyErr error
|
||||
rw *responseWriter
|
||||
|
||||
invokeSync func(fn func())
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return r.url, r.urlErr
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return r.method, r.methodErr
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
return r.header, r.headerErr
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
return r.body, r.bodyErr
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var errs []error
|
||||
if r.body != nil {
|
||||
if err := r.body.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.body = nil
|
||||
}
|
||||
|
||||
if err := r.Response().Finish(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return combineErrs(errs)
|
||||
}
|
||||
|
||||
// finishResponse must be called on the main-thread
|
||||
func (r *request) finishResponse() error {
|
||||
var errs []error
|
||||
if r.response != nil {
|
||||
if err := r.response.Release(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.response = nil
|
||||
}
|
||||
if r.deferral != nil {
|
||||
if err := r.deferral.Complete(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := r.deferral.Release(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.deferral = nil
|
||||
}
|
||||
return combineErrs(errs)
|
||||
}
|
||||
|
||||
type iStreamReleaseCloser struct {
|
||||
stream *edge.IStream
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (i *iStreamReleaseCloser) Read(p []byte) (int, error) {
|
||||
if i.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
return i.stream.Read(p)
|
||||
}
|
||||
|
||||
func (i *iStreamReleaseCloser) Close() error {
|
||||
if i.closed {
|
||||
return nil
|
||||
}
|
||||
i.closed = true
|
||||
return i.stream.Release()
|
||||
}
|
||||
|
||||
func getHeaders(req *edge.ICoreWebView2WebResourceRequest) (http.Header, error) {
|
||||
header := http.Header{}
|
||||
headers, err := req.GetHeaders()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetHeaders Error: %s", err)
|
||||
}
|
||||
defer headers.Release()
|
||||
|
||||
headersIt, err := headers.GetIterator()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetIterator Error: %s", err)
|
||||
}
|
||||
defer headersIt.Release()
|
||||
|
||||
for {
|
||||
has, err := headersIt.HasCurrentHeader()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("HasCurrentHeader Error: %s", err)
|
||||
}
|
||||
if !has {
|
||||
break
|
||||
}
|
||||
|
||||
name, value, err := headersIt.GetCurrentHeader()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetCurrentHeader Error: %s", err)
|
||||
}
|
||||
|
||||
header.Set(name, value)
|
||||
if _, err := headersIt.MoveNext(); err != nil {
|
||||
return nil, fmt.Errorf("MoveNext Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other
|
||||
// requests including IPC calls.
|
||||
// So prevent 304 status codes by removing the headers that are used in combinationwith caching.
|
||||
header.Del("If-Modified-Since")
|
||||
header.Del("If-None-Match")
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func combineErrs(errs []error) error {
|
||||
// TODO use Go1.20 errors.Join
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
errStrings := make([]string, len(errs))
|
||||
for i, err := range errs {
|
||||
errStrings[i] = err.Error()
|
||||
}
|
||||
|
||||
return fmt.Errorf(strings.Join(errStrings, "\n"))
|
||||
}
|
||||
25
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter.go
generated
vendored
Normal file
25
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentType = "Content-Type"
|
||||
)
|
||||
|
||||
var (
|
||||
errRequestStopped = errors.New("request has been stopped")
|
||||
errResponseFinished = errors.New("response has been finished")
|
||||
)
|
||||
|
||||
// A ResponseWriter interface is used by an HTTP handler to
|
||||
// construct an HTTP response for the WebView.
|
||||
type ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
|
||||
// Finish the response and flush all data. A Finish after the request has already been finished has no effect.
|
||||
Finish() error
|
||||
}
|
||||
164
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_darwin.go
generated
vendored
Normal file
164
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
//go:build darwin
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>);
|
||||
|
||||
static bool urlSchemeTaskCall(void *wkUrlSchemeTask, schemeTaskCaller fn) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
if (urlSchemeTask == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
@try {
|
||||
fn(urlSchemeTask);
|
||||
} @catch (NSException *exception) {
|
||||
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
||||
// But it seems to be very tricky to not deadlock when keeping a lock curing executing fn()
|
||||
// It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want
|
||||
// to get the lock again to start another request or stop it.
|
||||
if ([exception.reason isEqualToString: @"This task has already been stopped"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveData(void *wkUrlSchemeTask, void* data, int datalength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
[urlSchemeTask didReceiveData:nsdata];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidFinish(void *wkUrlSchemeTask) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
[urlSchemeTask didFinish];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveResponse(void *wkUrlSchemeTask, int statusCode, void *headersString, int headersStringLength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength];
|
||||
NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData:nsHeadersJSON options: NSJSONReadingMutableContainers error: nil];
|
||||
NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease];
|
||||
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
});
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ ResponseWriter = &responseWriter{}
|
||||
|
||||
type responseWriter struct {
|
||||
r *request
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
var contentLen int
|
||||
if buf != nil {
|
||||
contentLen = len(buf)
|
||||
}
|
||||
|
||||
if contentLen > 0 {
|
||||
// Create a C array to hold the data
|
||||
cBuf := C.malloc(C.size_t(contentLen))
|
||||
if cBuf == nil {
|
||||
return 0, fmt.Errorf("memory allocation failed for %d bytes", contentLen)
|
||||
}
|
||||
defer C.free(cBuf)
|
||||
|
||||
// Copy the Go slice to the C array
|
||||
C.memcpy(cBuf, unsafe.Pointer(&buf[0]), C.size_t(contentLen))
|
||||
|
||||
if !C.URLSchemeTaskDidReceiveData(rw.r.task, cBuf, C.int(contentLen)) {
|
||||
return 0, errRequestStopped
|
||||
}
|
||||
} else {
|
||||
if !C.URLSchemeTaskDidReceiveData(rw.r.task, nil, 0) {
|
||||
return 0, errRequestStopped
|
||||
}
|
||||
}
|
||||
|
||||
return contentLen, nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
header := map[string]string{}
|
||||
for k := range rw.Header() {
|
||||
header[k] = rw.Header().Get(k)
|
||||
}
|
||||
headerData, _ := json.Marshal(header)
|
||||
|
||||
var headers unsafe.Pointer
|
||||
var headersLen int
|
||||
if len(headerData) != 0 {
|
||||
headers = unsafe.Pointer(&headerData[0])
|
||||
headersLen = len(headerData)
|
||||
}
|
||||
|
||||
C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen))
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
|
||||
C.URLSchemeTaskDidFinish(rw.r.task)
|
||||
return nil
|
||||
}
|
||||
132
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_linux.go
generated
vendored
Normal file
132
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_linux.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 gio-unix-2.0
|
||||
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
|
||||
#cgo webkit2_41 pkg-config: webkit2gtk-4.1
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include "gio/gunixinputstream.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type responseWriter struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
finished bool
|
||||
|
||||
w io.WriteCloser
|
||||
wErr error
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
if rw.wErr != nil {
|
||||
return 0, rw.wErr
|
||||
}
|
||||
return rw.w.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
contentLength := int64(-1)
|
||||
if sLen := rw.Header().Get(HeaderContentLength); sLen != "" {
|
||||
if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 {
|
||||
contentLength = pLen
|
||||
}
|
||||
}
|
||||
|
||||
// We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the
|
||||
// read FD is given to the InputStream and will be closed there.
|
||||
// Furthermore we especially don't want to have the FD_CLOEXEC
|
||||
rFD, w, err := pipe()
|
||||
if err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to open pipe: %s", err))
|
||||
return
|
||||
}
|
||||
rw.w = w
|
||||
|
||||
stream := C.g_unix_input_stream_new(C.int(rFD), C.gboolean(1))
|
||||
defer C.g_object_unref(C.gpointer(stream))
|
||||
|
||||
if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), stream, contentLength); err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) finishWithError(code int, err error) {
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
rw.w = &nopCloser{io.Discard}
|
||||
}
|
||||
rw.wErr = err
|
||||
|
||||
msg := C.CString(err.Error())
|
||||
gerr := C.g_error_new_literal(C.g_quark_from_string(msg), C.int(code), msg)
|
||||
C.webkit_uri_scheme_request_finish_error(rw.req, gerr)
|
||||
C.g_error_free(gerr)
|
||||
C.free(unsafe.Pointer(msg))
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func pipe() (r int, w *os.File, err error) {
|
||||
var p [2]int
|
||||
e := syscall.Pipe2(p[0:], 0)
|
||||
if e != nil {
|
||||
return 0, nil, fmt.Errorf("pipe2: %s", e)
|
||||
}
|
||||
|
||||
return p[0], os.NewFile(uintptr(p[1]), "|1"), nil
|
||||
}
|
||||
105
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_windows.go
generated
vendored
Normal file
105
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/responsewriter_windows.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ http.ResponseWriter = &responseWriter{}
|
||||
|
||||
type responseWriter struct {
|
||||
req *request
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
code int
|
||||
body *bytes.Buffer
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
return rw.body.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
if rw.body == nil {
|
||||
rw.body = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
rw.code = code
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
|
||||
var errs []error
|
||||
|
||||
code := rw.code
|
||||
if code == http.StatusNotModified {
|
||||
// WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other
|
||||
// requests including IPC calls.
|
||||
errs = append(errs, fmt.Errorf("AssetServer returned 304 - StatusNotModified which are going to hang WebView2, changed code to 505 - StatusInternalServerError"))
|
||||
code = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
rw.req.invokeSync(func() {
|
||||
resp := rw.req.response
|
||||
|
||||
hdrs, err := resp.GetHeaders()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.GetHeaders failed: %s", err))
|
||||
} else {
|
||||
for k, v := range rw.header {
|
||||
if err := hdrs.AppendHeader(k, strings.Join(v, ",")); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.AppendHeader failed: %s", err))
|
||||
}
|
||||
}
|
||||
hdrs.Release()
|
||||
}
|
||||
|
||||
if err := resp.PutStatusCode(code); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.PutStatusCode failed: %s", err))
|
||||
}
|
||||
|
||||
if err := resp.PutByteContent(rw.body.Bytes()); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.PutByteContent failed: %s", err))
|
||||
}
|
||||
|
||||
if err := rw.req.finishResponse(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.finishResponse failed: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return combineErrs(errs)
|
||||
}
|
||||
71
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_36+.go
generated
vendored
Normal file
71
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_36+.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//go:build linux && (webkit2_36 || webkit2_40 || webkit2_41 )
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0
|
||||
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0 libsoup-2.4
|
||||
#cgo webkit2_41 pkg-config: webkit2gtk-4.1 libsoup-3.0
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include "libsoup/soup.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string {
|
||||
method := C.GoString(C.webkit_uri_scheme_request_get_http_method(req))
|
||||
return strings.ToUpper(method)
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(req *C.WebKitURISchemeRequest) http.Header {
|
||||
hdrs := C.webkit_uri_scheme_request_get_http_headers(req)
|
||||
|
||||
var iter C.SoupMessageHeadersIter
|
||||
C.soup_message_headers_iter_init(&iter, hdrs)
|
||||
|
||||
var name *C.char
|
||||
var value *C.char
|
||||
|
||||
h := http.Header{}
|
||||
for C.soup_message_headers_iter_next(&iter, &name, &value) != 0 {
|
||||
h.Add(C.GoString(name), C.GoString(value))
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, stream *C.GInputStream, streamLength int64) error {
|
||||
resp := C.webkit_uri_scheme_response_new(stream, C.gint64(streamLength))
|
||||
defer C.g_object_unref(C.gpointer(resp))
|
||||
|
||||
cReason := C.CString(http.StatusText(code))
|
||||
C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason)
|
||||
C.free(unsafe.Pointer(cReason))
|
||||
|
||||
cMimeType := C.CString(header.Get(HeaderContentType))
|
||||
C.webkit_uri_scheme_response_set_content_type(resp, cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
|
||||
hdrs := C.soup_message_headers_new(C.SOUP_MESSAGE_HEADERS_RESPONSE)
|
||||
for name, values := range header {
|
||||
cName := C.CString(name)
|
||||
for _, value := range values {
|
||||
cValue := C.CString(value)
|
||||
C.soup_message_headers_append(hdrs, cName, cValue)
|
||||
C.free(unsafe.Pointer(cValue))
|
||||
}
|
||||
C.free(unsafe.Pointer(cName))
|
||||
}
|
||||
|
||||
C.webkit_uri_scheme_response_set_http_headers(resp, hdrs)
|
||||
|
||||
C.webkit_uri_scheme_request_finish_with_response(req, resp)
|
||||
return nil
|
||||
}
|
||||
21
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_36.go
generated
vendored
Normal file
21
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_36.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
//go:build linux && webkit2_36
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: webkit2gtk-4.0
|
||||
|
||||
#include "webkit2/webkit2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const Webkit2MinMinorVersion = 36
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(_ *C.WebKitURISchemeRequest) io.ReadCloser {
|
||||
return http.NoBody
|
||||
}
|
||||
85
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_40+.go
generated
vendored
Normal file
85
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_40+.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
//go:build linux && (webkit2_40 || webkit2_41)
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 gio-unix-2.0
|
||||
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
|
||||
#cgo webkit2_41 pkg-config: webkit2gtk-4.1
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include "gio/gunixinputstream.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(req *C.WebKitURISchemeRequest) io.ReadCloser {
|
||||
stream := C.webkit_uri_scheme_request_get_http_body(req)
|
||||
if stream == nil {
|
||||
return http.NoBody
|
||||
}
|
||||
return &webkitRequestBody{stream: stream}
|
||||
}
|
||||
|
||||
type webkitRequestBody struct {
|
||||
stream *C.GInputStream
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *webkitRequestBody) Read(p []byte) (int, error) {
|
||||
if r.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if p != nil {
|
||||
content = unsafe.Pointer(&p[0])
|
||||
contentLen = len(p)
|
||||
}
|
||||
|
||||
var n C.gsize
|
||||
var gErr *C.GError
|
||||
res := C.g_input_stream_read_all(r.stream, content, C.gsize(contentLen), &n, nil, &gErr)
|
||||
if res == 0 {
|
||||
return 0, formatGError("stream read failed", gErr)
|
||||
} else if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
func (r *webkitRequestBody) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
// https://docs.gtk.org/gio/method.InputStream.close.html
|
||||
// Streams will be automatically closed when the last reference is dropped, but you might want to call this function
|
||||
// to make sure resources are released as early as possible.
|
||||
var err error
|
||||
var gErr *C.GError
|
||||
if C.g_input_stream_close(r.stream, nil, &gErr) == 0 {
|
||||
err = formatGError("stream close failed", gErr)
|
||||
}
|
||||
C.g_object_unref(C.gpointer(r.stream))
|
||||
r.stream = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func formatGError(msg string, gErr *C.GError, args ...any) error {
|
||||
if gErr != nil && gErr.message != nil {
|
||||
msg += ": " + C.GoString(gErr.message)
|
||||
C.g_error_free(gErr)
|
||||
}
|
||||
return fmt.Errorf(msg, args...)
|
||||
}
|
||||
5
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_40.go
generated
vendored
Normal file
5
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_40.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build linux && webkit2_40
|
||||
|
||||
package webview
|
||||
|
||||
const Webkit2MinMinorVersion = 40
|
||||
5
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_41.go
generated
vendored
Normal file
5
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_41.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build linux && webkit2_41
|
||||
|
||||
package webview
|
||||
|
||||
const Webkit2MinMinorVersion = 41
|
||||
48
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_legacy.go
generated
vendored
Normal file
48
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/webview/webkit2_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build linux && !(webkit2_36 || webkit2_40 || webkit2_41)
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const Webkit2MinMinorVersion = 0
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(_ *C.WebKitURISchemeRequest) string {
|
||||
return http.MethodGet
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(_ *C.WebKitURISchemeRequest) http.Header {
|
||||
// Fake some basic default headers that are needed if e.g. request are being proxied to the an external sever, like
|
||||
// we do in the devserver.
|
||||
h := http.Header{}
|
||||
h.Add("Accept", "*/*")
|
||||
h.Add("User-Agent", "wails.io/605.1.15")
|
||||
return h
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(_ *C.WebKitURISchemeRequest) io.ReadCloser {
|
||||
return http.NoBody
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, stream *C.GInputStream, streamLength int64) error {
|
||||
if code != http.StatusOK {
|
||||
return fmt.Errorf("StatusCodes not supported: %d - %s", code, http.StatusText(code))
|
||||
}
|
||||
|
||||
cMimeType := C.CString(header.Get(HeaderContentType))
|
||||
C.webkit_uri_scheme_request_finish(req, stream, C.gint64(streamLength), cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user