From 3d24a9b4c446a4fc7f883555174e4ff80c37db09 Mon Sep 17 00:00:00 2001 From: stffabi Date: Mon, 1 Aug 2022 10:55:25 +0200 Subject: [PATCH] [v2, darwin] Assign a unique requestId to every WKWebView request (#1681) This makes sure we always use the correct WKURLSchemeTask during processURLResponse. Only using the URL is not unique enough and might result in pending requests if two requests with the same URL are getting processed. --- .../frontend/desktop/darwin/Application.h | 2 +- .../frontend/desktop/darwin/Application.m | 15 ++--- .../frontend/desktop/darwin/WailsContext.h | 4 +- .../frontend/desktop/darwin/WailsContext.m | 59 ++++++++++++++----- .../frontend/desktop/darwin/frontend.go | 8 +-- v2/internal/frontend/desktop/darwin/main.m | 4 +- v2/internal/frontend/desktop/darwin/message.h | 2 +- 7 files changed, 60 insertions(+), 34 deletions(-) diff --git a/v2/internal/frontend/desktop/darwin/Application.h b/v2/internal/frontend/desktop/darwin/Application.h index 7e53c36e8..22304f4f7 100644 --- a/v2/internal/frontend/desktop/darwin/Application.h +++ b/v2/internal/frontend/desktop/darwin/Application.h @@ -45,7 +45,7 @@ void Quit(void*); const char* GetSize(void *ctx); const char* GetPosition(void *ctx); -void ProcessURLResponse(void *inctx, const char *url, int statusCode, void *headersString, int headersStringLength, void* data, int datalength); +void ProcessURLResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength, void* data, int datalength); /* Dialogs */ diff --git a/v2/internal/frontend/desktop/darwin/Application.m b/v2/internal/frontend/desktop/darwin/Application.m index ddb397162..18db2b3d9 100644 --- a/v2/internal/frontend/desktop/darwin/Application.m +++ b/v2/internal/frontend/desktop/darwin/Application.m @@ -51,16 +51,13 @@ WailsContext* Create(const char* title, int width, int height, int frameless, in return result; } -void ProcessURLResponse(void *inctx, const char *url, int statusCode, void *headersString, int headersStringLength, void* data, int datalength) { +void ProcessURLResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength, void* data, int datalength) { WailsContext *ctx = (__bridge WailsContext*) inctx; - NSString *nsurl = safeInit(url); - NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength]; - NSData *nsdata = [NSData dataWithBytes:data length:datalength]; - - [ctx processURLResponse:nsurl :statusCode :nsHeadersJSON :nsdata]; - - [nsdata release]; - [nsHeadersJSON release]; + @autoreleasepool { + NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength]; + NSData *nsdata = [NSData dataWithBytes:data length:datalength]; + [ctx processURLResponse:requestId :statusCode :nsHeadersJSON :nsdata]; + } } void ExecJS(void* inctx, const char *script) { diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.h b/v2/internal/frontend/desktop/darwin/WailsContext.h index 4325e5fbb..d0034d8b3 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.h +++ b/v2/internal/frontend/desktop/darwin/WailsContext.h @@ -47,6 +47,8 @@ @property bool debug; @property (retain) WKUserContentController* userContentController; +@property (retain) NSLock *urlRequestsLock; +@property unsigned long long urlRequestsId; @property (retain) NSMutableDictionary *urlRequests; @property (retain) NSMenu* applicationMenu; @@ -84,7 +86,7 @@ - (void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters; - (void) loadRequest:(NSString*)url; -- (void) processURLResponse:(NSString *)url :(int)statusCode :(NSData *)headersString :(NSData*)data; +- (void) processURLResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersString :(NSData*)data; - (void) ExecJS:(NSString*)script; - (NSScreen*) getCurrentScreen; diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.m b/v2/internal/frontend/desktop/darwin/WailsContext.m index f92fb3142..a6599b812 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.m +++ b/v2/internal/frontend/desktop/darwin/WailsContext.m @@ -106,6 +106,7 @@ [self.mouseEvent release]; [self.userContentController release]; [self.urlRequests release]; + [self.urlRequestsLock release]; [self.applicationMenu release]; [super dealloc]; } @@ -135,7 +136,8 @@ } - (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight { - + self.urlRequestsId = 0; + self.urlRequestsLock = [NSLock new]; self.urlRequests = [NSMutableDictionary new]; NSWindowStyleMask styleMask = 0; @@ -394,24 +396,41 @@ [self.webview evaluateJavaScript:script completionHandler:nil]; } -- (void) processURLResponse:(NSString *)url :(int)statusCode :(NSData *)headersJSON :(NSData *)data { - id urlSchemeTask = self.urlRequests[url]; - NSURL *nsurl = [NSURL URLWithString:url]; - NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData: headersJSON options: NSJSONReadingMutableContainers error: nil]; - NSHTTPURLResponse *response = [[NSHTTPURLResponse new] initWithURL:nsurl statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields]; - [urlSchemeTask didReceiveResponse:response]; - [urlSchemeTask didReceiveData:data]; - [urlSchemeTask didFinish]; - [self.urlRequests removeObjectForKey:url]; - [response release]; - if (headerFields != nil) { - [headerFields release]; +- (void) processURLResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersJSON :(NSData *)data { + NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId]; + + [self.urlRequestsLock lock]; + id urlSchemeTask = self.urlRequests[key]; + [self.urlRequestsLock unlock]; + + @try { + if (urlSchemeTask == nil) { + return; + } + + NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData: headersJSON options: NSJSONReadingMutableContainers error: nil]; + NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease]; + + @try { + [urlSchemeTask didReceiveResponse:response]; + [urlSchemeTask didReceiveData:data]; + [urlSchemeTask didFinish]; + } @catch (NSException *exception) { + // This is very bad to detect a stopped schemeTask this should be implemented in a better way + // See todo in stopURLSchemeTask... + if (![exception.reason isEqualToString: @"This task has already been stopped"]) { + @throw exception; + } + } + } @finally { + [self.urlRequestsLock lock]; + [self.urlRequests removeObjectForKey:key]; // This will release the urlSchemeTask which was retained from the dictionary + [self.urlRequestsLock unlock]; } } - (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id)urlSchemeTask { // This callback is run with an autorelease pool - self.urlRequests[urlSchemeTask.request.URL.absoluteString] = urlSchemeTask; const char *url = [urlSchemeTask.request.URL.absoluteString UTF8String]; const char *method = [urlSchemeTask.request.HTTPMethod UTF8String]; const char *headerJSON = ""; @@ -431,11 +450,19 @@ // TODO handle HTTPBodyStream } - processURLRequest(self, url, method, headerJSON, body, bodyLen); + [self.urlRequestsLock lock]; + self.urlRequestsId++; + unsigned long long requestId = self.urlRequestsId; + NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId]; + self.urlRequests[key] = urlSchemeTask; + [self.urlRequestsLock unlock]; + + processURLRequest(self, requestId, url, method, headerJSON, body, bodyLen); } - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id)urlSchemeTask { - + // TODO implement the stopping process here in a better way... + // As soon as we introduce response body streaming we need to rewrite this nevertheless. } - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index 75df093b3..842301a9e 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -350,9 +350,7 @@ func (f *Frontend) processRequest(r *request) { headersLen = len(headerData) } - url := C.CString(r.url) - defer C.free(unsafe.Pointer(url)) - C.ProcessURLResponse(r.ctx, url, C.int(rw.Code), headers, C.int(headersLen), content, C.int(contentLen)) + C.ProcessURLResponse(r.ctx, r.id, C.int(rw.Code), headers, C.int(headersLen), content, C.int(contentLen)) } //func (f *Frontend) processSystemEvent(message string) { @@ -372,6 +370,7 @@ func (f *Frontend) processRequest(r *request) { //} type request struct { + id C.ulonglong url string method string headers string @@ -412,13 +411,14 @@ func processMessage(message *C.char) { } //export processURLRequest -func processURLRequest(ctx unsafe.Pointer, url *C.char, method *C.char, headers *C.char, body unsafe.Pointer, bodyLen C.int) { +func processURLRequest(ctx unsafe.Pointer, requestId C.ulonglong, url *C.char, method *C.char, headers *C.char, body unsafe.Pointer, bodyLen C.int) { var goBody []byte if body != nil && bodyLen != 0 { goBody = C.GoBytes(body, bodyLen) } requestBuffer <- &request{ + id: requestId, url: C.GoString(url), method: C.GoString(method), headers: C.GoString(headers), diff --git a/v2/internal/frontend/desktop/darwin/main.m b/v2/internal/frontend/desktop/darwin/main.m index 154c33065..5f4618d1b 100644 --- a/v2/internal/frontend/desktop/darwin/main.m +++ b/v2/internal/frontend/desktop/darwin/main.m @@ -29,11 +29,11 @@ void processCallback(int callbackID) { NSLog(@"Process callback %d", callbackID); } -void processURLRequest(void *ctx, const char* url const char *method, const char *headers, const void *body, int bodyLen) { +void processURLRequest(void *ctx, unsigned long long requestId, const char* url, const char *method, const char *headers, const void *body, int bodyLen) { NSLog(@"processURLRequest called"); const char myByteArray[] = { 0x3c,0x68,0x31,0x3e,0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21,0x3c,0x2f,0x68,0x31,0x3e }; // void *inctx, const char *url, int statusCode, const char *headers, void* data, int datalength - ProcessURLResponse(ctx, url, 200, "{\"Content-Type\": \"text/html\"}", (void*)myByteArray, 21); + ProcessURLResponse(ctx, requestId, 200, "{\"Content-Type\": \"text/html\"}", (void*)myByteArray, 21); } unsigned char _Users_username_Pictures_SaltBae_png[] = { diff --git a/v2/internal/frontend/desktop/darwin/message.h b/v2/internal/frontend/desktop/darwin/message.h index f0a5f482b..b3405f4ab 100644 --- a/v2/internal/frontend/desktop/darwin/message.h +++ b/v2/internal/frontend/desktop/darwin/message.h @@ -15,7 +15,7 @@ extern "C" #endif void processMessage(const char *); -void processURLRequest(void*, const char *, const char *, const char *, const void *, int); +void processURLRequest(void*, unsigned long long, const char *, const char *, const char *, const void *, int); void processMessageDialogResponse(int); void processOpenFileDialogResponse(const char*); void processSaveFileDialogResponse(const char*);