diff --git a/v3/go.mod b/v3/go.mod index 7a0037950..68cb02806 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -24,13 +24,14 @@ require ( github.com/matryer/is v1.4.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 - github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 github.com/pterm/pterm v0.12.51 github.com/samber/lo v1.38.1 github.com/tc-hib/winres v0.1.6 - github.com/wailsapp/go-webview2 v1.0.6-0.20230901120557-e959fdf1ccc3 + github.com/wailsapp/go-webview2 v1.0.7 github.com/wailsapp/mimetype v1.4.1 + github.com/wailsapp/wails/v2 v2.6.0 golang.org/x/net v0.10.0 golang.org/x/sys v0.11.0 modernc.org/sqlite v1.21.0 @@ -74,10 +75,10 @@ require ( github.com/sergi/go-diff v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/crypto v0.1.0 // indirect + golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/image v0.5.0 // indirect - golang.org/x/mod v0.11.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/term v0.11.0 // indirect diff --git a/v3/go.sum b/v3/go.sum index 60bd79709..f3e550e99 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -278,8 +278,8 @@ github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkF github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -338,8 +338,12 @@ github.com/tmclane/purego v0.0.0-20230818202843-0b72c8c9140f h1:/HXk9aFXP97CJRzO github.com/tmclane/purego v0.0.0-20230818202843-0b72c8c9140f/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/wailsapp/go-webview2 v1.0.6-0.20230901120557-e959fdf1ccc3 h1:lN7ATT1NZrwKjn2F/VSRJxJeWQZvuypzRHeLwr2No4Q= github.com/wailsapp/go-webview2 v1.0.6-0.20230901120557-e959fdf1ccc3/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= +github.com/wailsapp/go-webview2 v1.0.7 h1:s95+7irJsAsTy1RsjJ6N0cYX7tZ4gP7Uzawds0L2urs= +github.com/wailsapp/go-webview2 v1.0.7/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= +github.com/wailsapp/wails/v2 v2.6.0 h1:EyH0zR/EO6dDiqNy8qU5spaXDfkluiq77xrkabPYD4c= +github.com/wailsapp/wails/v2 v2.6.0/go.mod h1:WBG9KKWuw0FKfoepBrr/vRlyTmHaMibWesK3yz6nNiM= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= @@ -364,8 +368,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -403,8 +407,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/v3/internal/assetserver/assetserver_legacy.go b/v3/internal/assetserver/assetserver_legacy.go deleted file mode 100644 index 486ac0132..000000000 --- a/v3/internal/assetserver/assetserver_legacy.go +++ /dev/null @@ -1,84 +0,0 @@ -package assetserver - -import ( - "io" - "net/http" - "net/http/httptest" - - "github.com/wailsapp/wails/v3/internal/assetserver/webview" -) - -// ProcessHTTPRequest 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) ProcessHTTPRequestLegacy(rw http.ResponseWriter, reqGetter func() (*http.Request, error)) { - d.processWebViewRequest(&legacyRequest{reqGetter: reqGetter, rw: rw}) -} - -type legacyRequest struct { - req *http.Request - rw http.ResponseWriter - - reqGetter func() (*http.Request, error) -} - -func (r *legacyRequest) URL() (string, error) { - req, err := r.request() - if err != nil { - return "", err - } - return req.URL.String(), nil -} - -func (r *legacyRequest) Method() (string, error) { - req, err := r.request() - if err != nil { - return "", err - } - return req.Method, nil -} - -func (r *legacyRequest) Header() (http.Header, error) { - req, err := r.request() - if err != nil { - return nil, err - } - return req.Header, nil -} - -func (r *legacyRequest) Body() (io.ReadCloser, error) { - req, err := r.request() - if err != nil { - return nil, err - } - return req.Body, nil -} - -func (r legacyRequest) Response() webview.ResponseWriter { - return &legacyRequestNoOpCloserResponseWriter{ResponseWriter: r.rw} -} - -func (r legacyRequest) Close() error { return nil } - -func (r *legacyRequest) request() (*http.Request, error) { - if r.req != nil { - return r.req, nil - } - - req, err := r.reqGetter() - if err != nil { - return nil, err - } - r.req = req - return req, nil -} - -type legacyRequestNoOpCloserResponseWriter struct { - http.ResponseWriter - code int -} - -func (*legacyRequestNoOpCloserResponseWriter) Finish() {} - -func (r *legacyRequestNoOpCloserResponseWriter) Code() int { - return r.ResponseWriter.(*httptest.ResponseRecorder).Code -} diff --git a/v3/internal/assetserver/assetserver_webview.go b/v3/internal/assetserver/assetserver_webview.go index a039f992d..66bc9597a 100644 --- a/v3/internal/assetserver/assetserver_webview.go +++ b/v3/internal/assetserver/assetserver_webview.go @@ -26,19 +26,15 @@ type assetServerWebView struct { func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchInit.Do(func() { workers := d.dispatchWorkers - if workers == 0 { - workers = 10 + if workers <= 0 { + return } workerC := make(chan webview.Request, workers*2) for i := 0; i < workers; i++ { go func() { for req := range workerC { - uri, _ := req.URL() d.processWebViewRequest(req) - if err := req.Close(); err != nil { - d.logError("Unable to call close for request for uri '%s'", uri) - } } }() } @@ -49,21 +45,40 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchReqC = dispatchC }) - d.dispatchReqC <- req + 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) + } } // processHTTPRequest 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) processWebViewRequest(r webview.Request) { +func (d *AssetServer) processWebViewRequestInternal(r webview.Request) { + uri := "unknown" + var err error + wrw := r.Response() - defer wrw.Finish() + 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() + uri, err = r.URL() if err != nil { - d.logError("Error processing request, unable to get URL (HttpResponse=500)", "error", err.Error()) + d.logError("Error processing request, unable to get URL: %s (HttpResponse=500)", err) http.Error(rw, err.Error(), http.StatusInternalServerError) return } diff --git a/v3/internal/assetserver/webview/request_windows.go b/v3/internal/assetserver/webview/request_windows.go new file mode 100644 index 000000000..77c51727a --- /dev/null +++ b/v3/internal/assetserver/webview/request_windows.go @@ -0,0 +1,216 @@ +//go: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")) +} diff --git a/v3/internal/assetserver/webview/responsewriter.go b/v3/internal/assetserver/webview/responsewriter.go index 988a65c5f..2fc7ede51 100644 --- a/v3/internal/assetserver/webview/responsewriter.go +++ b/v3/internal/assetserver/webview/responsewriter.go @@ -21,7 +21,7 @@ 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() + Finish() error // Code returns the HTTP status code of the response Code() int diff --git a/v3/internal/assetserver/webview/responsewriter_darwin.go b/v3/internal/assetserver/webview/responsewriter_darwin.go index cb2e606c3..499ae6f9f 100644 --- a/v3/internal/assetserver/webview/responsewriter_darwin.go +++ b/v3/internal/assetserver/webview/responsewriter_darwin.go @@ -135,17 +135,19 @@ func (rw *responseWriter) WriteHeader(code int) { C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen)) } -func (rw *responseWriter) Finish() { +func (rw *responseWriter) Finish() error { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return + return nil } rw.finished = true C.URLSchemeTaskDidFinish(rw.r.task) + + return nil } func (rw *responseWriter) Code() int { diff --git a/v3/internal/assetserver/webview/responsewriter_linux.go b/v3/internal/assetserver/webview/responsewriter_linux.go index 9b4c54472..8d93b6388 100644 --- a/v3/internal/assetserver/webview/responsewriter_linux.go +++ b/v3/internal/assetserver/webview/responsewriter_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package webview @@ -90,18 +89,19 @@ func (rw *responseWriter) WriteHeader(code int) { } } -func (rw *responseWriter) Finish() { +func (rw *responseWriter) Finish() error { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return + return nil } rw.finished = true if rw.w != nil { rw.w.Close() } + return nil } func (rw *responseWriter) finishWithError(code int, err error) { diff --git a/v3/internal/assetserver/webview/responsewriter_windows.go b/v3/internal/assetserver/webview/responsewriter_windows.go new file mode 100644 index 000000000..09bec236f --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter_windows.go @@ -0,0 +1,108 @@ +//go: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) +} + +func (rw *responseWriter) Code() int { + return rw.code +} diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 1c6c4351a..e550a2304 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -8,10 +8,10 @@ import ( "github.com/bep/debounce" "github.com/wailsapp/go-webview2/webviewloader" "github.com/wailsapp/wails/v3/internal/assetserver" + "github.com/wailsapp/wails/v3/internal/assetserver/webview" "github.com/wailsapp/wails/v3/internal/capabilities" "io" "net/http" - "net/http/httptest" "net/url" "path" "strconv" @@ -1306,13 +1306,7 @@ func (i *iStreamReleaseCloser) Close() error { } func (w *windowsWebviewWindow) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) { - /* - webviewRequests <- &webViewAssetRequest{ - Request: webview.NewRequest(wkUrlSchemeTask), - windowId: uint(windowID), - windowName: globalApplication.getWindowForID(uint(windowID)).Name(), - } - */ + // Setting the UserAgent on the CoreWebView2Settings clears the whole default UserAgent of the Edge browser, but // we want to just append our ApplicationIdentifier. So we adjust the UserAgent for every request. if reqHeaders, err := req.GetHeaders(); err == nil { @@ -1344,36 +1338,23 @@ func (w *windowsWebviewWindow) processRequest(req *edge.ICoreWebView2WebResource return } - rw := httptest.NewRecorder() - globalApplication.assets.ProcessHTTPRequestLegacy(rw, coreWebview2RequestToHttpRequest(req)) - - headers := []string{} - for k, v := range rw.Header() { - headers = append(headers, fmt.Sprintf("%s: %s", k, strings.Join(v, ","))) - } - - 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. - globalApplication.error("AssetServer returned 304 - StatusNotModified which is going to hang WebView2, changed code to 505 - StatusInternalServerError", "uri", uri) - code = http.StatusInternalServerError - } - - env := w.chromium.Environment() - response, err := env.CreateWebResourceResponse(rw.Body.Bytes(), code, http.StatusText(code), strings.Join(headers, "\n")) + webviewRequest, err := webview.NewRequest( + w.chromium.Environment(), + args, + func(fn func()) { + InvokeSync(fn) + }) if err != nil { - globalApplication.error("CreateWebResourceResponse Error: " + err.Error()) + globalApplication.error("%s: NewRequest failed: %s", uri, err) return } - defer response.Release() - // Send response back - err = args.PutResponse(response) - if err != nil { - globalApplication.error("PutResponse Error: " + err.Error()) - return + webviewRequests <- &webViewAssetRequest{ + Request: webviewRequest, + windowId: uint(windowID), + windowName: globalApplication.getWindowForID(uint(windowID)).Name(), } + //globalApplication.assets.ServeWebViewRequest(webviewRequest) } func (w *windowsWebviewWindow) setupChromium() {