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
|
import
strutils,
"../utils"
type
CurlHandle* = object
CurlInstance* = object
handle: ptr CurlHandle
data: seq[char]
CurlOption {.pure, size: sizeof(cint).} = enum
followLocation = 52,
noSignal = 99,
timeoutMs = 155,
connectTimeoutMs = 156,
writeData = 10001,
url = 10002,
writeFunction = 20011
CurlError* = object of Exception
{.passL: "-lcurl".}
proc initCurlGlobal*(flags: clong): cint
{.cdecl, importc: "curl_global_init".}
proc cleanupCurlGlobal*: void
{.cdecl, importc: "curl_global_cleanup".}
proc newCurlHandle*: ptr CurlHandle
{.cdecl, importc: "curl_easy_init".}
proc cleanup*(handle: ptr CurlHandle)
{.cdecl, importc: "curl_easy_cleanup".}
proc errorCurl*(error: cint): cstring
{.cdecl, importc: "curl_easy_strerror".}
proc setOption*(handle: ptr CurlHandle, option: CurlOption): cint
{.cdecl, importc: "curl_easy_setopt", varargs.}
proc perform*(handle: ptr CurlHandle): cint
{.cdecl, importc: "curl_easy_perform".}
proc escape*(handle: ptr CurlHandle, input: cstring, length: cint): cstring
{.cdecl, importc: "curl_easy_escape".}
proc freeCurl*(data: pointer)
{.cdecl, importc: "curl_free".}
proc escape*(instance: ref CurlInstance, s: string): string =
let esc = instance.handle.escape(s, 0)
if esc != nil:
let nesc = $esc
freeCurl(esc)
nesc
else:
""
proc curlWriteMemory(mem: array[csize.high, char], size: csize, nmemb: csize,
userdata: ref CurlInstance): csize {.cdecl.} =
let total = size * nmemb
if total > 0:
userData.data &= mem[0 .. total - 1]
total
var refCount = 0
template withCurlGlobal*(body: untyped): untyped =
block:
if refCount == 0:
if initCurlGlobal(0) != 0:
raise commandError(tr"failed to initialize curl library")
refCount += 1
try:
body
finally:
refCount -= 1
if refCount == 0:
cleanupCurlGlobal()
template withCurl*(instance: untyped, body: untyped): untyped =
block:
let handle = newCurlHandle()
if handle == nil:
raise commandError(tr"failed to initialize curl handle")
var instance: ref CurlInstance
new(instance)
instance.handle = handle
instance.data = newSeq[char]()
proc raiseError(code: cint) =
if code != 0:
let msg = code.errorCurl
if msg != nil:
raise newException(CurlError, tr"failed to perform request" & (": $#" % [$msg]))
else:
raise newException(CurlError, tr"failed to perform request")
proc performInternal(url: string, useTimeout: bool): seq[char] =
let timeoutMs = if useTimeout: 15000 else: 0
raiseError(handle.setOption(CurlOption.followLocation, (clong) 1))
raiseError(handle.setOption(CurlOption.noSignal, (clong) 1))
raiseError(handle.setOption(CurlOption.timeoutMs, (clong) timeoutMs))
raiseError(handle.setOption(CurlOption.connectTimeoutMs, (clong) timeoutMs))
raiseError(handle.setOption(CurlOption.url, url))
raiseError(handle.setOption(CurlOption.writeFunction, cast[pointer](curlWriteMemory)))
raiseError(handle.setOption(CurlOption.writeData, instance))
raiseError(handle.perform())
instance.data
proc performString(url: string, useTimeout: bool): string =
let data = performInternal(url, useTimeout)
var str = newStringOfCap(data.len)
for c in data:
str.add(c)
str
try:
body
finally:
handle.cleanup()
|