summaryrefslogtreecommitdiff
path: root/TalerWallet1/Quickjs/QuickDataTask.swift
blob: e4afe88e92493f0d7a4dd54554f575cb09897b6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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()
        }
    }
}