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  }