mt-lang 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/index.html +4 -3
- data/lib/milk_tea/base.rb +1 -1
- data/std/asset_pack.mt +267 -0
- data/std/async/libuv_runtime.mt +539 -0
- data/std/async/mailbox.mt +191 -0
- data/std/async/runtime.mt +87 -0
- data/std/async.mt +87 -0
- data/std/base64.mt +185 -0
- data/std/behavior_tree.mt +396 -0
- data/std/binary.mt +314 -0
- data/std/binary_heap.mt +157 -0
- data/std/bitset.mt +239 -0
- data/std/box2d.mt +535 -0
- data/std/bytes.mt +46 -0
- data/std/c/box2d.mt +1254 -0
- data/std/c/cgltf.mt +647 -0
- data/std/c/cjson.mt +106 -0
- data/std/c/crypto.mt +11 -0
- data/std/c/crypto_support.h +51 -0
- data/std/c/ctype.mt +19 -0
- data/std/c/ctype_bindgen.h +29 -0
- data/std/c/curl.mt +1223 -0
- data/std/c/enet.mt +473 -0
- data/std/c/errno.mt +41 -0
- data/std/c/errno_bindgen.h +53 -0
- data/std/c/flecs.mt +2945 -0
- data/std/c/fs.linux.mt +43 -0
- data/std/c/fs.windows.mt +43 -0
- data/std/c/fs_support.h +954 -0
- data/std/c/gl.mt +2065 -0
- data/std/c/gl_registry_helpers.h +10748 -0
- data/std/c/glfw.mt +499 -0
- data/std/c/libc.mt +167 -0
- data/std/c/libuv.mt +1491 -0
- data/std/c/math.mt +22 -0
- data/std/c/math_bindgen.h +31 -0
- data/std/c/miniaudio.mt +3420 -0
- data/std/c/pcre2.mt +735 -0
- data/std/c/process.mt +58 -0
- data/std/c/process_support.h +1147 -0
- data/std/c/raygui.mt +1602 -0
- data/std/c/raylib.mt +1245 -0
- data/std/c/raymath.mt +163 -0
- data/std/c/rlgl.mt +434 -0
- data/std/c/rpng.mt +60 -0
- data/std/c/rres.mt +187 -0
- data/std/c/sdl3.mt +4132 -0
- data/std/c/sqlite3.mt +951 -0
- data/std/c/stb_image.mt +52 -0
- data/std/c/stb_image_resize2.mt +128 -0
- data/std/c/stb_image_write.mt +19 -0
- data/std/c/stb_rect_pack.mt +38 -0
- data/std/c/stb_truetype.mt +175 -0
- data/std/c/stb_vorbis.mt +31 -0
- data/std/c/stdio.mt +90 -0
- data/std/c/steamworks.h +5769 -0
- data/std/c/steamworks.mt +4771 -0
- data/std/c/string.mt +16 -0
- data/std/c/string_bindgen.h +36 -0
- data/std/c/sync.mt +33 -0
- data/std/c/sync_support.h +219 -0
- data/std/c/terminal.mt +24 -0
- data/std/c/terminal_support.h +408 -0
- data/std/c/time.mt +26 -0
- data/std/c/tls.mt +24 -0
- data/std/c/tls_support.h +626 -0
- data/std/c/tracy.mt +53 -0
- data/std/c/zlib.mt +20 -0
- data/std/c/zlib_support.h +239 -0
- data/std/c/zstd.mt +231 -0
- data/std/cell.mt +60 -0
- data/std/cgltf.mt +115 -0
- data/std/cjson.mt +90 -0
- data/std/cli.mt +768 -0
- data/std/cookie.mt +198 -0
- data/std/counter.mt +160 -0
- data/std/crypto.mt +74 -0
- data/std/cstring.mt +13 -0
- data/std/ctype.mt +52 -0
- data/std/curl/runtime.mt +261 -0
- data/std/curl.mt +38 -0
- data/std/deque.mt +348 -0
- data/std/encoding.mt +93 -0
- data/std/enet.mt +133 -0
- data/std/env.mt +41 -0
- data/std/errno.mt +47 -0
- data/std/flecs.mt +978 -0
- data/std/fmt.mt +271 -0
- data/std/fs.linux.mt +696 -0
- data/std/fs.windows.mt +711 -0
- data/std/fsm.mt +267 -0
- data/std/gl.mt +2062 -0
- data/std/glfw.mt +471 -0
- data/std/goap.mt +342 -0
- data/std/graph.mt +521 -0
- data/std/gzip.mt +77 -0
- data/std/hash.mt +429 -0
- data/std/http/server.mt +810 -0
- data/std/http.mt +1227 -0
- data/std/intern.mt +53 -0
- data/std/jobs.mt +288 -0
- data/std/json.mt +627 -0
- data/std/libc.mt +34 -0
- data/std/libuv.mt +554 -0
- data/std/linear_algebra.mt +243 -0
- data/std/linked_map.mt +372 -0
- data/std/linked_map_view.mt +49 -0
- data/std/linked_set.mt +117 -0
- data/std/log.mt +69 -0
- data/std/map.mt +418 -0
- data/std/math.mt +24 -0
- data/std/mem/arena.mt +137 -0
- data/std/mem/endian.mt +51 -0
- data/std/mem/heap.mt +298 -0
- data/std/mem/pool.mt +164 -0
- data/std/mem/stack.mt +47 -0
- data/std/mem/tracking.mt +119 -0
- data/std/miniaudio.mt +1299 -0
- data/std/multiset.mt +191 -0
- data/std/net/channel.mt +727 -0
- data/std/net/clock.mt +237 -0
- data/std/net/discovery.mt +277 -0
- data/std/net/lobby.mt +813 -0
- data/std/net/manager.mt +539 -0
- data/std/net/mux.mt +839 -0
- data/std/net/nat.mt +122 -0
- data/std/net/packet.mt +219 -0
- data/std/net/punch.mt +162 -0
- data/std/net/rpc.mt +119 -0
- data/std/net/session.mt +1157 -0
- data/std/net/stun.mt +252 -0
- data/std/net/sync.mt +183 -0
- data/std/net/turn.mt +459 -0
- data/std/net.mt +2944 -0
- data/std/oauth2.mt +408 -0
- data/std/option.mt +51 -0
- data/std/ordered_map.mt +497 -0
- data/std/ordered_set.mt +348 -0
- data/std/path.mt +368 -0
- data/std/pcre2/runtime.mt +35 -0
- data/std/pcre2.mt +78 -0
- data/std/priority_queue.mt +55 -0
- data/std/process.mt +837 -0
- data/std/queue.mt +55 -0
- data/std/random.mt +146 -0
- data/std/raygui.mt +99 -0
- data/std/raylib/debug_console.mt +71 -0
- data/std/raylib/easing.mt +388 -0
- data/std/raylib/packed_assets.mt +255 -0
- data/std/raylib/runtime.mt +30 -0
- data/std/raylib/tracy_gpu.mt +37 -0
- data/std/raylib.mt +1510 -0
- data/std/raymath.mt +153 -0
- data/std/result.mt +89 -0
- data/std/rlgl.mt +268 -0
- data/std/rpng.mt +46 -0
- data/std/rres.mt +36 -0
- data/std/sdl3/runtime.mt +54 -0
- data/std/sdl3.mt +1731 -0
- data/std/serialize.mt +68 -0
- data/std/set.mt +124 -0
- data/std/spatial.mt +176 -0
- data/std/sqlite3.mt +151 -0
- data/std/stack.mt +55 -0
- data/std/stb_image.mt +47 -0
- data/std/stb_image_resize2.mt +41 -0
- data/std/stb_image_write.mt +17 -0
- data/std/stb_rect_pack.mt +15 -0
- data/std/stb_truetype.mt +77 -0
- data/std/stb_vorbis.mt +16 -0
- data/std/stdio.mt +88 -0
- data/std/steamworks.mt +1542 -0
- data/std/str.mt +293 -0
- data/std/string.mt +234 -0
- data/std/sync.mt +194 -0
- data/std/tar.mt +704 -0
- data/std/terminal.mt +1002 -0
- data/std/testing.mt +266 -0
- data/std/thread.mt +120 -0
- data/std/time.mt +105 -0
- data/std/tls.mt +616 -0
- data/std/toml.mt +1310 -0
- data/std/tracy.mt +42 -0
- data/std/uri.mt +118 -0
- data/std/url.mt +372 -0
- data/std/vec.mt +433 -0
- data/std/zstd.mt +94 -0
- metadata +187 -2
data/std/tls.mt
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import std.async as aio
|
|
2
|
+
import std.async.libuv_runtime as aio_backend
|
|
3
|
+
import std.bytes as bytes
|
|
4
|
+
import std.c.tls as c
|
|
5
|
+
import std.fmt as fmt
|
|
6
|
+
import std.libuv as libuv
|
|
7
|
+
import std.mem.arena as arena
|
|
8
|
+
import std.mem.heap as heap
|
|
9
|
+
import std.net as net
|
|
10
|
+
import std.str as text
|
|
11
|
+
import std.string as string
|
|
12
|
+
import std.vec as vec
|
|
13
|
+
|
|
14
|
+
type NativeHandle = libuv.uv_handle_t
|
|
15
|
+
type NativePollHandle = libuv.uv_poll_t
|
|
16
|
+
|
|
17
|
+
const tls_io_ready: int = 0
|
|
18
|
+
const tls_io_want_read: int = 1
|
|
19
|
+
const tls_io_want_write: int = 2
|
|
20
|
+
const tls_io_eof: int = 3
|
|
21
|
+
|
|
22
|
+
public struct Error:
|
|
23
|
+
code: int
|
|
24
|
+
message: string.String
|
|
25
|
+
|
|
26
|
+
public struct Stream:
|
|
27
|
+
state: ptr[StreamState]?
|
|
28
|
+
|
|
29
|
+
struct StreamState:
|
|
30
|
+
client: ptr[c.mt_tls_client]?
|
|
31
|
+
tcp: net.TcpStream
|
|
32
|
+
fd: int
|
|
33
|
+
pending_operation: bool
|
|
34
|
+
|
|
35
|
+
struct PollState:
|
|
36
|
+
ready: bool
|
|
37
|
+
status_code: int
|
|
38
|
+
revents: int
|
|
39
|
+
error: Error
|
|
40
|
+
error_owned: bool
|
|
41
|
+
waiter_frame: ptr[void]?
|
|
42
|
+
waiter: fn(frame: ptr[void]) -> void
|
|
43
|
+
waiter_registered: bool
|
|
44
|
+
handle: ptr[NativePollHandle]?
|
|
45
|
+
closing: bool
|
|
46
|
+
closed: bool
|
|
47
|
+
released: bool
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function take_owned_string(data: ptr[char]?, len: ptr_uint) -> string.String:
|
|
51
|
+
if data == null:
|
|
52
|
+
if len != 0:
|
|
53
|
+
fatal(c"tls.take_owned_string missing storage")
|
|
54
|
+
|
|
55
|
+
return string.String.create()
|
|
56
|
+
|
|
57
|
+
return unsafe: string.String(data = ptr[ubyte]<-data, len = len, capacity = len, owns_storage = true)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function take_owned_bytes(data: ptr[ubyte]?, len: ptr_uint) -> bytes.Bytes:
|
|
61
|
+
if data == null:
|
|
62
|
+
if len != 0:
|
|
63
|
+
fatal(c"tls.take_owned_bytes missing storage")
|
|
64
|
+
|
|
65
|
+
return bytes.Bytes.empty()
|
|
66
|
+
|
|
67
|
+
return unsafe: bytes.Bytes(data = own[ubyte]<-data, len = len)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
function take_error(raw: c.mt_tls_error, fallback: str) -> Error:
|
|
71
|
+
if raw.message_data == null and raw.message_len == 0:
|
|
72
|
+
return Error(code = raw.code, message = string.String.from_str(fallback))
|
|
73
|
+
|
|
74
|
+
return Error(code = raw.code, message = take_owned_string(raw.message_data, raw.message_len))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function take_net_error(raw: net.Error) -> Error:
|
|
78
|
+
return Error(code = raw.code, message = raw.message)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
function empty_error() -> Error:
|
|
82
|
+
return Error(code = 0, message = string.String.create())
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
function tls_error(message: str) -> Error:
|
|
86
|
+
return Error(code = -1, message = string.String.from_str(message))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function libuv_error(code: int) -> Error:
|
|
90
|
+
return Error(code = code, message = string.String.from_str(text.cstr_as_str(libuv.strerror(code))))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
function release_socket_addresses(values: ref[vec.Vec[net.SocketAddress]]) -> void:
|
|
94
|
+
var index: ptr_uint = 0
|
|
95
|
+
while index < values.len:
|
|
96
|
+
let current = values.get(index) else:
|
|
97
|
+
fatal(c"tls release_socket_addresses missing value")
|
|
98
|
+
|
|
99
|
+
var address = unsafe: read(current)
|
|
100
|
+
address.release()
|
|
101
|
+
index += 1
|
|
102
|
+
|
|
103
|
+
values.release()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
function poll_state(frame: ptr[void]) -> ptr[PollState]:
|
|
107
|
+
return unsafe: ptr[PollState]<-frame
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
function poll_as_handle(handle: ptr[NativePollHandle]) -> ptr[NativeHandle]:
|
|
111
|
+
return unsafe: ptr[NativeHandle]<-handle
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
function handle_as_poll(handle: ptr[NativeHandle]) -> ptr[NativePollHandle]:
|
|
115
|
+
return unsafe: ptr[NativePollHandle]<-handle
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
function noop_waiter(frame: ptr[void]) -> void:
|
|
119
|
+
unsafe: frame
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function poll_task(state: ptr[PollState]) -> Task[Result[int, Error]]:
|
|
123
|
+
return unsafe: Task[Result[int, Error]](
|
|
124
|
+
frame = ptr[void]<-state,
|
|
125
|
+
ready = poll_ready,
|
|
126
|
+
set_waiter = poll_set_waiter,
|
|
127
|
+
release = poll_release,
|
|
128
|
+
take_result = poll_take_result
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
function release_poll_error(state: ptr[PollState]) -> void:
|
|
133
|
+
unsafe:
|
|
134
|
+
if state.error_owned:
|
|
135
|
+
state.error.release()
|
|
136
|
+
state.error = empty_error()
|
|
137
|
+
state.error_owned = false
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
function poll_close_callback(handle: ptr[NativeHandle]) -> void:
|
|
141
|
+
let state_raw = libuv.handle_get_data(handle) else:
|
|
142
|
+
unsafe: heap.release_bytes(ptr[void]<-handle_as_poll(handle))
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
let state = unsafe: ptr[PollState]<-state_raw
|
|
146
|
+
unsafe:
|
|
147
|
+
heap.release_bytes(ptr[void]<-handle_as_poll(handle))
|
|
148
|
+
state.handle = null
|
|
149
|
+
state.closed = true
|
|
150
|
+
|
|
151
|
+
if state.released:
|
|
152
|
+
release_poll_error(state)
|
|
153
|
+
heap.release(state)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
function close_poll_handle(state: ptr[PollState]) -> void:
|
|
157
|
+
unsafe:
|
|
158
|
+
let poll = state.handle else:
|
|
159
|
+
state.closed = true
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
if state.closing or state.closed:
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
state.closing = true
|
|
166
|
+
libuv.poll_stop(poll)
|
|
167
|
+
libuv.close(poll_as_handle(poll), poll_close_callback)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
function finish_poll_success(state: ptr[PollState], revents: int) -> void:
|
|
171
|
+
unsafe:
|
|
172
|
+
release_poll_error(state)
|
|
173
|
+
state.ready = true
|
|
174
|
+
state.status_code = 0
|
|
175
|
+
state.revents = revents
|
|
176
|
+
close_poll_handle(state)
|
|
177
|
+
|
|
178
|
+
if state.waiter_registered:
|
|
179
|
+
state.waiter(ptr[void]<-state.waiter_frame)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
function finish_poll_failure(state: ptr[PollState], error: Error, status_code: int) -> void:
|
|
183
|
+
unsafe:
|
|
184
|
+
release_poll_error(state)
|
|
185
|
+
state.ready = true
|
|
186
|
+
state.status_code = status_code
|
|
187
|
+
state.revents = 0
|
|
188
|
+
state.error = error
|
|
189
|
+
state.error_owned = true
|
|
190
|
+
close_poll_handle(state)
|
|
191
|
+
|
|
192
|
+
if state.waiter_registered:
|
|
193
|
+
state.waiter(ptr[void]<-state.waiter_frame)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
function poll_ready(frame: ptr[void]) -> bool:
|
|
197
|
+
return unsafe: poll_state(frame).ready
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
function poll_set_waiter(frame: ptr[void], waiter_frame: ptr[void], waiter: fn(frame: ptr[void]) -> void) -> void:
|
|
201
|
+
let state = poll_state(frame)
|
|
202
|
+
unsafe:
|
|
203
|
+
if state.ready:
|
|
204
|
+
waiter(waiter_frame)
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
state.waiter_frame = waiter_frame
|
|
208
|
+
state.waiter = waiter
|
|
209
|
+
state.waiter_registered = true
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
function poll_release(frame: ptr[void]) -> void:
|
|
213
|
+
let state = poll_state(frame)
|
|
214
|
+
|
|
215
|
+
unsafe:
|
|
216
|
+
if state.closed:
|
|
217
|
+
release_poll_error(state)
|
|
218
|
+
heap.release(state)
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
state.released = true
|
|
222
|
+
if state.handle == null:
|
|
223
|
+
release_poll_error(state)
|
|
224
|
+
heap.release(state)
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
close_poll_handle(state)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
function poll_take_result(frame: ptr[void]) -> Result[int, Error]:
|
|
231
|
+
let state = poll_state(frame)
|
|
232
|
+
unsafe:
|
|
233
|
+
if state.status_code == 0:
|
|
234
|
+
return Result[int, Error].success(value = state.revents)
|
|
235
|
+
|
|
236
|
+
let error = state.error
|
|
237
|
+
state.error = empty_error()
|
|
238
|
+
state.error_owned = false
|
|
239
|
+
return Result[int, Error].failure(error = error)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
function poll_callback(handle: ptr[NativePollHandle], status_code: int, events: int) -> void:
|
|
243
|
+
let state_raw = libuv.handle_get_data(poll_as_handle(handle)) else:
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
let state = unsafe: ptr[PollState]<-state_raw
|
|
247
|
+
if status_code != 0:
|
|
248
|
+
finish_poll_failure(state, libuv_error(status_code), status_code)
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
finish_poll_success(state, events)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
function alloc_poll_handle() -> ptr[NativePollHandle]:
|
|
255
|
+
let handle_size = libuv.handle_size(libuv.uv_handle_type.UV_POLL)
|
|
256
|
+
return unsafe: ptr[NativePollHandle]<-heap.must_alloc_zeroed_bytes(1, handle_size)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
function poll_events_for_status(status_code: int) -> int:
|
|
260
|
+
if status_code == tls_io_want_read:
|
|
261
|
+
return int<-(libuv.uv_poll_event.UV_READABLE | libuv.uv_poll_event.UV_DISCONNECT)
|
|
262
|
+
|
|
263
|
+
if status_code == tls_io_want_write:
|
|
264
|
+
return int<-libuv.uv_poll_event.UV_WRITABLE
|
|
265
|
+
|
|
266
|
+
fatal(c"tls.poll_events_for_status requires WANT_READ or WANT_WRITE")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
function poll_on(runtime: aio.Runtime, fd: int, events: int) -> Task[Result[int, Error]]:
|
|
270
|
+
let loop = aio_backend.runtime_loop(runtime)
|
|
271
|
+
let state = heap.must_alloc_zeroed[PollState](1)
|
|
272
|
+
let poll = alloc_poll_handle()
|
|
273
|
+
|
|
274
|
+
unsafe:
|
|
275
|
+
state.ready = false
|
|
276
|
+
state.status_code = 0
|
|
277
|
+
state.revents = 0
|
|
278
|
+
state.error = empty_error()
|
|
279
|
+
state.error_owned = false
|
|
280
|
+
state.waiter_frame = null
|
|
281
|
+
state.waiter = noop_waiter
|
|
282
|
+
state.waiter_registered = false
|
|
283
|
+
state.handle = poll
|
|
284
|
+
state.closing = false
|
|
285
|
+
state.closed = false
|
|
286
|
+
state.released = false
|
|
287
|
+
|
|
288
|
+
let init_status = libuv.poll_init(loop, poll, fd)
|
|
289
|
+
if init_status != 0:
|
|
290
|
+
unsafe:
|
|
291
|
+
heap.release_bytes(ptr[void]<-poll)
|
|
292
|
+
state.handle = null
|
|
293
|
+
state.closed = true
|
|
294
|
+
finish_poll_failure(state, libuv_error(init_status), init_status)
|
|
295
|
+
return poll_task(state)
|
|
296
|
+
|
|
297
|
+
unsafe:
|
|
298
|
+
libuv.handle_set_data(poll_as_handle(poll), ptr[void]<-state)
|
|
299
|
+
|
|
300
|
+
let start_status = libuv.poll_start(poll, events, poll_callback)
|
|
301
|
+
if start_status != 0:
|
|
302
|
+
finish_poll_failure(state, libuv_error(start_status), start_status)
|
|
303
|
+
|
|
304
|
+
return poll_task(state)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
async function wait_for_io(runtime: aio.Runtime, fd: int, status_code: int) -> Result[bool, Error]:
|
|
308
|
+
let poll_result = await poll_on(runtime, fd, poll_events_for_status(status_code))
|
|
309
|
+
poll_result?
|
|
310
|
+
return Result[bool, Error].success(value = true)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
function begin_stream_operation(state_raw: ptr[StreamState]?) -> Result[ptr[StreamState], Error]:
|
|
314
|
+
let state = state_raw else:
|
|
315
|
+
return Result[ptr[StreamState], Error].failure(error = tls_error("tls stream is released"))
|
|
316
|
+
|
|
317
|
+
unsafe:
|
|
318
|
+
if state.client == null:
|
|
319
|
+
return Result[ptr[StreamState], Error].failure(error = tls_error("tls stream is released"))
|
|
320
|
+
|
|
321
|
+
if state.pending_operation:
|
|
322
|
+
return Result[
|
|
323
|
+
ptr[StreamState],
|
|
324
|
+
Error
|
|
325
|
+
].failure(error = tls_error("tls stream already has a pending operation"))
|
|
326
|
+
|
|
327
|
+
state.pending_operation = true
|
|
328
|
+
|
|
329
|
+
return Result[ptr[StreamState], Error].success(value = state)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
function end_stream_operation(state: ptr[StreamState]) -> void:
|
|
333
|
+
unsafe: state.pending_operation = false
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
async function handshake_on(runtime: aio.Runtime, state_raw: ptr[StreamState]?) -> Result[bool, Error]:
|
|
337
|
+
let state = begin_stream_operation(state_raw)?
|
|
338
|
+
defer end_stream_operation(state)
|
|
339
|
+
|
|
340
|
+
while true:
|
|
341
|
+
let client = unsafe: read(state).client else:
|
|
342
|
+
return Result[bool, Error].failure(error = tls_error("tls stream is released"))
|
|
343
|
+
|
|
344
|
+
var raw_error = zero[c.mt_tls_error]
|
|
345
|
+
let status_code = c.mt_tls_client_handshake(client, raw_error)
|
|
346
|
+
if status_code == tls_io_ready:
|
|
347
|
+
return Result[bool, Error].success(value = true)
|
|
348
|
+
|
|
349
|
+
if status_code == tls_io_want_read or status_code == tls_io_want_write:
|
|
350
|
+
let wait_result = await wait_for_io(runtime, unsafe: read(state).fd, status_code)
|
|
351
|
+
wait_result?
|
|
352
|
+
continue
|
|
353
|
+
|
|
354
|
+
if status_code == tls_io_eof:
|
|
355
|
+
return Result[bool, Error].failure(error = tls_error("tls stream closed during handshake"))
|
|
356
|
+
|
|
357
|
+
return Result[bool, Error].failure(error = take_error(raw_error, "tls connect failed"))
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
async function write_on(
|
|
361
|
+
runtime: aio.Runtime,
|
|
362
|
+
state_raw: ptr[StreamState]?,
|
|
363
|
+
content: span[ubyte]
|
|
364
|
+
) -> Result[ptr_uint, Error]:
|
|
365
|
+
if content.len == 0:
|
|
366
|
+
return Result[ptr_uint, Error].success(value = 0)
|
|
367
|
+
|
|
368
|
+
let state = begin_stream_operation(state_raw)?
|
|
369
|
+
defer end_stream_operation(state)
|
|
370
|
+
|
|
371
|
+
var offset: ptr_uint = 0
|
|
372
|
+
while offset < content.len:
|
|
373
|
+
let client = unsafe: read(state).client else:
|
|
374
|
+
return Result[ptr_uint, Error].failure(error = tls_error("tls stream is released"))
|
|
375
|
+
|
|
376
|
+
var transferred: ptr_uint = 0
|
|
377
|
+
var raw_error = zero[c.mt_tls_error]
|
|
378
|
+
let status_code = c.mt_tls_client_write(
|
|
379
|
+
client,
|
|
380
|
+
unsafe: content.data + offset,
|
|
381
|
+
content.len - offset,
|
|
382
|
+
transferred,
|
|
383
|
+
raw_error
|
|
384
|
+
)
|
|
385
|
+
if status_code == tls_io_ready:
|
|
386
|
+
if transferred == 0:
|
|
387
|
+
return Result[ptr_uint, Error].failure(error = tls_error("tls write made no progress"))
|
|
388
|
+
|
|
389
|
+
offset += transferred
|
|
390
|
+
continue
|
|
391
|
+
|
|
392
|
+
if status_code == tls_io_want_read or status_code == tls_io_want_write:
|
|
393
|
+
let wait_result = await wait_for_io(runtime, unsafe: read(state).fd, status_code)
|
|
394
|
+
wait_result?
|
|
395
|
+
continue
|
|
396
|
+
|
|
397
|
+
if status_code == tls_io_eof:
|
|
398
|
+
return Result[ptr_uint, Error].failure(error = tls_error("tls stream closed during write"))
|
|
399
|
+
|
|
400
|
+
return Result[ptr_uint, Error].failure(error = take_error(raw_error, "tls write failed"))
|
|
401
|
+
|
|
402
|
+
return Result[ptr_uint, Error].success(value = offset)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
async function read_once_on(
|
|
406
|
+
runtime: aio.Runtime,
|
|
407
|
+
state_raw: ptr[StreamState]?,
|
|
408
|
+
max_bytes: ptr_uint
|
|
409
|
+
) -> Result[bytes.Bytes, Error]:
|
|
410
|
+
if max_bytes == 0:
|
|
411
|
+
return Result[bytes.Bytes, Error].failure(error = tls_error("tls read requires max_bytes > 0"))
|
|
412
|
+
|
|
413
|
+
let state = begin_stream_operation(state_raw)?
|
|
414
|
+
defer end_stream_operation(state)
|
|
415
|
+
|
|
416
|
+
let buffer = heap.must_alloc[ubyte](max_bytes)
|
|
417
|
+
while true:
|
|
418
|
+
let client = unsafe: read(state).client else:
|
|
419
|
+
unsafe: heap.release(buffer)
|
|
420
|
+
return Result[bytes.Bytes, Error].failure(error = tls_error("tls stream is released"))
|
|
421
|
+
|
|
422
|
+
var transferred: ptr_uint = 0
|
|
423
|
+
var raw_error = zero[c.mt_tls_error]
|
|
424
|
+
let status_code = c.mt_tls_client_read(client, buffer, max_bytes, transferred, raw_error)
|
|
425
|
+
if status_code == tls_io_ready:
|
|
426
|
+
if transferred == 0:
|
|
427
|
+
heap.release(buffer)
|
|
428
|
+
return Result[bytes.Bytes, Error].success(value = bytes.Bytes.empty())
|
|
429
|
+
|
|
430
|
+
return Result[bytes.Bytes, Error].success(value = bytes.Bytes(data = buffer, len = transferred))
|
|
431
|
+
|
|
432
|
+
if status_code == tls_io_eof:
|
|
433
|
+
heap.release(buffer)
|
|
434
|
+
return Result[bytes.Bytes, Error].success(value = bytes.Bytes.empty())
|
|
435
|
+
|
|
436
|
+
if status_code == tls_io_want_read or status_code == tls_io_want_write:
|
|
437
|
+
let wait_result = await wait_for_io(runtime, unsafe: read(state).fd, status_code)
|
|
438
|
+
match wait_result:
|
|
439
|
+
Result.failure as wait_error_payload:
|
|
440
|
+
heap.release(buffer)
|
|
441
|
+
return Result[bytes.Bytes, Error].failure(error = wait_error_payload.error)
|
|
442
|
+
Result.success as wait_payload:
|
|
443
|
+
wait_payload.value
|
|
444
|
+
continue
|
|
445
|
+
|
|
446
|
+
heap.release(buffer)
|
|
447
|
+
return Result[bytes.Bytes, Error].failure(error = take_error(raw_error, "tls read failed"))
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
async function shutdown_on(runtime: aio.Runtime, state_raw: ptr[StreamState]?) -> Result[bool, Error]:
|
|
451
|
+
let state = begin_stream_operation(state_raw)?
|
|
452
|
+
defer end_stream_operation(state)
|
|
453
|
+
|
|
454
|
+
while true:
|
|
455
|
+
let client = unsafe: read(state).client else:
|
|
456
|
+
return Result[bool, Error].failure(error = tls_error("tls stream is released"))
|
|
457
|
+
|
|
458
|
+
var raw_error = zero[c.mt_tls_error]
|
|
459
|
+
let status_code = c.mt_tls_client_shutdown(client, raw_error)
|
|
460
|
+
if status_code == tls_io_ready:
|
|
461
|
+
return Result[bool, Error].success(value = true)
|
|
462
|
+
|
|
463
|
+
if status_code == tls_io_want_read or status_code == tls_io_want_write:
|
|
464
|
+
let wait_result = await wait_for_io(runtime, unsafe: read(state).fd, status_code)
|
|
465
|
+
wait_result?
|
|
466
|
+
continue
|
|
467
|
+
|
|
468
|
+
return Result[bool, Error].failure(error = take_error(raw_error, "tls shutdown failed"))
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
public async function connect_on(runtime: aio.Runtime, host: str, port: int) -> Result[Stream, Error]:
|
|
472
|
+
var service = fmt.to_string_int(port)
|
|
473
|
+
defer service.release()
|
|
474
|
+
|
|
475
|
+
let addresses_result = await net.resolve_all_on(runtime, host, service.as_str())
|
|
476
|
+
match addresses_result:
|
|
477
|
+
Result.failure as payload:
|
|
478
|
+
return Result[Stream, Error].failure(error = take_net_error(payload.error))
|
|
479
|
+
Result.success as payload:
|
|
480
|
+
var addresses = payload.value
|
|
481
|
+
defer release_socket_addresses(ref_of(addresses))
|
|
482
|
+
|
|
483
|
+
var last_error = tls_error("tls connect failed")
|
|
484
|
+
var last_error_owned = true
|
|
485
|
+
defer:
|
|
486
|
+
if last_error_owned:
|
|
487
|
+
last_error.release()
|
|
488
|
+
|
|
489
|
+
var index: ptr_uint = 0
|
|
490
|
+
while index < addresses.len:
|
|
491
|
+
let current = addresses.get(index) else:
|
|
492
|
+
fatal(c"tls.connect_on missing resolved address")
|
|
493
|
+
|
|
494
|
+
let connect_result = await net.connect_on(runtime, unsafe: read(current))
|
|
495
|
+
match connect_result:
|
|
496
|
+
Result.failure as connect_error_payload:
|
|
497
|
+
if last_error_owned:
|
|
498
|
+
last_error.release()
|
|
499
|
+
last_error = take_net_error(connect_error_payload.error)
|
|
500
|
+
last_error_owned = true
|
|
501
|
+
Result.success as connect_payload:
|
|
502
|
+
if last_error_owned:
|
|
503
|
+
last_error.release()
|
|
504
|
+
return await client_on(runtime, host, connect_payload.value)
|
|
505
|
+
|
|
506
|
+
index += 1
|
|
507
|
+
|
|
508
|
+
return Result[Stream, Error].failure(error = last_error)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
public function connect(host: str, port: int) -> Task[Result[Stream, Error]]:
|
|
512
|
+
return connect_on(aio.current_runtime(), host, port)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
public async function client_on(runtime: aio.Runtime, host: str, transport: net.TcpStream) -> Result[Stream, Error]:
|
|
516
|
+
var owned_transport = transport
|
|
517
|
+
|
|
518
|
+
let fd_result = owned_transport.socket_fd()
|
|
519
|
+
match fd_result:
|
|
520
|
+
Result.failure as payload:
|
|
521
|
+
owned_transport.release()
|
|
522
|
+
return Result[Stream, Error].failure(error = take_net_error(payload.error))
|
|
523
|
+
Result.success as payload:
|
|
524
|
+
var raw_client: ptr[c.mt_tls_client]? = null
|
|
525
|
+
var host_storage = arena.create(host.len + 1)
|
|
526
|
+
defer host_storage.release()
|
|
527
|
+
|
|
528
|
+
var raw_error = zero[c.mt_tls_error]
|
|
529
|
+
let create_status = c.mt_tls_client_create(host_storage.to_cstr(host), payload.value, raw_client, raw_error)
|
|
530
|
+
if create_status != 0:
|
|
531
|
+
owned_transport.release()
|
|
532
|
+
return Result[Stream, Error].failure(error = take_error(raw_error, "tls connect failed"))
|
|
533
|
+
|
|
534
|
+
let live_client = raw_client else:
|
|
535
|
+
owned_transport.release()
|
|
536
|
+
return Result[Stream, Error].failure(error = tls_error("tls connect failed"))
|
|
537
|
+
|
|
538
|
+
let state = heap.must_alloc_zeroed[StreamState](1)
|
|
539
|
+
unsafe:
|
|
540
|
+
state.client = live_client
|
|
541
|
+
state.tcp = owned_transport
|
|
542
|
+
state.fd = payload.value
|
|
543
|
+
state.pending_operation = false
|
|
544
|
+
|
|
545
|
+
var stream = Stream(state = state)
|
|
546
|
+
let handshake_result = await handshake_on(runtime, state)
|
|
547
|
+
match handshake_result:
|
|
548
|
+
Result.failure as handshake_payload:
|
|
549
|
+
stream.release()
|
|
550
|
+
return Result[Stream, Error].failure(error = handshake_payload.error)
|
|
551
|
+
Result.success as handshake_payload:
|
|
552
|
+
handshake_payload.value
|
|
553
|
+
return Result[Stream, Error].success(value = stream)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
public function client(host: str, transport: net.TcpStream) -> Task[Result[Stream, Error]]:
|
|
557
|
+
return client_on(aio.current_runtime(), host, transport)
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
extending Error:
|
|
561
|
+
public editable function release() -> void:
|
|
562
|
+
this.message.release()
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
extending Stream:
|
|
566
|
+
public editable function release() -> void:
|
|
567
|
+
let state = this.state else:
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
unsafe:
|
|
571
|
+
if state.pending_operation:
|
|
572
|
+
fatal(c"tls stream released with a pending operation")
|
|
573
|
+
|
|
574
|
+
let client = state.client
|
|
575
|
+
if client != null[ptr[c.mt_tls_client]]:
|
|
576
|
+
c.mt_tls_client_release(client)
|
|
577
|
+
state.client = null
|
|
578
|
+
|
|
579
|
+
var transport = state.tcp
|
|
580
|
+
transport.release()
|
|
581
|
+
state.tcp = zero[net.TcpStream]
|
|
582
|
+
|
|
583
|
+
heap.release(state)
|
|
584
|
+
this.state = null
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
public function write_bytes(content: span[ubyte]) -> Task[Result[ptr_uint, Error]]:
|
|
588
|
+
return write_on(aio.current_runtime(), this.state, content)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
public function read_once(max_bytes: ptr_uint) -> Task[Result[bytes.Bytes, Error]]:
|
|
592
|
+
return read_once_on(aio.current_runtime(), this.state, max_bytes)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
public function shutdown() -> Task[Result[bool, Error]]:
|
|
596
|
+
return shutdown_on(aio.current_runtime(), this.state)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
public function exchange(host: str, port: int, request: span[ubyte]) -> Result[bytes.Bytes, Error]:
|
|
600
|
+
var host_storage = arena.create(host.len + 1)
|
|
601
|
+
defer host_storage.release()
|
|
602
|
+
|
|
603
|
+
var raw_response = zero[c.mt_tls_bytes]
|
|
604
|
+
var raw_error = zero[c.mt_tls_error]
|
|
605
|
+
let status_code = c.mt_tls_exchange(
|
|
606
|
+
host_storage.to_cstr(host),
|
|
607
|
+
port,
|
|
608
|
+
request.data,
|
|
609
|
+
request.len,
|
|
610
|
+
raw_response,
|
|
611
|
+
raw_error
|
|
612
|
+
)
|
|
613
|
+
if status_code != 0:
|
|
614
|
+
return Result[bytes.Bytes, Error].failure(error = take_error(raw_error, "tls exchange failed"))
|
|
615
|
+
|
|
616
|
+
return Result[bytes.Bytes, Error].success(value = take_owned_bytes(raw_response.data, raw_response.len))
|