Skip to content

Commit 9e58ce6

Browse files
authored
Merge pull request #90 from treeform/guzba
use webby
2 parents b1388b9 + 756c065 commit 9e58ce6

File tree

8 files changed

+111
-70
lines changed

8 files changed

+111
-70
lines changed

README.md

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,37 +46,40 @@ Need a more complex API?
4646
* headers: User-Agent, Content-Type..
4747
* response code: 200, 404, 500..
4848
* response headers: Content-Type..
49-
* error: timeout, DNS fail ...
5049

51-
Use request/response instead.
50+
Use these instead.
5251

5352
```nim
54-
Request* = ref object
55-
url*: Url
56-
headers*: seq[Header]
57-
timeout*: float32
58-
verb*: string
59-
body*: string
60-
6153
Response* = ref object
62-
url*: Url
63-
headers*: seq[Header]
54+
headers*: HttpHeaders
6455
code*: int
6556
body*: string
6657
```
6758

68-
Usage example:
59+
Usage examples:
60+
61+
```nim
62+
import puppy
63+
64+
let response = get("http://www.istrolid.com", @[("Auth", "1")])
65+
echo response.code
66+
echo response.headers
67+
echo response.body.len
68+
```
6969

7070
```nim
71-
let req = Request(
72-
url: parseUrl("http://www.istrolid.com"),
73-
verb: "get",
74-
headers: @[Header(key: "Auth", value: "1")]
71+
import puppy
72+
73+
let body = "{\"json\":true}"
74+
75+
let response = post(
76+
"http://api.website.com",
77+
@[("Content-Type", "application/json")],
78+
body
7579
)
76-
let res = fetch(req)
77-
echo res.code
78-
echo res.headers
79-
echo res.body.len
80+
echo response.code
81+
echo response.headers
82+
echo response.body.len
8083
```
8184

8285
## Always use Libcurl

puppy.nimble

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
version = "1.6.2"
1+
version = "2.0.0"
22
author = "Andre von Houck"
33
description = "Puppy fetches resources via HTTP and HTTPS."
44
license = "MIT"
55

66
srcDir = "src"
77

88
requires "nim >= 1.2.2"
9-
requires "urlly >= 1.1.0"
109
requires "libcurl >= 1.0.0"
1110
requires "zippy >= 0.10.0"
11+
requires "webby >= 0.1.3"

src/puppy.nim

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import puppy/common, urlly
1+
import puppy/common
22

33
when defined(windows) and not defined(puppyLibcurl):
44
# WinHTTP Windows
@@ -10,7 +10,7 @@ else:
1010
# LIBCURL Linux
1111
import puppy/platforms/linux/platform
1212

13-
export common, urlly
13+
export common
1414

1515
proc addDefaultHeaders(req: Request) =
1616
if req.headers["user-agent"] == "":
@@ -35,7 +35,7 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
3535
proc newRequest*(
3636
url: string,
3737
verb = "get",
38-
headers = newSeq[Header](),
38+
headers = emptyHttpHeaders(),
3939
timeout: float32 = 60
4040
): Request =
4141
## Allocates a new request object with defaults.
@@ -45,10 +45,56 @@ proc newRequest*(
4545
result.headers = headers
4646
result.timeout = timeout
4747

48-
proc fetch*(url: string, headers = newSeq[Header]()): string =
49-
let
50-
req = newRequest(url, "get", headers)
51-
res = req.fetch()
48+
proc get*(
49+
url: string,
50+
headers = emptyHttpHeaders(),
51+
timeout: float32 = 60
52+
): Response =
53+
fetch(newRequest(url, "GET", headers, timeout))
54+
55+
proc post*(
56+
url: string,
57+
headers = emptyHttpHeaders(),
58+
body: sink string = "",
59+
timeout: float32 = 60
60+
): Response =
61+
let request = newRequest(url, "POST", headers, timeout)
62+
request.body = move body
63+
fetch(request)
64+
65+
proc put*(
66+
url: string,
67+
headers = emptyHttpHeaders(),
68+
body: sink string = "",
69+
timeout: float32 = 60
70+
): Response =
71+
let request = newRequest(url, "PUT", headers, timeout)
72+
request.body = move body
73+
fetch(request)
74+
75+
proc patch*(
76+
url: string,
77+
headers = emptyHttpHeaders(),
78+
body: sink string = "",
79+
timeout: float32 = 60
80+
): Response =
81+
let request = newRequest(url, "PATCH", headers, timeout)
82+
request.body = move body
83+
fetch(request)
84+
85+
proc delete*(
86+
url: string,
87+
headers = emptyHttpHeaders(),
88+
timeout: float32 = 60
89+
): Response =
90+
fetch(newRequest(url, "DELETE", headers, timeout))
91+
92+
proc fetch*(url: string, headers = emptyHttpHeaders()): string =
93+
## Simple fetch that directly returns the GET response body.
94+
## Raises an exception if anything goes wrong or if the response code
95+
## is not 200. See get, post, put etc for similar calls that return
96+
## a response object.
97+
let res = get(url, headers)
5298
if res.code == 200:
5399
return res.body
54100
raise newException(PuppyError,

src/puppy/common.nim

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
import std/strutils, urlly
1+
import std/strutils, webby
22

3-
export urlly
3+
export webby
44

55
const CRLF* = "\r\n"
66

77
type
8-
Header* = object
9-
key*: string
10-
value*: string
11-
128
Request* = ref object
139
url*: Url
14-
headers*: seq[Header]
10+
headers*: HttpHeaders
1511
timeout*: float32
1612
verb*: string
1713
body*: string
@@ -20,27 +16,21 @@ type
2016
allowAnyHttpsCertificate*: bool
2117

2218
Response* = ref object
23-
headers*: seq[Header]
19+
headers*: HttpHeaders
2420
code*: int
2521
body*: string
2622

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

29-
proc `[]`*(headers: seq[Header], key: string): string =
30-
## Get a key out of headers. Not case sensitive.
31-
## Use a for loop to get multiple keys.
32-
for header in headers:
33-
if cmpIgnorecase(header.key, key) == 0:
34-
return header.value
35-
36-
proc `[]=`*(headers: var seq[Header], key, value: string) =
37-
## Sets a key in the headers. Not case sensitive.
38-
## If key is not there appends a new key-value pair at the end.
39-
for header in headers.mitems:
40-
if cmpIgnorecase(header.key, key) == 0:
41-
header.value = value
42-
return
43-
headers.add(Header(key: key, value: value))
25+
Header* = object
26+
key*: string
27+
value*: string
4428

4529
proc `$`*(req: Request): string =
4630
req.verb.toUpperAscii & " " & $req.url
31+
32+
converter toWebby*(headers: seq[Header]): HttpHeaders =
33+
cast[HttpHeaders](headers)
34+
35+
converter toWebby*(header: Header): (string, string) =
36+
(header.key, header.value)

src/puppy/platforms/linux/platform.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
2828
var strings: seq[string]
2929
strings.add $req.url
3030
strings.add req.verb.toUpperAscii()
31-
for header in req.headers:
32-
strings.add header.key & ": " & header.value
31+
for (k, v) in req.headers:
32+
strings.add k & ": " & v
3333
3434
let curl = easy_init()
3535
@@ -89,7 +89,7 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
8989
for headerLine in headerData.split(CRLF):
9090
let arr = headerLine.split(":", 1)
9191
if arr.len == 2:
92-
result.headers.add(Header(key: arr[0].strip(), value: arr[1].strip()))
92+
result.headers.add((arr[0].strip(), arr[1].strip()))
9393
result.body = bodyWrap.str
9494
if result.headers["Content-Encoding"] == "gzip":
9595
try:

src/puppy/platforms/macos/platform.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
1212

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

15-
for header in req.headers:
16-
request.setValue(@(header.value), @(header.key))
15+
for (k, v) in req.headers:
16+
request.setValue(@(v), @(k))
1717

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

4343
if data.length > 0:
4444
result.body.setLen(data.length)

src/puppy/platforms/win32/platform.nim

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
9090
)
9191
9292
var requestHeaderBuf: string
93-
for header in req.headers:
94-
requestHeaderBuf &= header.key & ": " & header.value & CRLF
93+
for (k, v) in req.headers:
94+
requestHeaderBuf &= k & ": " & v & CRLF
9595
9696
let wideRequestHeaderBuf = requestHeaderBuf.wstr()
9797
@@ -226,9 +226,9 @@ proc fetch*(req: Request): Response {.raises: [PuppyError].} =
226226
if line != "":
227227
let parts = line.split(":", 1)
228228
if parts.len == 2:
229-
result.headers.add(Header(
230-
key: parts[0].strip(),
231-
value: parts[1].strip()
229+
result.headers.add((
230+
parts[0].strip(),
231+
parts[1].strip()
232232
))
233233

234234
var i: int

tests/test.nim

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ block:
4949
doAssert res.headers.len > 0
5050
doAssert res.body != ""
5151

52+
block:
53+
discard fetch(Request(
54+
url: parseUrl("https://www.google.com"),
55+
verb: "get",
56+
headers: @[Header(key: "User-Agent", value: "Puppy")]
57+
)
58+
)
59+
5260
when defined(windows):
5361
block:
5462
let httpsServer = startProcess(
@@ -153,13 +161,7 @@ try:
153161

154162
block:
155163
# test empty post
156-
let req = Request(
157-
url: parseUrl("http://localhost:8080/post"),
158-
verb: "post",
159-
body: ""
160-
)
161-
let res = fetch(req)
162-
doAssert ($req).startsWith("POST http://localhost:8080/post")
164+
let res = post("http://localhost:8080/post")
163165
doAssert res.code == 200
164166
doAssert res.body == ""
165167

0 commit comments

Comments
 (0)