github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/pkg/assetserver/webview/responsewriter_darwin.go (about) 1 //go:build darwin 2 3 package webview 4 5 /* 6 #cgo CFLAGS: -x objective-c 7 #cgo LDFLAGS: -framework Foundation -framework WebKit 8 9 #import <Foundation/Foundation.h> 10 #import <WebKit/WebKit.h> 11 12 typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>); 13 14 static bool urlSchemeTaskCall(void *wkUrlSchemeTask, schemeTaskCaller fn) { 15 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 16 if (urlSchemeTask == nil) { 17 return false; 18 } 19 20 @autoreleasepool { 21 @try { 22 fn(urlSchemeTask); 23 } @catch (NSException *exception) { 24 // This is very bad to detect a stopped schemeTask this should be implemented in a better way 25 // But it seems to be very tricky to not deadlock when keeping a lock curing executing fn() 26 // It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want 27 // to get the lock again to start another request or stop it. 28 if ([exception.reason isEqualToString: @"This task has already been stopped"]) { 29 return false; 30 } 31 32 @throw exception; 33 } 34 35 return true; 36 } 37 } 38 39 static bool URLSchemeTaskDidReceiveData(void *wkUrlSchemeTask, void* data, int datalength) { 40 return urlSchemeTaskCall( 41 wkUrlSchemeTask, 42 ^(id<WKURLSchemeTask> urlSchemeTask) { 43 NSData *nsdata = [NSData dataWithBytes:data length:datalength]; 44 [urlSchemeTask didReceiveData:nsdata]; 45 }); 46 } 47 48 static bool URLSchemeTaskDidFinish(void *wkUrlSchemeTask) { 49 return urlSchemeTaskCall( 50 wkUrlSchemeTask, 51 ^(id<WKURLSchemeTask> urlSchemeTask) { 52 [urlSchemeTask didFinish]; 53 }); 54 } 55 56 static bool URLSchemeTaskDidReceiveResponse(void *wkUrlSchemeTask, int statusCode, void *headersString, int headersStringLength) { 57 return urlSchemeTaskCall( 58 wkUrlSchemeTask, 59 ^(id<WKURLSchemeTask> urlSchemeTask) { 60 NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength]; 61 NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData:nsHeadersJSON options: NSJSONReadingMutableContainers error: nil]; 62 NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease]; 63 64 [urlSchemeTask didReceiveResponse:response]; 65 }); 66 } 67 */ 68 import "C" 69 70 import ( 71 "encoding/json" 72 "net/http" 73 "unsafe" 74 ) 75 76 var _ ResponseWriter = &responseWriter{} 77 78 type responseWriter struct { 79 r *request 80 81 header http.Header 82 wroteHeader bool 83 84 finished bool 85 } 86 87 func (rw *responseWriter) Header() http.Header { 88 if rw.header == nil { 89 rw.header = http.Header{} 90 } 91 return rw.header 92 } 93 94 func (rw *responseWriter) Write(buf []byte) (int, error) { 95 if rw.finished { 96 return 0, errResponseFinished 97 } 98 99 rw.WriteHeader(http.StatusOK) 100 101 var content unsafe.Pointer 102 var contentLen int 103 if buf != nil { 104 content = unsafe.Pointer(&buf[0]) 105 contentLen = len(buf) 106 } 107 108 if !C.URLSchemeTaskDidReceiveData(rw.r.task, content, C.int(contentLen)) { 109 return 0, errRequestStopped 110 } 111 return contentLen, nil 112 } 113 114 func (rw *responseWriter) WriteHeader(code int) { 115 if rw.wroteHeader || rw.finished { 116 return 117 } 118 rw.wroteHeader = true 119 120 header := map[string]string{} 121 for k := range rw.Header() { 122 header[k] = rw.Header().Get(k) 123 } 124 headerData, _ := json.Marshal(header) 125 126 var headers unsafe.Pointer 127 var headersLen int 128 if len(headerData) != 0 { 129 headers = unsafe.Pointer(&headerData[0]) 130 headersLen = len(headerData) 131 } 132 133 C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen)) 134 } 135 136 func (rw *responseWriter) Finish() error { 137 if !rw.wroteHeader { 138 rw.WriteHeader(http.StatusNotImplemented) 139 } 140 141 if rw.finished { 142 return nil 143 } 144 rw.finished = true 145 146 C.URLSchemeTaskDidFinish(rw.r.task) 147 return nil 148 }