diff options
Diffstat (limited to 'src/wrapper/curl.nim')
-rw-r--r-- | src/wrapper/curl.nim | 123 |
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() |