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  }