From db527c0fc29eb24b9d17c0cd7c0b98ed8f33d3d3 Mon Sep 17 00:00:00 2001 From: stffabi Date: Fri, 31 Mar 2023 10:08:31 +0200 Subject: [PATCH] [assetserver] Add linux platform implementation (#2543) --- .../frontend/desktop/linux/frontend.go | 49 ++--------- v2/internal/frontend/desktop/linux/webkit2.go | 6 +- v2/pkg/assetserver/webview/request_linux.go | 85 +++++++++++++++++++ v2/pkg/assetserver/webview/responsewriter.go | 11 +++ .../webview/responsewriter_darwin.go | 6 -- .../webview/responsewriter_linux.go} | 36 +++++--- .../assetserver/webview}/webkit2_36.go | 8 +- .../assetserver/webview}/webkit2_legacy.go | 8 +- 8 files changed, 136 insertions(+), 73 deletions(-) create mode 100644 v2/pkg/assetserver/webview/request_linux.go rename v2/{internal/frontend/desktop/linux/webkit2_responsewriter.go => pkg/assetserver/webview/responsewriter_linux.go} (74%) rename v2/{internal/frontend/desktop/linux => pkg/assetserver/webview}/webkit2_36.go (91%) rename v2/{internal/frontend/desktop/linux => pkg/assetserver/webview}/webkit2_legacy.go (83%) diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 07d270f13..072940d3a 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -78,7 +78,6 @@ import ( "encoding/json" "fmt" "log" - "net/http" "net/url" "os" "runtime" @@ -88,6 +87,7 @@ import ( "unsafe" "github.com/wailsapp/wails/v2/pkg/assetserver" + "github.com/wailsapp/wails/v2/pkg/assetserver/webview" "github.com/wailsapp/wails/v2/internal/binding" "github.com/wailsapp/wails/v2/internal/frontend" @@ -166,10 +166,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. } result.assets = assets - // Start 10 processors to handle requests in parallel - for i := 0; i < 10; i++ { - go result.startRequestProcessor() - } + go result.startRequestProcessor() } go result.startMessageProcessor() @@ -461,50 +458,16 @@ func processMessage(message *C.char) { messageBuffer <- goMessage } -var requestBuffer = make(chan unsafe.Pointer, 100) +var requestBuffer = make(chan webview.Request, 100) func (f *Frontend) startRequestProcessor() { for request := range requestBuffer { - f.processRequest(request) - C.g_object_unref(C.gpointer(request)) + f.assets.ServeWebViewRequest(request) + request.Release() } } //export processURLRequest func processURLRequest(request unsafe.Pointer) { - // Increment reference counter to allow async processing, will be decremented after the processing - // has been finished by a worker. - C.g_object_ref(C.gpointer(request)) - requestBuffer <- request -} - -func (f *Frontend) processRequest(request unsafe.Pointer) { - req := (*C.WebKitURISchemeRequest)(request) - uri := C.webkit_uri_scheme_request_get_uri(req) - goURI := C.GoString(uri) - - rw := &webKitResponseWriter{req: req} - defer rw.Close() - - f.assets.ProcessHTTPRequestLegacy( - rw, - func() (*http.Request, error) { - method := webkit_uri_scheme_request_get_http_method(req) - r, err := http.NewRequest(method, goURI, nil) - if err != nil { - return nil, err - } - r.Header = webkit_uri_scheme_request_get_http_headers(req) - - if r.URL.Host != f.startURL.Host { - if r.Body != nil { - r.Body.Close() - } - - return nil, fmt.Errorf("Expected host '%s' in request, but was '%s'", f.startURL.Host, r.URL.Host) - } - - return r, nil - }) - + requestBuffer <- webview.NewRequest(request) } diff --git a/v2/internal/frontend/desktop/linux/webkit2.go b/v2/internal/frontend/desktop/linux/webkit2.go index 9d64104c4..843b72604 100644 --- a/v2/internal/frontend/desktop/linux/webkit2.go +++ b/v2/internal/frontend/desktop/linux/webkit2.go @@ -12,10 +12,12 @@ import ( "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/linux" + + "github.com/wailsapp/wails/v2/pkg/assetserver/webview" ) func validateWebKit2Version(options *options.App) { - if C.webkit_get_major_version() == 2 && C.webkit_get_minor_version() >= webkit2MinMinorVersion { + if C.webkit_get_major_version() == 2 && C.webkit_get_minor_version() >= webview.Webkit2MinMinorVersion { return } @@ -24,6 +26,6 @@ func validateWebKit2Version(options *options.App) { msg = options.Linux.Messages } - v := fmt.Sprintf("2.%d.0", webkit2MinMinorVersion) + v := fmt.Sprintf("2.%d.0", webview.Webkit2MinMinorVersion) showModalDialogAndExit("WebKit2GTK", fmt.Sprintf(msg.WebKit2GTKMinRequired, v)) } diff --git a/v2/pkg/assetserver/webview/request_linux.go b/v2/pkg/assetserver/webview/request_linux.go new file mode 100644 index 000000000..b5a57fdc5 --- /dev/null +++ b/v2/pkg/assetserver/webview/request_linux.go @@ -0,0 +1,85 @@ +//go:build linux +// +build linux + +package webview + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 gio-unix-2.0 + +#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` +// +// Please make sure to call Release() when finished using the request. +func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request { + webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest) + req := &request{req: webkitReq} + req.AddRef() + return req +} + +var _ Request = &request{} + +type request struct { + req *C.WebKitURISchemeRequest + + header http.Header + body io.ReadCloser + rw *responseWriter +} + +func (r *request) AddRef() error { + C.g_object_ref(C.gpointer(r.req)) + return nil +} + +func (r *request) Release() error { + C.g_object_unref(C.gpointer(r.req)) + return nil +} + +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 + } + + // WebKit2GTK has currently no support for request bodies. + r.body = http.NoBody + + 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 +} diff --git a/v2/pkg/assetserver/webview/responsewriter.go b/v2/pkg/assetserver/webview/responsewriter.go index c372275df..9e3c1952f 100644 --- a/v2/pkg/assetserver/webview/responsewriter.go +++ b/v2/pkg/assetserver/webview/responsewriter.go @@ -1,9 +1,20 @@ 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 { diff --git a/v2/pkg/assetserver/webview/responsewriter_darwin.go b/v2/pkg/assetserver/webview/responsewriter_darwin.go index 1caa6e85d..77de3c455 100644 --- a/v2/pkg/assetserver/webview/responsewriter_darwin.go +++ b/v2/pkg/assetserver/webview/responsewriter_darwin.go @@ -69,16 +69,10 @@ import "C" import ( "encoding/json" - "errors" "net/http" "unsafe" ) -var ( - errRequestStopped = errors.New("request has been stopped") - errResponseFinished = errors.New("response has been finished") -) - var _ ResponseWriter = &responseWriter{} type responseWriter struct { diff --git a/v2/internal/frontend/desktop/linux/webkit2_responsewriter.go b/v2/pkg/assetserver/webview/responsewriter_linux.go similarity index 74% rename from v2/internal/frontend/desktop/linux/webkit2_responsewriter.go rename to v2/pkg/assetserver/webview/responsewriter_linux.go index ca3fa65c7..52e28aa5d 100644 --- a/v2/internal/frontend/desktop/linux/webkit2_responsewriter.go +++ b/v2/pkg/assetserver/webview/responsewriter_linux.go @@ -1,7 +1,7 @@ //go:build linux // +build linux -package linux +package webview /* #cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 gio-unix-2.0 @@ -20,28 +20,31 @@ import ( "strconv" "syscall" "unsafe" - - "github.com/wailsapp/wails/v2/pkg/assetserver" ) -type webKitResponseWriter struct { +type responseWriter struct { req *C.WebKitURISchemeRequest header http.Header wroteHeader bool + finished bool w io.WriteCloser wErr error } -func (rw *webKitResponseWriter) Header() http.Header { +func (rw *responseWriter) Header() http.Header { if rw.header == nil { rw.header = http.Header{} } return rw.header } -func (rw *webKitResponseWriter) Write(buf []byte) (int, error) { +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 @@ -49,14 +52,14 @@ func (rw *webKitResponseWriter) Write(buf []byte) (int, error) { return rw.w.Write(buf) } -func (rw *webKitResponseWriter) WriteHeader(code int) { - if rw.wroteHeader { +func (rw *responseWriter) WriteHeader(code int) { + if rw.wroteHeader || rw.finished { return } rw.wroteHeader = true contentLength := int64(-1) - if sLen := rw.Header().Get(assetserver.HeaderContentLength); sLen != "" { + if sLen := rw.Header().Get(HeaderContentLength); sLen != "" { if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 { contentLength = pLen } @@ -72,7 +75,7 @@ func (rw *webKitResponseWriter) WriteHeader(code int) { } rw.w = w - stream := C.g_unix_input_stream_new(C.int(rFD), gtkBool(true)) + 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 { @@ -81,13 +84,22 @@ func (rw *webKitResponseWriter) WriteHeader(code int) { } } -func (rw *webKitResponseWriter) Close() { +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 *webKitResponseWriter) finishWithError(code int, err error) { +func (rw *responseWriter) finishWithError(code int, err error) { if rw.w != nil { rw.w.Close() rw.w = &nopCloser{io.Discard} diff --git a/v2/internal/frontend/desktop/linux/webkit2_36.go b/v2/pkg/assetserver/webview/webkit2_36.go similarity index 91% rename from v2/internal/frontend/desktop/linux/webkit2_36.go rename to v2/pkg/assetserver/webview/webkit2_36.go index 0c0c382e6..f792e033c 100644 --- a/v2/internal/frontend/desktop/linux/webkit2_36.go +++ b/v2/pkg/assetserver/webview/webkit2_36.go @@ -1,6 +1,6 @@ //go:build linux && webkit2_36 -package linux +package webview /* #cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 libsoup-2.4 @@ -15,11 +15,9 @@ import ( "net/http" "strings" "unsafe" - - "github.com/wailsapp/wails/v2/pkg/assetserver" ) -const webkit2MinMinorVersion = 36 +const Webkit2MinMinorVersion = 36 func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string { method := C.GoString(C.webkit_uri_scheme_request_get_http_method(req)) @@ -51,7 +49,7 @@ func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, h C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason) C.free(unsafe.Pointer(cReason)) - cMimeType := C.CString(header.Get(assetserver.HeaderContentType)) + cMimeType := C.CString(header.Get(HeaderContentType)) C.webkit_uri_scheme_response_set_content_type(resp, cMimeType) C.free(unsafe.Pointer(cMimeType)) diff --git a/v2/internal/frontend/desktop/linux/webkit2_legacy.go b/v2/pkg/assetserver/webview/webkit2_legacy.go similarity index 83% rename from v2/internal/frontend/desktop/linux/webkit2_legacy.go rename to v2/pkg/assetserver/webview/webkit2_legacy.go index 9d5158f4e..4f9010d9a 100644 --- a/v2/internal/frontend/desktop/linux/webkit2_legacy.go +++ b/v2/pkg/assetserver/webview/webkit2_legacy.go @@ -1,6 +1,6 @@ //go:build linux && !webkit2_36 -package linux +package webview /* #cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 @@ -14,11 +14,9 @@ import ( "fmt" "net/http" "unsafe" - - "github.com/wailsapp/wails/v2/pkg/assetserver" ) -const webkit2MinMinorVersion = 0 +const Webkit2MinMinorVersion = 0 func webkit_uri_scheme_request_get_http_method(_ *C.WebKitURISchemeRequest) string { return http.MethodGet @@ -33,7 +31,7 @@ func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, h return fmt.Errorf("StatusCodes not supported: %d - %s", code, http.StatusText(code)) } - cMimeType := C.CString(header.Get(assetserver.HeaderContentType)) + 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