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()
}
}
}
|