mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 01:10:29 +08:00

* [assetserver, darwin] Fix copying request headers by using strdup * [assetserver, linux] Fake some basic http headers for legacy webkit2 versions to support proxying requests to other servers This fixes the devserver on v2 for newer vite versions that use the custom scheme. * [v2, windows] 304 responses are going to hang the WebView2 so prevent them by removing cache related headers in the request. * [v2, dev] Now uses the custom schemes `wails://` on macOS and Linux for all Vite versions. Prevent missing reload after fast multiple savings on Linux and Windows.
249 lines
5.8 KiB
Go
249 lines
5.8 KiB
Go
//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()
|
|
}
|
|
r.Response().Finish()
|
|
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
|
|
}
|