1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
import options
import streams
import strutils
import tables
import bindings/curl
import io/request
import io/serialize
import types/url
import utils/twtstr
type
HeaderResult* = ref object
statusline: bool
headers: HeaderList
curl: CURL
ostream: Stream
request: Request
template setopt(curl: CURL, opt: CURLoption, arg: typed) =
discard curl_easy_setopt(curl, opt, arg)
template setopt(curl: CURL, opt: CURLoption, arg: string) =
discard curl_easy_setopt(curl, opt, cstring(arg))
template getinfo(curl: CURL, info: CURLINFO, arg: typed) =
discard curl_easy_getinfo(curl, info, arg)
proc curlWriteHeader(p: cstring, size: csize_t, nitems: csize_t, userdata: pointer): csize_t {.cdecl.} =
var line = newString(nitems)
for i in 0..<nitems:
line[i] = p[i]
let headers = cast[HeaderResult](userdata)
if not headers.statusline:
headers.statusline = true
headers.ostream.swrite(int(CURLE_OK))
var status: int
headers.curl.getinfo(CURLINFO_RESPONSE_CODE, addr status)
headers.ostream.swrite(status)
return nitems
let k = line.until(':')
if k.len == line.len:
# empty line (last, before body) or invalid (=> error)
headers.ostream.swrite(headers.headers.getOrDefault("Content-Type").until(';'))
var urlp: cstring
headers.curl.getinfo(CURLINFO_REDIRECT_URL, addr urlp)
if "Location" in headers.headers.table:
let location = headers.headers.table["Location"][0]
headers.ostream.swrite(parseUrl(location, some(headers.request.url)))
else:
headers.ostream.swrite(none(Url))
return nitems
let v = line.substr(k.len + 1).strip()
headers.headers.add(k, v)
return nitems
proc curlWriteBody(p: cstring, size: csize_t, nmemb: csize_t, userdata: pointer): csize_t {.cdecl.} =
var s = newString(nmemb)
for i in 0..<nmemb:
s[i] = p[i]
let stream = cast[Stream](userdata)
if nmemb > 0:
stream.swrite(s)
stream.flush()
return nmemb
proc loadHttp*(request: Request, ostream: Stream) =
let curl = curl_easy_init()
if curl == nil: return # fail
let surl = request.url.serialize()
curl.setopt(CURLOPT_URL, surl)
curl.setopt(CURLOPT_WRITEDATA, ostream)
curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
let headerres = HeaderResult(headers: newHeaderList(), curl: curl, ostream: ostream, request: request)
curl.setopt(CURLOPT_HEADERDATA, headerres)
curl.setopt(CURLOPT_HEADERFUNCTION, curlWriteHeader)
var mime: curl_mime = nil
case request.httpmethod
of HTTP_GET: curl.setopt(CURLOPT_HTTPGET, 1)
of HTTP_POST:
curl.setopt(CURLOPT_POST, 1)
if request.multipart.issome:
mime = curl_mime_init(curl)
if mime == nil: return # fail
for entry in request.multipart.get.content:
let part = curl_mime_addpart(mime)
if part == nil: return # fail
curl_mime_name(part, cstring(entry.name))
if entry.isFile:
if entry.isStream:
curl_mime_filedata(part, cstring(entry.filename))
else:
let fd = readFile(entry.filename)
curl_mime_data(part, cstring(fd), csize_t(fd.len))
# may be overridden by curl_mime_filedata, so set it here
curl_mime_filename(part, cstring(entry.filename))
else:
curl_mime_data(part, cstring(entry.content), csize_t(entry.content.len))
curl.setopt(CURLOPT_MIMEPOST, mime)
elif request.body.issome:
curl.setopt(CURLOPT_POSTFIELDS, cstring(request.body.get))
curl.setopt(CURLOPT_POSTFIELDSIZE, request.body.get.len)
else: discard #TODO
var slist: curl_slist = nil
for k, v in request.headers:
let header = k & ": " & v
slist = curl_slist_append(slist, cstring(header))
if slist != nil:
curl.setopt(CURLOPT_HTTPHEADER, slist)
let res = curl_easy_perform(curl)
if res == CURLE_OK:
ostream.swrite("")
ostream.flush()
else:
ostream.swrite(int(res))
ostream.flush()
curl_easy_cleanup(curl)
if mime != nil:
curl_mime_free(mime)
if slist != nil:
curl_slist_free_all(slist)
|