-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathcurl.c
246 lines (222 loc) · 8.45 KB
/
curl.c
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <curl/curl.h>
#include "gracelib.h"
// curl.c defines a Grace module "curl" which can be compiled for
// dynamic or static linking. The module is a relatively thin wrapper
// of a subset of the libcurl API. The module contains one method:
// easy -> CurlEasy
// This method wraps curl_easy_init, returning a CurlEasy object
// representing a CURL* handle. CurlEasy objects support these methods:
// onReceive(blk : Block) -> Done
// Set the block to be executed when data is received in response
// to a request. The block is passed the received data as an Octets
// object. Wraps CURLOPT_WRITEFUNCTION.
// url:=(url : String) -> Done
// Set the URL to be requested. Wraps CURLOPT_URL.
// perform -> Done
// Make the request. Wraps curl_easy_perform.
// onHeader(blk : Block) -> Done
// Set the block to be executed when a header is received in response
// to a request. The block is passed the received header as an Octets
// object. Wraps CURLOPT_HEADERFUNCTION.
// escape(s : String | Octets) -> String
// URL-encodes s. Wraps curl_easy_escape.
// unescape(s : String | Octets) -> Octets
// URL-decodes s. Wraps curl_easy_unescape.
// responseCode -> Number
// Returns the result code of the most recent request performed.
// Wraps CURLINFO_RESPONSE_CODE.
// effectiveUrl -> String
// Returns the effective URL, after any redirects, of the most
// recent request performed. Wraps CURLINFO_EFFECTIVE_URL.
// includeResponseHeader:=(b : Boolean) -> Done
// Sets whether to include response headers in the returned data.
// Wraps CURLOPT_HEADER.
// followLocation:=(b : Boolean) -> Done
// Sets whether to follow any Location: headers that appear in.
// Wraps CURLOPT_FOLLOWLOCATION.
// A sample use might be:
// def req = curl.easy
// req.url := "http://example.com/"
// req.onReceive {d->
// io.output.write(d.decode("utf-8"))
// }
// req.perform
Object curl_module = NULL;
ClassData CurlEasy;
extern ClassData Octets;
struct CurlModuleObject {
OBJECT_HEADER;
};
#define MINIGRACE_CURLEASY_OBJECTS 3
struct CurlEasyObject {
OBJECT_HEADER;
CURL *handle;
Object objects[MINIGRACE_CURLEASY_OBJECTS];
};
Object CurlEasy_perform(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_perform(r->handle);
return self;
}
size_t CurlEasy__receive(char *ptr, size_t size, size_t nmemb,
void *blk) {
size_t bsz = size * nmemb;
char buf[bsz];
int tmp = 1;
Object arg = alloc_Octets(ptr, bsz);
callmethod(blk, "apply", 1, &tmp, &arg);
return bsz;
}
Object CurlEasy_onReceive(Object self, int nparts, int *argcv,
Object *argv, int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_setopt(r->handle, CURLOPT_WRITEFUNCTION, &CurlEasy__receive);
curl_easy_setopt(r->handle, CURLOPT_WRITEDATA, argv[0]);
r->objects[0] = argv[0];
return alloc_done();
}
size_t CurlEasy__receiveHeader(char *ptr, size_t size, size_t nmemb,
void *blk) {
size_t bsz = size * nmemb;
char buf[bsz];
int tmp = 1;
Object arg = alloc_Octets(ptr, bsz);
callmethod(blk, "apply", 1, &tmp, &arg);
return bsz;
}
Object CurlEasy_onHeader(Object self, int nparts, int *argcv,
Object *argv, int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_setopt(r->handle, CURLOPT_HEADERFUNCTION, &CurlEasy__receiveHeader);
curl_easy_setopt(r->handle, CURLOPT_HEADERDATA, argv[0]);
r->objects[2] = argv[0];
return alloc_done();
}
Object CurlEasy_url_assign(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_setopt(r->handle, CURLOPT_URL, grcstring(argv[0]));
r->objects[1] = argv[0];
return alloc_done();
}
Object CurlEasy_includeResponseHeader(Object self, int nparts, int *argcv,
Object *argv, int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_setopt(r->handle, CURLOPT_HEADER, istrue(argv[0]));
return alloc_done();
}
Object CurlEasy_followLocation(Object self, int nparts, int *argcv,
Object *argv, int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
curl_easy_setopt(r->handle, CURLOPT_FOLLOWLOCATION, istrue(argv[0]));
return alloc_done();
}
Object CurlEasy_responseCode(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
long l;
curl_easy_getinfo(r->handle, CURLINFO_RESPONSE_CODE, &l);
return alloc_Float64(l);
}
Object CurlEasy_effectiveUrl(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
char *c;
curl_easy_getinfo(r->handle, CURLINFO_EFFECTIVE_URL, &c);
return alloc_String(c);
}
Object CurlEasy_escape(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
char *inp;
int len = 0;
if (argv[0]->class == Octets) {
struct OctetsObject *oct = (struct OctetsObject *)argv[0];
inp = oct->body;
len = oct->blen;
} else {
inp = grcstring(argv[0]);
}
char *c = curl_easy_escape(r->handle, inp, len);
Object o = alloc_String(c);
curl_free(c);
return o;
}
Object CurlEasy_unescape(Object self, int nparts, int *argcv, Object *argv,
int flags) {
struct CurlEasyObject *r = (struct CurlEasyObject *)self;
char *inp;
int len = 0;
if (argv[0]->class == Octets) {
struct OctetsObject *oct = (struct OctetsObject *)argv[0];
inp = oct->body;
len = oct->blen;
} else {
inp = grcstring(argv[0]);
}
char *c = curl_easy_unescape(r->handle, inp, len, &len);
Object o = alloc_Octets(c, len);
curl_free(c);
return o;
}
void CurlEasy__mark(struct CurlEasyObject *r) {
for (int i=0; i<MINIGRACE_CURLEASY_OBJECTS; i++)
gc_mark(r->objects[i]);
}
void CurlEasy__release(struct CurlEasyObject *r) {
curl_easy_cleanup(r->handle);
}
Object alloc_CurlEasy() {
if (!CurlEasy) {
CurlEasy = alloc_class3("CurlEasy", 14, (void*)&CurlEasy__mark,
(void*)&CurlEasy__release);
add_Method(CurlEasy, "perform", &CurlEasy_perform);
add_Method(CurlEasy, "onReceive", &CurlEasy_onReceive);
add_Method(CurlEasy, "url:=", &CurlEasy_url_assign);
add_Method(CurlEasy, "responseCode", &CurlEasy_responseCode);
add_Method(CurlEasy, "effectiveUrl", &CurlEasy_effectiveUrl);
add_Method(CurlEasy, "onHeader", &CurlEasy_onHeader);
add_Method(CurlEasy, "followLocation:=", &CurlEasy_followLocation);
add_Method(CurlEasy, "includeResponseHeader:=",
&CurlEasy_includeResponseHeader);
add_Method(CurlEasy, "escape", &CurlEasy_escape);
add_Method(CurlEasy, "unescape", &CurlEasy_unescape);
add_Method(CurlEasy, "==", &Object_Equals);
add_Method(CurlEasy, "!=", &Object_NotEquals);
add_Method(CurlEasy, "asString", &Object_asString);
add_Method(CurlEasy, "asDebugString", &Object_asString);
}
Object o = alloc_obj(sizeof(struct CurlEasyObject)
- sizeof(struct Object), CurlEasy);
struct CurlEasyObject *r = (struct CurlEasyObject *)o;
r->handle = curl_easy_init();
curl_easy_setopt(r->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP);
curl_easy_setopt(r->handle, CURLOPT_MAXREDIRS, 10);
curl_easy_setopt(r->handle, CURLOPT_USERAGENT, "Minigrace HTTP Library");
return o;
}
Object curl_easy(Object self, int nparams, int *argcv, Object *argv,
int flags) {
if (nparams != 1)
gracedie("curl.easy requires one argument");
return alloc_CurlEasy();
}
// Create and return a Grace object with all the above functions as methods.
Object module_curl_init() {
if (curl_module != NULL)
return curl_module;
ClassData c = alloc_class("Module<curl>", 12);
add_Method(c, "easy", &curl_easy);
Object o = alloc_newobj(sizeof(struct CurlModuleObject)
- sizeof(struct Object), c);
curl_module = o;
gc_root(curl_module);
struct CurlModuleObject *cmo = (struct CurlModuleObject *)o;
curl_global_init(CURL_GLOBAL_DEFAULT);
return o;
}