-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcurl-mlton.sml
295 lines (218 loc) · 9.15 KB
/
curl-mlton.sml
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
structure Curl : CURL =
struct
exception Curl of int
structure Const = CurlConst
open Const
open MLton.Pointer
val malloc = (_import "malloc": Word.word -> t;) o Word.fromInt
val free = _import "free": t -> unit;
val global_init = _import "curl_global_init": int -> int;
val global_cleanup = _import "curl_global_cleanup": unit -> unit;
fun init () = global_init(CURL_GLOBAL_ALL)
val cleanup = global_cleanup
fun withCurl f = (
global_init(CURL_GLOBAL_ALL);
f () handle exc => (global_cleanup (); raise exc);
global_cleanup ()
)
fun readCString p =
let
fun len i = if getWord8 (p, i) = 0w0 then i else len (i + 1)
val length = len 0
fun getChar i = Byte.byteToChar (getWord8 (p, i))
in
CharVector.tabulate(length, getChar)
end
val version_ffi = _import "curl_version": unit -> t;
fun version () = let val p = version_ffi () in readCString p end
val is_64bit = sizeofPointer = 0w8
structure Easy =
struct
type curl = t
structure H = HashArrayLargeInt
val hash_size = 100
val headerfunction_cb_H = H.hash hash_size (* easy_int => cb *)
val writefunction_cb_H = H.hash hash_size (* easy_int => cb *)
val easy2int = C_Size.toLargeInt o C_Pointer.toWord
val init_ffi = _import "curl_easy_init": unit -> t;
fun init () =
let
val curl = init_ffi ()
in
if curl = null
then raise Curl CURLE_FAILED_INIT
else curl
end
val setopt_str_ffi = _import "curl_easy_setopt": t * int * string -> int;
fun setopt_str (curl, opt, str) = setopt_str_ffi (curl, opt, str ^ "\000")
val setopt_int_32bit = _import "curl_easy_setopt": t * int * Int32.int -> int;
val setopt_int_64bit = _import "curl_easy_setopt": t * int * Int64.int -> int;
fun setopt_int (p, opt, v) =
if is_64bit
then setopt_int_64bit (p, opt, (Int64.fromInt v) )
else setopt_int_32bit (p, opt, (Int32.fromInt v) )
val perform = _import "curl_easy_perform" reentrant: t -> int;
val cleanup_ffi = _import "curl_easy_cleanup" reentrant: t -> unit;
fun cleanup curl =
let
val easy_int = easy2int curl
in
H.delete(headerfunction_cb_H, easy_int);
H.delete(writefunction_cb_H, easy_int);
cleanup_ffi curl
end
val setopt_void = _import "curl_easy_setopt": t * int * t -> int;
val setopt_cb_ffi = _import "curl_easy_setopt" reentrant: t * int * t -> int;
val curlopt_headerfunction_cb_export = _export "curlopt_headerfunction_cb": (t * int * int * t -> C_Size.t) -> unit;
val curlopt_headerfunction_cb = _address "curlopt_headerfunction_cb" public: t;
val curlopt_writefunction_cb_export = _export "curlopt_writefunction_cb": (t * int * int * t -> C_Size.t) -> unit;
val curlopt_writefunction_cb = _address "curlopt_writefunction_cb" public: t;
fun cb_low_h_f (ptr, size, nmemb, curl) =
let
val length = size * nmemb
val arr = Word8Array.tabulate(length, (fn(i) => getWord8(ptr, i)))
val s = Byte.bytesToString (Word8Array.vector arr)
val easy_int = easy2int curl
val cb = valOf(H.sub(headerfunction_cb_H, easy_int))
in
C_Size.fromInt (cb s)
end
fun cb_low_w_f (ptr, size, nmemb, curl) =
let
val length = size * nmemb
val arr = Word8Array.tabulate(length, (fn(i) => getWord8(ptr, i)))
val s = Byte.bytesToString (Word8Array.vector arr)
val easy_int = easy2int curl
val cb = valOf(H.sub(writefunction_cb_H, easy_int))
in
C_Size.fromInt (cb s)
end
val _ = curlopt_headerfunction_cb_export cb_low_h_f
val _ = curlopt_writefunction_cb_export cb_low_w_f
fun setopt_cb(curl, opt, cb) =
if opt = CURLOPT_HEADERFUNCTION
then
let
val easy_int = easy2int curl
val _ = H.update(headerfunction_cb_H, easy_int, cb)
val _ = setopt_void(curl, CURLOPT_HEADERDATA, curl)
in
setopt_cb_ffi(curl, opt, curlopt_headerfunction_cb)
end
else
if opt = CURLOPT_WRITEFUNCTION
then
let
val easy_int = easy2int curl
val _ = H.update(writefunction_cb_H, easy_int, cb)
val _ = setopt_void(curl, CURLOPT_WRITEDATA, curl)
in
setopt_cb_ffi(curl, opt, curlopt_writefunction_cb)
end
else 0
val setopt_list_ffi = _import "curl_easy_setopt": t * int * t -> int;
fun setopt_list(curl, opt, []) = (setopt_list_ffi(curl, opt, null); fn () => ())
| setopt_list(curl, opt, l) =
let
val l = List.map String.toCString l
val cnt = List.length l
val size = List.foldl (fn (s,size) => size + String.size s) 0 l
val mem = malloc(2 * (Word.toInt sizeofPointer) * cnt + size + cnt)
fun doit [] p = []
| doit (x::xs) p =
let
val sp = add(p, 0w2 * sizeofPointer)
val np = if List.null xs then null else add(p, 0w2 * sizeofPointer + Word.fromInt(1 + String.size x))
in
setPointer(p, 0, sp);
setPointer(p, 1, np);
Word8Vector.foldli (fn(i, c, r) => (setWord8(sp, i, c) ; r + 1 ) ) 0 (Byte.stringToBytes x);
setWord8(sp, (String.size x), 0w0);
doit xs np
end
in
doit l mem;
setopt_list_ffi(curl, opt, mem);
fn () => (setopt_list_ffi(curl, opt, null); free mem)
end
val getinfo_str_ffi = _import "curl_easy_getinfo": t * int * t ref -> int;
fun getinfo_str(curl, info) =
let
val pp = ref null
in
getinfo_str_ffi(curl, info, pp);
readCString (!pp)
end
val strerror_ffi = _import "curl_easy_strerror": int -> t;
val strerror = readCString o strerror_ffi
end
structure Multi =
struct
type multi = t
type easy = Easy.curl
val easy2int = C_Size.toLargeInt o C_Pointer.toWord
val init_ffi = _import "curl_multi_init": unit -> t;
fun init () =
let
val multi = init_ffi ()
in
if multi = null
then raise Curl CURLE_FAILED_INIT
else multi
end
val cleanup = _import "curl_multi_cleanup" reentrant: t -> int;
val setopt_timer_cb_ffi = _import "curl_multi_setopt" reentrant: t * int * t -> int;
val curlmopt_timerfunction_cb_export_32bit = _export "curlmopt_timerfunction_cb_32bit" : (t * Int32.int * t -> int) -> unit;
val curlmopt_timerfunction_cb_export_64bit = _export "curlmopt_timerfunction_cb_64bit" : (t * Int64.int * t -> int) -> unit;
val curlmopt_timerfunction_cb_32bit = _address "curlmopt_timerfunction_cb_32bit" public: t;
val curlmopt_timerfunction_cb_64bit = _address "curlmopt_timerfunction_cb_64bit" public: t;
fun setopt_timer_cb(multi, cb) = (
if is_64bit
then (
curlmopt_timerfunction_cb_export_64bit ( fn(multi, timeout_ms, _) => cb(multi, Int64.toInt timeout_ms) );
setopt_timer_cb_ffi(multi, CURLMOPT_TIMERFUNCTION, curlmopt_timerfunction_cb_64bit)
)
else (
curlmopt_timerfunction_cb_export_32bit ( fn(multi, timeout_ms, _) => cb(multi, Int32.toInt timeout_ms) );
setopt_timer_cb_ffi(multi, CURLMOPT_TIMERFUNCTION, curlmopt_timerfunction_cb_32bit)
)
)
val setopt_socket_cb_ffi = _import "curl_multi_setopt" reentrant: t * int * t -> int;
val curlmopt_socketfunction_cb_export = _export "curlmopt_socketfunction_cb": (t * int * int * t * t -> int) -> unit;
val curlmopt_socketfunction_cb = _address "curlmopt_socketfunction_cb" public: t;
fun setopt_socket_cb(multi, cb) = (
curlmopt_socketfunction_cb_export ( fn(easy, socket, poll, _, _) => cb(easy, socket, poll) );
setopt_socket_cb_ffi(multi, CURLMOPT_SOCKETFUNCTION, curlmopt_socketfunction_cb)
)
val add_handle = _import "curl_multi_add_handle" reentrant: t * t -> int;
val remove_handle = _import "curl_multi_remove_handle" reentrant: t * t -> int;
val socket_action_ffi = _import "curl_multi_socket_action" reentrant: t * int * int * t -> int;
val running_handles_mem = malloc(4)
fun socket_action(multi, socket, ev_bitmask) =
let
val _ = setInt32(running_handles_mem, 0, 0)
val _ = socket_action_ffi(multi, socket, ev_bitmask, running_handles_mem)
in
getInt32(running_handles_mem, 0)
end
val info_read_ffi = _import "curl_multi_info_read": t * t -> t;
val msgs_in_queue_mem = malloc(4)
fun read_msg p =
let
val msg = getInt32(p, 0) val p = add(p, sizeofPointer) (* not 0w4 because alignment *)
val easy = getPointer(p, 0) val p = add(p, sizeofPointer)
val result = getInt32(p, 0)
in
(msg, easy, result)
end
fun info_read(multi) =
let
val _ = setInt32(msgs_in_queue_mem, 0, 0)
val msg_pointer = info_read_ffi(multi, msgs_in_queue_mem)
in
if msg_pointer = null
then NONE
else SOME (read_msg msg_pointer)
end
end
end