github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/pkg/assetserver/webview/request_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 #include <string.h> 12 13 static void URLSchemeTaskRetain(void *wkUrlSchemeTask) { 14 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 15 [urlSchemeTask retain]; 16 } 17 18 static void URLSchemeTaskRelease(void *wkUrlSchemeTask) { 19 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 20 [urlSchemeTask release]; 21 } 22 23 static const char * URLSchemeTaskRequestURL(void *wkUrlSchemeTask) { 24 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 25 @autoreleasepool { 26 return [urlSchemeTask.request.URL.absoluteString UTF8String]; 27 } 28 } 29 30 static const char * URLSchemeTaskRequestMethod(void *wkUrlSchemeTask) { 31 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 32 @autoreleasepool { 33 return [urlSchemeTask.request.HTTPMethod UTF8String]; 34 } 35 } 36 37 static const char * URLSchemeTaskRequestHeadersJSON(void *wkUrlSchemeTask) { 38 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 39 @autoreleasepool { 40 NSData *headerData = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil]; 41 if (!headerData) { 42 return nil; 43 } 44 45 NSString* headerString = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease]; 46 const char * headerJSON = [headerString UTF8String]; 47 48 return strdup(headerJSON); 49 } 50 } 51 52 static bool URLSchemeTaskRequestBodyBytes(void *wkUrlSchemeTask, const void **body, int *bodyLen) { 53 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 54 @autoreleasepool { 55 if (!urlSchemeTask.request.HTTPBody) { 56 return false; 57 } 58 59 *body = urlSchemeTask.request.HTTPBody.bytes; 60 *bodyLen = urlSchemeTask.request.HTTPBody.length; 61 return true; 62 } 63 } 64 65 static bool URLSchemeTaskRequestBodyStreamOpen(void *wkUrlSchemeTask) { 66 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 67 @autoreleasepool { 68 if (!urlSchemeTask.request.HTTPBodyStream) { 69 return false; 70 } 71 72 [urlSchemeTask.request.HTTPBodyStream open]; 73 return true; 74 } 75 } 76 77 static void URLSchemeTaskRequestBodyStreamClose(void *wkUrlSchemeTask) { 78 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 79 @autoreleasepool { 80 if (!urlSchemeTask.request.HTTPBodyStream) { 81 return; 82 } 83 84 [urlSchemeTask.request.HTTPBodyStream close]; 85 } 86 } 87 88 static int URLSchemeTaskRequestBodyStreamRead(void *wkUrlSchemeTask, void *buf, int bufLen) { 89 id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask; 90 91 @autoreleasepool { 92 NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream; 93 if (!stream) { 94 return -2; 95 } 96 97 NSStreamStatus status = stream.streamStatus; 98 if (status == NSStreamStatusAtEnd || !stream.hasBytesAvailable) { 99 return 0; 100 } else if (status != NSStreamStatusOpen) { 101 return -3; 102 } 103 104 return [stream read:buf maxLength:bufLen]; 105 } 106 } 107 */ 108 import "C" 109 110 import ( 111 "bytes" 112 "encoding/json" 113 "fmt" 114 "io" 115 "net/http" 116 "unsafe" 117 ) 118 119 // NewRequest creates as new WebViewRequest based on a pointer to an `id<WKURLSchemeTask>` 120 func NewRequest(wkURLSchemeTask unsafe.Pointer) Request { 121 C.URLSchemeTaskRetain(wkURLSchemeTask) 122 return newRequestFinalizer(&request{task: wkURLSchemeTask}) 123 } 124 125 var _ Request = &request{} 126 127 type request struct { 128 task unsafe.Pointer 129 130 header http.Header 131 body io.ReadCloser 132 rw *responseWriter 133 } 134 135 func (r *request) URL() (string, error) { 136 return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil 137 } 138 139 func (r *request) Method() (string, error) { 140 return C.GoString(C.URLSchemeTaskRequestMethod(r.task)), nil 141 } 142 143 func (r *request) Header() (http.Header, error) { 144 if r.header != nil { 145 return r.header, nil 146 } 147 148 header := http.Header{} 149 if cHeaders := C.URLSchemeTaskRequestHeadersJSON(r.task); cHeaders != nil { 150 if headers := C.GoString(cHeaders); headers != "" { 151 var h map[string]string 152 if err := json.Unmarshal([]byte(headers), &h); err != nil { 153 return nil, fmt.Errorf("unable to unmarshal request headers: %s", err) 154 } 155 156 for k, v := range h { 157 header.Add(k, v) 158 } 159 } 160 C.free(unsafe.Pointer(cHeaders)) 161 } 162 r.header = header 163 return header, nil 164 } 165 166 func (r *request) Body() (io.ReadCloser, error) { 167 if r.body != nil { 168 return r.body, nil 169 } 170 171 var body unsafe.Pointer 172 var bodyLen C.int 173 if C.URLSchemeTaskRequestBodyBytes(r.task, &body, &bodyLen) { 174 if body != nil && bodyLen > 0 { 175 r.body = io.NopCloser(bytes.NewReader(C.GoBytes(body, bodyLen))) 176 } else { 177 r.body = http.NoBody 178 } 179 } else if C.URLSchemeTaskRequestBodyStreamOpen(r.task) { 180 r.body = &requestBodyStreamReader{task: r.task} 181 } 182 183 return r.body, nil 184 } 185 186 func (r *request) Response() ResponseWriter { 187 if r.rw != nil { 188 return r.rw 189 } 190 191 r.rw = &responseWriter{r: r} 192 return r.rw 193 } 194 195 func (r *request) Close() error { 196 var err error 197 if r.body != nil { 198 err = r.body.Close() 199 } 200 err = r.Response().Finish() 201 if err != nil { 202 return err 203 } 204 C.URLSchemeTaskRelease(r.task) 205 return err 206 } 207 208 var _ io.ReadCloser = &requestBodyStreamReader{} 209 210 type requestBodyStreamReader struct { 211 task unsafe.Pointer 212 closed bool 213 } 214 215 // Read implements io.Reader 216 func (r *requestBodyStreamReader) Read(p []byte) (n int, err error) { 217 var content unsafe.Pointer 218 var contentLen int 219 if p != nil { 220 content = unsafe.Pointer(&p[0]) 221 contentLen = len(p) 222 } 223 224 res := C.URLSchemeTaskRequestBodyStreamRead(r.task, content, C.int(contentLen)) 225 if res > 0 { 226 return int(res), nil 227 } 228 229 switch res { 230 case 0: 231 return 0, io.EOF 232 case -1: 233 return 0, fmt.Errorf("body: stream error") 234 case -2: 235 return 0, fmt.Errorf("body: no stream defined") 236 case -3: 237 return 0, io.ErrClosedPipe 238 default: 239 return 0, fmt.Errorf("body: unknown error %d", res) 240 } 241 } 242 243 func (r *requestBodyStreamReader) Close() error { 244 if r.closed { 245 return nil 246 } 247 r.closed = true 248 249 C.URLSchemeTaskRequestBodyStreamClose(r.task) 250 return nil 251 }