aboutsummaryrefslogtreecommitdiff
path: root/src/wrapper/curl.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/wrapper/curl.nim')
-rw-r--r--src/wrapper/curl.nim123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/wrapper/curl.nim b/src/wrapper/curl.nim
new file mode 100644
index 0000000..5f3e814
--- /dev/null
+++ b/src/wrapper/curl.nim
@@ -0,0 +1,123 @@
+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): seq[char] =
+ raiseError(handle.setOption(CurlOption.followLocation, (clong) 1))
+ raiseError(handle.setOption(CurlOption.noSignal, (clong) 1))
+ raiseError(handle.setOption(CurlOption.timeoutMs, (clong) 15000))
+ raiseError(handle.setOption(CurlOption.connectTimeoutMs, (clong) 15000))
+ 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): string =
+ let data = performInternal(url)
+ var str = newStringOfCap(data.len)
+ for c in data:
+ str.add(c)
+ str
+
+ try:
+ body
+ finally:
+ handle.cleanup()