5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 10:39:50 +08:00
wails/v2/pkg/assetserver/webview/responsewriter_darwin.go
Lea Anthony 3f83b42db5
FIX: Address Mac CGO Crash (#3590)
* Copy request to Go memory

* Update changelog.mdx

* Update v2/pkg/assetserver/webview/responsewriter_darwin.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Fix import

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2024-08-20 18:12:56 +10:00

165 lines
4.0 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>
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
}