mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
[v2] assetserver/webview purego implementation
This commit is contained in:
parent
7fd627f169
commit
769662d77a
5 changed files with 466 additions and 0 deletions
86
v2/pkg/assetserver/webview/request_linux_purego.go
Normal file
86
v2/pkg/assetserver/webview/request_linux_purego.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//go:build linux && purego
|
||||
// +build linux,purego
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
// 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 uintptr) Request {
|
||||
webkitReq := webKitURISchemeRequest
|
||||
req := &request{req: webkitReq}
|
||||
req.AddRef()
|
||||
return req
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
req uintptr
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) AddRef() error {
|
||||
var objectRef func(uintptr)
|
||||
purego.RegisterLibFunc(&objectRef, gtk, "g_object_ref")
|
||||
objectRef(r.req)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *request) Release() error {
|
||||
var objectUnref func(uintptr)
|
||||
purego.RegisterLibFunc(&objectUnref, gtk, "g_object_unref")
|
||||
objectUnref(r.req)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
var getUri func(uintptr) string
|
||||
purego.RegisterLibFunc(&getUri, webkit, "webkit_uri_scheme_request_get_uri")
|
||||
return getUri(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 {
|
||||
fmt.Println("r.Response()")
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r.req}
|
||||
return r.rw
|
||||
}
|
||||
176
v2/pkg/assetserver/webview/responsewriter_linux_purego.go
Normal file
176
v2/pkg/assetserver/webview/responsewriter_linux_purego.go
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
//go:build linux && purego
|
||||
// +build linux,purego
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
const (
|
||||
gtk3 = "libgtk-3.so"
|
||||
gtk4 = "libgtk-4.so"
|
||||
)
|
||||
|
||||
var (
|
||||
gtk uintptr
|
||||
webkit uintptr
|
||||
version int
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
// gtk, err = purego.Dlopen(gtk4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
// if err == nil {
|
||||
// version = 4
|
||||
// return
|
||||
// }
|
||||
// log.Println("Failed to open GTK4: Falling back to GTK3")
|
||||
gtk, err = purego.Dlopen(gtk3, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version = 3
|
||||
|
||||
var webkit4 string = "libwebkit2gtk-4.1.so"
|
||||
webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
req uintptr
|
||||
|
||||
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) {
|
||||
// TODO? Is this ever called? I don't think so!
|
||||
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
|
||||
}
|
||||
}
|
||||
fmt.Println("content_length", contentLength)
|
||||
// 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
|
||||
|
||||
var newStream func(int, bool) uintptr
|
||||
purego.RegisterLibFunc(&newStream, gtk, "g_unix_input_stream_new")
|
||||
var unRef func(uintptr)
|
||||
purego.RegisterLibFunc(&unRef, gtk, "g_object_unref")
|
||||
stream := newStream(rFD, true)
|
||||
|
||||
/* var reqFinish func(uintptr, uintptr, uintptr, uintptr, int64) int
|
||||
purego.RegisterLibFunc(&reqFinish, webkit, "webkit_uri_scheme_request_finish")
|
||||
|
||||
header := rw.Header()
|
||||
defer unRef(stream)
|
||||
if err := reqFinish(rw.req, code, header, stream, contentLength); err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err))
|
||||
}
|
||||
*/
|
||||
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
|
||||
|
||||
var newLiteral func(uint32, string, int, string) uintptr // is this correct?
|
||||
purego.RegisterLibFunc(&newLiteral, gtk, "g_error_new_literal")
|
||||
var newQuark func(string) uintptr
|
||||
purego.RegisterLibFunc(&newQuark, gtk, "g_quark_from_string")
|
||||
var freeError func(uintptr)
|
||||
purego.RegisterLibFunc(&freeError, gtk, "g_error_free")
|
||||
var finishError func(uintptr, uintptr)
|
||||
purego.RegisterLibFunc(&finishError, webkit, "webkit_uri_scheme_request_finish_error")
|
||||
|
||||
msg := string(err.Error())
|
||||
//gquark := newQuark(msg)
|
||||
gerr := newLiteral(1, msg, code, msg)
|
||||
finishError(rw.req, gerr)
|
||||
freeError(gerr)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
94
v2/pkg/assetserver/webview/webkit2_36+_purego.go
Normal file
94
v2/pkg/assetserver/webview/webkit2_36+_purego.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//go:build linux && (webkit2_36 || webkit2_40) && purego
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(req uintptr) string {
|
||||
var getMethod func(uintptr) string
|
||||
purego.RegisterLibFunc(&getMethod, gtk, "webkit_uri_scheme_request_get_http_method")
|
||||
return strings.ToUpper(getMethod(req))
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(req uintptr) http.Header {
|
||||
var getHeaders func(uintptr) uintptr
|
||||
purego.RegisterLibFunc(&getUri, webkit, "webkit_uri_scheme_request_get_http_headers")
|
||||
|
||||
hdrs := getHeaders(req)
|
||||
|
||||
var headersIterInit func(uintptr, uintptr) uintptr
|
||||
purego.RegisterLibFunc(&headersIterInit, gtk, "soup_message_headers_iter_init")
|
||||
|
||||
// TODO: How do we get a struct?
|
||||
/*
|
||||
typedef struct {
|
||||
SoupMessageHeaders *hdrs;
|
||||
int index_common;
|
||||
int index_uncommon;
|
||||
} SoupMessageHeadersIterReal;
|
||||
*/
|
||||
iter := make([]byte, 12)
|
||||
headersIterInit(&iter, hdrs)
|
||||
|
||||
var iterNext func(uintptr, *string, *string) int
|
||||
purego.RegisterLibFunc(&iterNext, gtk, "soup_message_headers_iter_next")
|
||||
|
||||
var name string
|
||||
var value string
|
||||
h := http.Header{}
|
||||
|
||||
for iterNext(&iter, &name, &value) != 0 {
|
||||
h.Add(name, value)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req uintptr, code int, header http.Header, stream uintptr, streamLength int64) error {
|
||||
|
||||
var newResponse func(uintptr, int64) string
|
||||
purego.RegisterLibFunc(&newResponse, webkit, "webkit_uri_scheme_response_new")
|
||||
var unRef func(uintptr)
|
||||
purego.RegisterLibFunc(&unRef, gtk, "g_object_unref")
|
||||
|
||||
resp := newResponse(stream, streamLength)
|
||||
defer unRef(resp)
|
||||
|
||||
var setStatus func(uintptr, int, string)
|
||||
purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_status")
|
||||
|
||||
setStatus(resp, code, cReason)
|
||||
|
||||
var setContentType func(uintptr, string)
|
||||
purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_content_type")
|
||||
|
||||
setContentType(resp, header.Get(HeaderContentType))
|
||||
|
||||
soup := gtk
|
||||
var soupHeadersNew func(int) uintptr
|
||||
purego.RegisterLibFunc(&unRef, soup, "soup_message_headers_new")
|
||||
var soupHeadersAppend func(uintptr, string, string)
|
||||
purego.RegisterLibFunc(&unRef, soup, "soup_message_headers_append")
|
||||
|
||||
hdrs := soupHeadersNew(SOUP_MESSAGE_HEADERS_RESPONSE)
|
||||
for name, values := range header {
|
||||
for _, value := range values {
|
||||
soupHeadersAppend(hdrs, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
var setHttpHeaders func(uintptr, uintptr)
|
||||
purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_http_headers")
|
||||
|
||||
setHttpHeaders(resp, hdrs)
|
||||
var finishWithResponse func(uintptr, uintptr)
|
||||
purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_request_finish_with_response")
|
||||
finishWithResponse(req, resp)
|
||||
|
||||
return nil
|
||||
}
|
||||
74
v2/pkg/assetserver/webview/webkit2_40+_purego.go
Normal file
74
v2/pkg/assetserver/webview/webkit2_40+_purego.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//go:build linux && webkit2_40 && purego
|
||||
|
||||
package webview
|
||||
|
||||
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...)
|
||||
}
|
||||
36
v2/pkg/assetserver/webview/webkit2_legacy_purego.go
Normal file
36
v2/pkg/assetserver/webview/webkit2_legacy_purego.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//go:build linux && !(webkit2_36 || webkit2_40) && purego
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
const Webkit2MinMinorVersion = 0
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(_ uintptr) string {
|
||||
return http.MethodGet
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(_ uintptr) http.Header {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(_ uintptr) io.ReadCloser {
|
||||
return http.NoBody
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req uintptr, code int, header http.Header, stream uintptr, streamLength int64) error {
|
||||
if code != http.StatusOK {
|
||||
return fmt.Errorf("StatusCodes not supported: %d - %s", code, http.StatusText(code))
|
||||
}
|
||||
|
||||
var requestFinish func(uintptr, uintptr, int64, string)
|
||||
purego.RegisterLibFunc(&requestFinish, webkit, "webkit_uri_scheme_request_finish")
|
||||
requestFinish(req, stream, streamLength, header.Get(HeaderContentType))
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue