Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use webby #90

Merged
merged 11 commits into from
Dec 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,37 +46,40 @@ Need a more complex API?
* headers: User-Agent, Content-Type..
* response code: 200, 404, 500..
* response headers: Content-Type..
* error: timeout, DNS fail ...

Use request/response instead.
Use these instead.

```nim
Request* = ref object
url*: Url
headers*: seq[Header]
timeout*: float32
verb*: string
body*: string

Response* = ref object
url*: Url
headers*: seq[Header]
headers*: HttpHeaders
code*: int
body*: string
```

Usage example:
Usage examples:

```nim
import puppy

let response = get("http://www.istrolid.com", @[("Auth", "1")])
echo response.code
echo response.headers
echo response.body.len
```

```nim
let req = Request(
url: parseUrl("http://www.istrolid.com"),
verb: "get",
headers: @[Header(key: "Auth", value: "1")]
import puppy

let body = "{\"json\":true}"

let response = post(
"http://api.website.com",
@[("Content-Type", "application/json")],
body
)
let res = fetch(req)
echo res.code
echo res.headers
echo res.body.len
echo response.code
echo response.headers
echo response.body.len
```

## Always use Libcurl
Expand Down
4 changes: 2 additions & 2 deletions puppy.nimble
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
version = "1.6.2"
version = "2.0.0"
author = "Andre von Houck"
description = "Puppy fetches resources via HTTP and HTTPS."
license = "MIT"

srcDir = "src"

requires "nim >= 1.2.2"
requires "urlly >= 1.1.0"
requires "libcurl >= 1.0.0"
requires "zippy >= 0.10.0"
requires "webby >= 0.1.3"
60 changes: 53 additions & 7 deletions src/puppy.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import puppy/common, urlly
import puppy/common

when defined(windows) and not defined(puppyLibcurl):
# WinHTTP Windows
Expand All @@ -10,7 +10,7 @@ else:
# LIBCURL Linux
import puppy/platforms/linux/platform

export common, urlly
export common

proc addDefaultHeaders(req: Request) =
if req.headers["user-agent"] == "":
Expand All @@ -35,7 +35,7 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
proc newRequest*(
url: string,
verb = "get",
headers = newSeq[Header](),
headers = emptyHttpHeaders(),
timeout: float32 = 60
): Request =
## Allocates a new request object with defaults.
Expand All @@ -45,10 +45,56 @@ proc newRequest*(
result.headers = headers
result.timeout = timeout

proc fetch*(url: string, headers = newSeq[Header]()): string =
let
req = newRequest(url, "get", headers)
res = req.fetch()
proc get*(
url: string,
headers = emptyHttpHeaders(),
timeout: float32 = 60
): Response =
fetch(newRequest(url, "GET", headers, timeout))

proc post*(
url: string,
headers = emptyHttpHeaders(),
body: sink string = "",
timeout: float32 = 60
): Response =
let request = newRequest(url, "POST", headers, timeout)
request.body = move body
fetch(request)

proc put*(
url: string,
headers = emptyHttpHeaders(),
body: sink string = "",
timeout: float32 = 60
): Response =
let request = newRequest(url, "PUT", headers, timeout)
request.body = move body
fetch(request)

proc patch*(
url: string,
headers = emptyHttpHeaders(),
body: sink string = "",
timeout: float32 = 60
): Response =
let request = newRequest(url, "PATCH", headers, timeout)
request.body = move body
fetch(request)

proc delete*(
url: string,
headers = emptyHttpHeaders(),
timeout: float32 = 60
): Response =
fetch(newRequest(url, "DELETE", headers, timeout))

proc fetch*(url: string, headers = emptyHttpHeaders()): string =
## Simple fetch that directly returns the GET response body.
## Raises an exception if anything goes wrong or if the response code
## is not 200. See get, post, put etc for similar calls that return
## a response object.
let res = get(url, headers)
if res.code == 200:
return res.body
raise newException(PuppyError,
Expand Down
36 changes: 13 additions & 23 deletions src/puppy/common.nim
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import std/strutils, urlly
import std/strutils, webby

export urlly
export webby

const CRLF* = "\r\n"

type
Header* = object
key*: string
value*: string

Request* = ref object
url*: Url
headers*: seq[Header]
headers*: HttpHeaders
timeout*: float32
verb*: string
body*: string
Expand All @@ -20,27 +16,21 @@ type
allowAnyHttpsCertificate*: bool

Response* = ref object
headers*: seq[Header]
headers*: HttpHeaders
code*: int
body*: string

PuppyError* = object of IOError ## Raised if an operation fails.

proc `[]`*(headers: seq[Header], key: string): string =
## Get a key out of headers. Not case sensitive.
## Use a for loop to get multiple keys.
for header in headers:
if cmpIgnorecase(header.key, key) == 0:
return header.value

proc `[]=`*(headers: var seq[Header], key, value: string) =
## Sets a key in the headers. Not case sensitive.
## If key is not there appends a new key-value pair at the end.
for header in headers.mitems:
if cmpIgnorecase(header.key, key) == 0:
header.value = value
return
headers.add(Header(key: key, value: value))
Header* = object
key*: string
value*: string

proc `$`*(req: Request): string =
req.verb.toUpperAscii & " " & $req.url

converter toWebby*(headers: seq[Header]): HttpHeaders =
cast[HttpHeaders](headers)

converter toWebby*(header: Header): (string, string) =
(header.key, header.value)
6 changes: 3 additions & 3 deletions src/puppy/platforms/linux/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
var strings: seq[string]
strings.add $req.url
strings.add req.verb.toUpperAscii()
for header in req.headers:
strings.add header.key & ": " & header.value
for (k, v) in req.headers:
strings.add k & ": " & v

let curl = easy_init()

Expand Down Expand Up @@ -89,7 +89,7 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
for headerLine in headerData.split(CRLF):
let arr = headerLine.split(":", 1)
if arr.len == 2:
result.headers.add(Header(key: arr[0].strip(), value: arr[1].strip()))
result.headers.add((arr[0].strip(), arr[1].strip()))
result.body = bodyWrap.str
if result.headers["Content-Encoding"] == "gzip":
try:
Expand Down
6 changes: 3 additions & 3 deletions src/puppy/platforms/macos/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =

request.setHTTPMethod(@(req.verb.toUpperAscii()))

for header in req.headers:
request.setValue(@(header.value), @(header.key))
for (k, v) in req.headers:
request.setValue(@(v), @(k))

if req.body.len > 0:
request.setHTTPBody(NSData.dataWithBytes(req.body[0].addr, req.body.len))
Expand All @@ -38,7 +38,7 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
if key.int == 0:
break
let value = dictionary.objectForKey(key)
result.headers[$(key.NSString)] = $(value.NSString)
result.headers.add(($(key.NSString), $(value.NSString)))

if data.length > 0:
result.body.setLen(data.length)
Expand Down
10 changes: 5 additions & 5 deletions src/puppy/platforms/win32/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
)

var requestHeaderBuf: string
for header in req.headers:
requestHeaderBuf &= header.key & ": " & header.value & CRLF
for (k, v) in req.headers:
requestHeaderBuf &= k & ": " & v & CRLF

let wideRequestHeaderBuf = requestHeaderBuf.wstr()

Expand Down Expand Up @@ -226,9 +226,9 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
if line != "":
let parts = line.split(":", 1)
if parts.len == 2:
result.headers.add(Header(
key: parts[0].strip(),
value: parts[1].strip()
result.headers.add((
parts[0].strip(),
parts[1].strip()
))

var i: int
Expand Down
16 changes: 9 additions & 7 deletions tests/test.nim
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ block:
doAssert res.headers.len > 0
doAssert res.body != ""

block:
discard fetch(Request(
url: parseUrl("https://www.google.com"),
verb: "get",
headers: @[Header(key: "User-Agent", value: "Puppy")]
)
)

when defined(windows):
block:
let httpsServer = startProcess(
Expand Down Expand Up @@ -153,13 +161,7 @@ try:

block:
# test empty post
let req = Request(
url: parseUrl("http://localhost:8080/post"),
verb: "post",
body: ""
)
let res = fetch(req)
doAssert ($req).startsWith("POST http://localhost:8080/post")
let res = post("http://localhost:8080/post")
doAssert res.code == 200
doAssert res.body == ""

Expand Down