From 383986e60df346bb03c2f02dd5f7bc81f5d20a79 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Wed, 31 Jan 2024 00:27:07 -0600 Subject: [PATCH] updated readme, libcurl small changes --- README.md | 58 +++++++++++++++++--------- src/puppy/platforms/linux/platform.nim | 38 ++++++++--------- 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 30df6da..7efd3b4 100644 --- a/README.md +++ b/README.md @@ -4,50 +4,70 @@ `nimble install puppy` -![Github Actions](https://github.com/treeform/puppy/workflows/Github%20Actions/badge.svg) +![Github Actions](https://github.com/treeform/puppy/actions/workflows/build.yml/badge.svg) [API reference](https://treeform.github.io/puppy) ## About -Puppy does not use Nim's HTTP stack, instead it uses `WinHttp` API on Windows , `AppKit` on macOS, and `libcurl` on Linux. Because Puppy uses system APIs, there is no need to ship extra `*.dll`s, `cacert.pem`, or forget to pass the `-d:ssl` flag. This also has the effect of producing slightly smaller binaires. +Puppy makes HTTP requests easy! -Furthermore, Puppy supports gzip transparently right out of the box. +With Puppy you can make HTTP requests without needing to pass the `-d:ssl` flag or shipping extra `*.dll`s and `cacerts.pem` on Windows. Puppy avoids these gotchas by using system APIs instead of Nim's HTTP stack. + +Some other highlights of Puppy are: + +* Supports gzip'ed responses out of the box +* Make an HTTP request using a one-line `proc` call OS | Method ----- | --------------------------- Win32 | WinHttp WinHttpRequest macOS | AppKit NSMutableURLRequest -linux | libcurl easy_perform +Linux | libcurl easy_perform *Curently does not support async* -```nim -import puppy +## Easy mode +```nim echo fetch("http://neverssl.com/") ``` -Will raise `PuppyError` if the response status code is not `200`. +A call to `fetch` will raise PuppyError if the response status code is not 200. + +## More request types + +Make a basic GET request: + +```nim +import puppy + +let response = get("https://www.google.com/") +``` Need to pass headers? ```nim import puppy -echo fetch( +let response = get( "http://neverssl.com/", headers = @[("User-Agent", "Nim 1.0")] ) ``` -Need a more complex API? -* verbs: GET, POST, PUT, UPDATE, DELETE.. -* headers: User-Agent, Content-Type.. -* response code: 200, 404, 500.. -* response headers: Content-Type.. +Easy one-line procs for your favorite verbs: + +```nim +discard get(url, headers) +discard post(url, headers, body) +discard put(url, headers, body) +discard patch(url, headers, body) +discard delete(url, headers) +discard head(url, headers) +``` -Use these instead. +## Working with responses ```nim Response* = ref object @@ -56,8 +76,6 @@ Response* = ref object body*: string ``` -Usage examples: - ```nim import puppy @@ -82,7 +100,7 @@ echo response.headers echo response.body.len ``` -## Examples +## More examples Using multipart/form-data: @@ -109,6 +127,8 @@ let response = post("Your API endpoint here", headers, body) See the [examples/](https://github.com/treeform/puppy) folder for more examples. -## Always use Libcurl +## Always use libcurl + +You can pass `-d:puppyLibcurl` to force use of `libcurl` even on Windows and macOS. This is useful if for some reason the native OS API is not working. -You can pass `-d:puppyLibcurl` to force use of `libcurl` even on windows and macOS. This is useful to debug, if the some reason native OS API does not work. Libcurl is usually installed on macOS but requires a `curl.dll` on windows. +Libcurl is typically ready-to-use on macOS and Linux. On Windows you'll need to grab the latest libcurl DLL from https://curl.se/windows/, rename it to libcurl.dll, and put it in the same directory as your executable. diff --git a/src/puppy/platforms/linux/platform.nim b/src/puppy/platforms/linux/platform.nim index 793998a..8c98cc5 100644 --- a/src/puppy/platforms/linux/platform.nim +++ b/src/puppy/platforms/linux/platform.nim @@ -15,26 +15,26 @@ type StringWrap = object ## some sort of wrapper to be passed to C. str: string +{.push stackTrace: off.} + +proc curlWriteFn( + buffer: cstring, + size: int, + count: int, + outstream: pointer +): int {.cdecl.} = + let + outbuf = cast[ptr StringWrap](outstream) + i = outbuf.str.len + outbuf.str.setLen(outbuf.str.len + count) + copyMem(outbuf.str[i].addr, buffer, count) + result = size * count + +{.pop.} + proc internalFetch*(req: Request): Response {.raises: [PuppyError].} = result = Response() - {.push stackTrace: off.} - - proc curlWriteFn( - buffer: cstring, - size: int, - count: int, - outstream: pointer - ): int {.cdecl.} = - let - outbuf = cast[ptr StringWrap](outstream) - i = outbuf.str.len - outbuf.str.setLen(outbuf.str.len + count) - copyMem(outbuf.str[i].addr, buffer, count) - result = size * count - - {.pop.} - var strings: seq[string] strings.add $req.url strings.add req.verb.toUpperAscii() @@ -65,9 +65,9 @@ proc internalFetch*(req: Request): Response {.raises: [PuppyError].} = discard curl.easy_setopt(OPT_HTTPHEADER, headerList) - if req.verb.toUpperAscii() == "HEAD": + if cmpIgnoreCase(req.verb, "HEAD") == 0: discard curl.easy_setopt(OPT_NOBODY, 1) - elif req.verb.toUpperAscii() == "POST" or req.body.len > 0: + elif cmpIgnoreCase(req.verb, "POST") == 0 or req.body.len > 0: discard curl.easy_setopt(OPT_POSTFIELDSIZE, req.body.len) discard curl.easy_setopt(OPT_POSTFIELDS, req.body.cstring)