Skip to content

Commit

Permalink
updated readme, libcurl small changes
Browse files Browse the repository at this point in the history
  • Loading branch information
guzba committed Jan 31, 2024
1 parent 5c57e11 commit 383986e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 38 deletions.
58 changes: 39 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -56,8 +76,6 @@ Response* = ref object
body*: string
```

Usage examples:

```nim
import puppy
Expand All @@ -82,7 +100,7 @@ echo response.headers
echo response.body.len
```

## Examples
## More examples

Using multipart/form-data:

Expand All @@ -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.
38 changes: 19 additions & 19 deletions src/puppy/platforms/linux/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 383986e

Please sign in to comment.