gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] 11/16: Native Networking via URLSession.dataTask


From: gnunet
Subject: [taler-taler-ios] 11/16: Native Networking via URLSession.dataTask
Date: Thu, 29 Feb 2024 17:19:18 +0100

This is an automated email from the git hooks/post-receive script.

marc-stibane pushed a commit to branch master
in repository taler-ios.

commit f39b4cae710ff997ac1415343e6c0fb765e8d4a9
Author: Marc Stibane <marc@taler.net>
AuthorDate: Thu Feb 29 14:31:16 2024 +0100

    Native Networking via URLSession.dataTask
---
 TalerWallet.xcodeproj/project.pbxproj    |   6 ++
 TalerWallet1/Quickjs/QuickDataTask.swift | 156 +++++++++++++++++++++++++++++++
 TalerWallet1/Quickjs/quickjs.swift       |  74 ++++++++++-----
 3 files changed, 211 insertions(+), 25 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index e5ce46c..410f541 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -244,6 +244,8 @@
                4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED80E872B8F5FB8008BD576 /* CStringArray.swift 
*/; };
                4ED80E8B2B8F60E7008BD576 /* Atomic.swift in Sources */ = {isa = 
PBXBuildFile; fileRef = 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */; };
                4ED80E8C2B8F60E7008BD576 /* Atomic.swift in Sources */ = {isa = 
PBXBuildFile; fileRef = 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */; };
+               4ED80E8E2B8F6212008BD576 /* QuickDataTask.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED80E8D2B8F6212008BD576 /* QuickDataTask.swift 
*/; };
+               4ED80E8F2B8F6212008BD576 /* QuickDataTask.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED80E8D2B8F6212008BD576 /* QuickDataTask.swift 
*/; };
                4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; 
};
                4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; 
};
                4EE171882B49635800BF9FF5 /* MarkdownUI in Frameworks */ = {isa 
= PBXBuildFile; productRef = 4EE171872B49635800BF9FF5 /* MarkdownUI */; };
@@ -431,6 +433,7 @@
                4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= ThreeAmountsV.swift; sourceTree = "<group>"; };
                4ED80E872B8F5FB8008BD576 /* CStringArray.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= CStringArray.swift; sourceTree = "<group>"; };
                4ED80E8A2B8F60E7008BD576 /* Atomic.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= Atomic.swift; sourceTree = "<group>"; };
+               4ED80E8D2B8F6212008BD576 /* QuickDataTask.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= QuickDataTask.swift; sourceTree = "<group>"; };
                4EDBDCD82AB787CB00925C02 /* CallStack.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= CallStack.swift; sourceTree = "<group>"; };
                4EEC118C2B83DE4700146CFF /* AmountInputV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AmountInputV.swift; sourceTree = "<group>"; };
                4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= SubjectInputV.swift; sourceTree = "<group>"; };
@@ -634,6 +637,7 @@
                        isa = PBXGroup;
                        children = (
                                4EB0950D2989CB9A0043A8A1 /* quickjs.swift */,
+                               4ED80E8D2B8F6212008BD576 /* QuickDataTask.swift 
*/,
                        );
                        path = Quickjs;
                        sourceTree = "<group>";
@@ -1099,6 +1103,7 @@
                                4E3EAE2B2A990778009F1BE8 /* LoadingView.swift 
in Sources */,
                                4E3EAE8C2AA0933C009F1BE8 /* Font+Taler.swift in 
Sources */,
                                4E3327BA2AD1635100BF5AD6 /* 
AsyncSemaphore.swift in Sources */,
+                               4ED80E8E2B8F6212008BD576 /* QuickDataTask.swift 
in Sources */,
                                4E3EAE2C2A990778009F1BE8 /* 
ManualWithdraw.swift in Sources */,
                                4E3EAE2D2A990778009F1BE8 /* 
Model+Exchange.swift in Sources */,
                                4EBC0F012B7B3CD600C0CB19 /* DepositIbanV.swift 
in Sources */,
@@ -1214,6 +1219,7 @@
                                4EB0956D2989CBFE0043A8A1 /* LoadingView.swift 
in Sources */,
                                4E3EAE8D2AA0933C009F1BE8 /* Font+Taler.swift in 
Sources */,
                                4E3327BB2AD1635100BF5AD6 /* 
AsyncSemaphore.swift in Sources */,
+                               4ED80E8F2B8F6212008BD576 /* QuickDataTask.swift 
in Sources */,
                                4E50B3502A1BEE8000F9F01C /* 
ManualWithdraw.swift in Sources */,
                                4E3B4BC92A42BC4800CC88B8 /* 
Model+Exchange.swift in Sources */,
                                4EBC0F022B7B3CD600C0CB19 /* DepositIbanV.swift 
in Sources */,
diff --git a/TalerWallet1/Quickjs/QuickDataTask.swift 
b/TalerWallet1/Quickjs/QuickDataTask.swift
new file mode 100644
index 0000000..e4afe88
--- /dev/null
+++ b/TalerWallet1/Quickjs/QuickDataTask.swift
@@ -0,0 +1,156 @@
+/*
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
+ * See LICENSE.md
+ */
+/**
+ * @author Marc Stibane
+ */
+import Foundation
+//import "Foundation/NSURLError.h"
+import os.log
+
+import FTalerWalletcore
+
+// will be called from wallet-core for networking
+func request_create(userdata: Optional<UnsafeMutableRawPointer>,
+                 requestInfo: 
Optional<UnsafeMutablePointer<JSHttpRequestInfo>>) -> Int32 {
+    let quickjs = 
Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+
+    if let requestInfo {
+        if let url = URL(string: String(cString: requestInfo.pointee.url)) {
+            let responseCb = requestInfo.pointee.response_cb
+            let responseCbCls = requestInfo.pointee.response_cb_cls
+            let method = String(cString: requestInfo.pointee.method)
+            let requestHeaders = requestInfo.pointee.request_headers
+                let redirect = requestInfo.pointee.redirect             // 
TODO: redirect
+            let timeoutMs = requestInfo.pointee.timeout_ms
+                let debug = requestInfo.pointee.debug                   // 
TODO: debug
+            let reqBody = requestInfo.pointee.req_body
+            let bodyLen = requestInfo.pointee.req_body_len
+
+            var request = URLRequest(url: url)
+            request.httpMethod = method
+            request.timeoutInterval = TimeInterval(timeoutMs)
+            if let reqBody {        // caller will deallocate the req_body 
after dataTask finish or cancel
+                let body = Data(bytesNoCopy: reqBody, count: Int(bodyLen), 
deallocator: .none)
+                request.httpBody = body
+            }
+            if var ptr = requestHeaders {
+                while let cString = ptr.pointee {
+                    let string = String(cString: cString)
+                    if let index = string.firstIndex(of: ":") {
+                        let headerField = string.prefix(upTo: index)
+                        let nextIndex = string.index(index, offsetBy: 1)       
 // skip the ":"
+                        let value = string.suffix(from: nextIndex)
+                        request.addValue(String(value), forHTTPHeaderField: 
String(headerField))
+                    }
+                    ptr += 1
+                }
+            }
+
+            return quickjs.reqCreate(request, responseCb, responseCbCls)
+        }
+    }
+    return 0
+}
+
+func request_cancel(userdata: Optional<UnsafeMutableRawPointer>,
+                   requestID: Int32) -> Int32 {
+    let quickjs = 
Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+    return quickjs.reqCancel(requestID)
+}
+// MARK: -
+class QuickDataTask: NSObject {
+    let urlSession: URLSession
+    let request: URLRequest
+    let requestID: Int32
+    var requests: [Int32: QuickDataTask]
+    let responseCb: JSHttpResponseCb?
+    let responseCbCls: Optional<UnsafeMutableRawPointer>
+
+    var dataTask: URLSessionDataTask? = nil
+    var logger: Logger
+
+    init(urlSession: URLSession,
+            request: URLRequest,
+          requestID: Int32,
+           requests: inout [Int32: QuickDataTask],
+         responseCb: JSHttpResponseCb?,
+      responseCbCls: Optional<UnsafeMutableRawPointer>
+    ) {
+        self.logger = Logger(subsystem: "net.taler.gnu", category: 
"Networking")
+        self.urlSession = urlSession
+        self.request = request
+        self.requestID = requestID
+        self.requests = requests
+        self.responseCb = responseCb
+        self.responseCbCls = responseCbCls
+    }
+    func run() {
+        if let responseCb, let responseCbCls {
+            let method = self.request.httpMethod ?? "Unknown"
+            let url = self.request.url?.absoluteString ?? EMPTYSTRING
+            logger.trace("❓\(self.requestID, privacy: .public)  \(method, 
privacy: .public) \(url, privacy: .public)")
+            dataTask = urlSession.dataTask(with: request) { [self] data, 
response, error in
+                let err: Error
+                if let response = response as? HTTPURLResponse {
+                    var headerArray: [String] = []
+                    var numHeaders: Int32 = 0
+                    var status = Int32(response.statusCode)
+                    var errmsg = 
HTTPURLResponse.localizedString(forStatusCode: Int(status))
+                    var errmsg_p0 = UnsafeMutablePointer<CChar>(mutating: 
errmsg.cString(using: .utf8))
+              // Initialization of 'UnsafeMutablePointer<CChar>' (aka 
'UnsafeMutablePointer<Int8>') results in a dangling pointer
+                    let headers = response.allHeaderFields
+                    for (key,value) in headers {
+                        headerArray.append("\(key): \(value)")
+                        numHeaders += 1
+                    }
+                    let cHeaders = CStringArray(headerArray)
+
+                    if let data {
+                        var ndata:NSData = data as NSData
+                        var bodyPtr = UnsafeMutableRawPointer(mutating: 
ndata.bytes)
+                        var responseInfo = JSHttpResponseInfo(request_id: 
self.requestID,
+                                                              status: status,
+                                                              errmsg: 
errmsg_p0,
+                                                    response_headers: 
cHeaders.pointer,
+                                                num_response_headers: 
numHeaders,
+                                                                body: bodyPtr,
+                                                            body_len: 
data.count)
+                        let responseInfoPtr = 
UnsafeMutablePointer<JSHttpResponseInfo>(&responseInfo)
+                        // Initialization of 
'UnsafeMutablePointer<JSHttpResponseInfo>' results in a dangling pointer
+                        logger.trace("❗️ \(self.requestID, privacy: .public) 
\(url, privacy: .public)")
+                        responseCb(responseCbCls, responseInfoPtr)
+                        return
+                    } else { // data == nil
+                        logger.error("‼️\(self.requestID, privacy: .public)  
\(method, privacy: .public) \(response.statusCode, privacy: .public) \(errmsg, 
privacy: .public)")
+                        var responseInfo = JSHttpResponseInfo(request_id: 
self.requestID,
+                                                                  status: 
status,
+                                                                  errmsg: 
errmsg_p0,
+                                                        response_headers: 
cHeaders.pointer,
+                                                    num_response_headers: 
numHeaders,
+                                                                    body: nil,
+                                                                body_len: 0)
+                        let responseInfoPtr = 
UnsafeMutablePointer<JSHttpResponseInfo>(&responseInfo)
+                        responseCb(responseCbCls, responseInfoPtr)
+                    }
+                } else {
+                    var errmsg = "No http response from \(url)"
+                    logger.error("⁉️\(self.requestID, privacy: .public)  
\(method, privacy: .public) \(errmsg, privacy: .public)")
+                    var errmsg_p0 = UnsafeMutablePointer<CChar>(mutating: 
errmsg.cString(using: .utf8))
+                    var responseInfo = JSHttpResponseInfo(request_id: 
self.requestID,
+                                                          status: 0,
+                                                          errmsg: errmsg_p0,
+                                                          response_headers: 
nil,
+                                                          
num_response_headers: 0,
+                                                          body: nil,
+                                                          body_len: 0)
+                    let responseInfoPtr = 
UnsafeMutablePointer<JSHttpResponseInfo>(&responseInfo)
+                    responseCb(responseCbCls, responseInfoPtr)
+                }
+                requests[requestID] = nil
+            }
+            dataTask?.resume()
+        }
+    }
+}
diff --git a/TalerWallet1/Quickjs/quickjs.swift 
b/TalerWallet1/Quickjs/quickjs.swift
index 06878af..dbe796d 100644
--- a/TalerWallet1/Quickjs/quickjs.swift
+++ b/TalerWallet1/Quickjs/quickjs.swift
@@ -16,17 +16,17 @@ public protocol QuickjsMessageHandler: AnyObject {
 // MARK: -
 func notification_callback(userdata: Optional<UnsafeMutableRawPointer>,
                            payload: Optional<UnsafePointer<Int8>>) {
-    let native = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+    let quickjs = 
Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
     let string = String(cString: payload!)
-    native.internalOnNotify(payload: string)
+    quickjs.internalOnNotify(payload: string)
 }
 
 func logging_callback(userdata: Optional<UnsafeMutableRawPointer>,
                           level: TALER_WALLET_LogLevel,
                             tag: Optional<UnsafePointer<Int8>>,
                         message: Optional<UnsafePointer<Int8>>) {
-    let native = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
-    let logger = native.logger
+    let quickjs = 
Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+    let logger = quickjs.logger
     let swiftTag = String(cString: tag!)
     let swiftMessage = String(cString: message!)
 
@@ -43,14 +43,21 @@ func logging_callback(userdata: 
Optional<UnsafeMutableRawPointer>,
             logger.trace("\(swiftTag, privacy: .public)  \(swiftMessage, 
privacy: .public)")
         default: break
     }
-//    native.internalOnLog(message: swiftMessage)
 }
 // MARK: -
-public class Quickjs {
+public class Quickjs {      // acts as singleton, since only one instance ever 
exists
     var talerWalletInstance: OpaquePointer!
     public weak var messageHandler: QuickjsMessageHandler?
     var logger: Logger
 
+    @Atomic(default: 0)
+    private var lastRequestID: Int32
+    private var requests: [Int32: QuickDataTask] = [:]
+
+    private lazy var urlSession: URLSession = {
+        return URLSession(configuration: .default)
+    }()
+
     public init() {
         self.logger = Logger(subsystem: "net.taler.gnu", category: "QuickJS")
         self.talerWalletInstance = TALER_WALLET_create()
@@ -60,18 +67,48 @@ public class Quickjs {
         TALER_WALLET_set_log_handler(talerWalletInstance,
                                      logging_callback,
                                      Unmanaged.passUnretained(self).toOpaque())
-        let http_curl = js_curl_http_client_create()
-        TALER_set_http_client_implementation(talerWalletInstance, http_curl)
+#if USE_HTTP_CLIENT_CURL
+        let http_impl = js_curl_http_client_create()
+#else
+        let http_impl = TALER_pack_http_client_implementation(request_create, 
request_cancel,
+                                                              
Unmanaged.passUnretained(self).toOpaque())
+#endif
+        // http_impl got malloc'd, and could possibly be free'd when the app 
terminates
+        TALER_set_http_client_implementation(talerWalletInstance, http_impl)
+        // - but we never free anything on termination, thus we don't save 
http_impl here
         TALER_WALLET_run(talerWalletInstance)
     }
 
-    deinit {
-        // FIXME: TALER_WALLET_destroy
-//        TALER_WALLET_destroy(talerWalletInstance)
+    func reqCreate(_ request: URLRequest,
+                _ responseCb: JSHttpResponseCb?,
+             _ responseCbCls: Optional<UnsafeMutableRawPointer>) -> Int32 {
+        let requestID = $lastRequestID.atomicIncrement()
+        let quickDataTask = QuickDataTask(urlSession: urlSession,
+                                             request: request,
+                                           requestID: requestID,
+                                            requests: &requests,
+                                          responseCb: responseCb,
+                                       responseCbCls: responseCbCls)
+        quickDataTask.run()
+        requests[requestID] = quickDataTask
+        return requestID
     }
 
+    func reqCancel(_ requestID: Int32) -> Int32 {
+        if let quickDataTask = requests[requestID] {
+            if let dataTask = quickDataTask.dataTask {
+                dataTask.cancel()
+            }
+        }
+        requests[requestID] = nil
+        return 0
+    }
 
-    public func internalOnNotify(payload: String) {
+    deinit {
+        // No need to call TALER_WALLET_destroy - memory gets purged anyway
+    }
+
+    func internalOnNotify(payload: String) {
         if let handler = messageHandler {
             handler.handleMessage(message: payload)
         }
@@ -90,17 +127,4 @@ public class Quickjs {
     public func sendMessage(message: String) {
         TALER_WALLET_send_request(talerWalletInstance, message)
     }
-
-    /// Note: This *must* be called before releasing the object, or else the 
thread will keep going.
-//    public func waitStopped() {
-//        scheduleNodeThreadSync {
-//            self.stopped = true
-//        }
-//        thread.cancel()
-//    }
-
-//    public func putModuleCode(modName: String, code: String) {
-//        __putModuleCodeNative(self.instance, modName.cString(using: .utf8),
-//                              code.cString(using: .utf8))
-//    }
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]