Skip to content

Commit a0661e2

Browse files
committed
feat: Add idiomatic use of headers (#11)
Initialize and release HTTP headers adding three different ways to to add headers. Closes #10
1 parent af3d3b2 commit a0661e2

6 files changed

+203
-33
lines changed

documentation/source/introduction.rst

+101
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,104 @@ wrapper, you access the information directly using property syntax.
202202
format-err("curl easy perform failed: %s\n",
203203
err.curl-error-message)
204204
end block;
205+
206+
Headers
207+
-------
208+
209+
Libcurl offers manipulation of HTTP headers using `CURLOPT_HTTPHEADER`
210+
option.
211+
212+
Libcurl's `CURLOPT_HTTPHEADER` option allows you to send custom HTTP
213+
headers with your requests. The option accepts a linked list of
214+
header strings, which you build using `curl_slist` and `curl_slist_append`.
215+
216+
.. code-block:: C
217+
:caption: Libcurl example using HTTP headers
218+
219+
CURL *curl;
220+
CURLcode res;
221+
222+
curl_global_init(CURL_GLOBAL_DEFAULT);
223+
curl = curl_easy_init();
224+
225+
struct curl_slist *headers = NULL;
226+
headers = curl_slist_append(headers, "Content-Type: application/json");
227+
headers = curl_slist_append(headers, "Authorization: Bearer your_token_here");
228+
headers = curl_slist_append(headers, "X-Custom-Header: Custom Value");
229+
230+
if (curl) {
231+
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/data");
232+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
233+
234+
// ... other curl options ...
235+
236+
res = curl_easy_perform(curl);
237+
238+
if (res != CURLE_OK) {
239+
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
240+
}
241+
242+
curl_slist_free_all(headers);
243+
curl_easy_cleanup(curl);
244+
}
245+
curl_global_cleanup();
246+
247+
The above example could be translated to Opendylan in three ways:
248+
249+
.. code-block:: dylan
250+
:caption: Using `add-header!`
251+
252+
with-curl-global($curl-global-default)
253+
with-curl-easy(curl = make(<curl-easy>),
254+
url = "https://api.example.com/data")
255+
256+
add-header!(curl,
257+
"Content-Type: application/json",
258+
"Authorization: Bearer your_token_here",
259+
"X-Custom-Header: Custom Value");
260+
261+
// ... other curl options ...
262+
263+
curl-easy-perform(curl);
264+
265+
end with-curl-easy;
266+
end with-curl-global;
267+
268+
Or passing the options to :macro:`with-curl-easy`:
269+
270+
.. code-block:: dylan
271+
:caption: Using `with-curl-easy` options
272+
273+
with-curl-global($curl-global-default)
274+
with-curl-easy(curl = make(<curl-easy>),
275+
url = "https://api.example.com/data",
276+
header = "Content-Type: application/json",
277+
header = "Authorization: Bearer your_token_here",
278+
header = "X-Custom-Header: Custom Value")
279+
280+
// ... other curl options ...
281+
282+
curl-easy-perform(curl);
283+
284+
end with-curl-easy;
285+
end with-curl-global;
286+
287+
Or using the setter method :meth:`curl-header-setter`
288+
289+
.. code-block:: dylan
290+
:caption: Using `curl-header-setter`
291+
292+
with-curl-global($curl-global-default)
293+
with-curl-easy(curl = make(<curl-easy>),
294+
url = "https://api.example.com/data")
295+
296+
curl.curl-header := "Content-Type: application/json";
297+
curl.curl-header := "Authorization: Bearer your_token_here";
298+
curl.curl-header := "X-Custom-Header: Custom Value";
299+
300+
// ... other curl options ...
301+
302+
curl-easy-perform(curl);
303+
304+
end with-curl-easy;
305+
end with-curl-global;

documentation/source/reference.rst

+61-14
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Macros
166166
end;
167167
168168
:example:
169-
169+
170170
.. code-block:: dylan
171171
:caption: Passing options to macro
172172
@@ -189,13 +189,13 @@ Macros
189189

190190
.. code-block:: dylan
191191
192-
let curl = #f;
192+
let curl = #f;
193193
block ()
194-
curl := make(<curl-easy>);
195-
... body ...
196-
cleanup
197-
curl-easy-cleanup(curl)
198-
end block;
194+
curl := make(<curl-easy>);
195+
body ...
196+
cleanup
197+
curl-easy-cleanup(curl)
198+
end block;
199199
200200
.. dylan:macro:: with-curl-global
201201
:statement:
@@ -207,19 +207,19 @@ Macros
207207

208208
:macrocall:
209209
.. parsed-literal::
210-
with-curl-global (*flags*) body end
210+
with-curl-global (*flags*) body end
211211
212212
:discussion:
213213

214214
This code is equivalent to:
215215

216216
.. code-block:: dylan
217217
218-
block()
219-
curl-library-setup(flags)
220-
body;
221-
rescue
222-
curl-library-cleanup()
218+
block()
219+
curl-library-setup(flags)
220+
body;
221+
rescue
222+
curl-library-cleanup()
223223
end;
224224
225225
:example:
@@ -230,7 +230,7 @@ Macros
230230
with-curl-easy (curl)
231231
curl.curl-url := "https://example.com";
232232
curl-easy-perform(curl);
233-
// do staff
233+
// retrieve information
234234
end;
235235
end with-curl-global;
236236
@@ -241,6 +241,53 @@ Macros
241241
* :func:`curl-library-setup`
242242
* :func:`curl-library-cleanup`
243243

244+
Headers
245+
=======
246+
247+
.. function:: add-header!
248+
249+
:signature:
250+
251+
add-header! (*curl* #rest *headers*) => (*curl*)
252+
253+
:parameter curl:
254+
An instance of :class:`<curl-easy>`
255+
:parameter #rest headers:
256+
0 or more :drm:`<string>` headers
257+
:value curl:
258+
An instance of :class:`<curl-easy>`
259+
260+
:example:
261+
262+
.. code-block:: dylan
263+
264+
add-header!("Content-type: application/json",
265+
"Authorization: Bearer you_token_here");
266+
267+
.. method:: curl-header-setter
268+
:specializer: <string>
269+
270+
:signature:
271+
272+
curl-header-setter (header curl) => (header)
273+
274+
:parameter curl: An instance of :class:`<curl-easy>`
275+
:parameter header: An instance of :drm:`<string>`
276+
:value header: An instance of :drm:`<string>`
277+
278+
:description:
279+
280+
Sets a HTTP header. Each use adds a header to the headers
281+
list.
282+
283+
If the key is repeated the values are appended, not replaced.
284+
285+
.. code-block:: dylan
286+
287+
curl.curl-header := "X-friend: you";
288+
curl.curl-header := "X-friend: her";
289+
// "X-friend: you, her"
290+
244291
Options
245292
=======
246293

src/curl-easy-module.dylan

+6-2
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ define module curl-easy
259259
curl-header-anchor,
260260
curl-header-anchor-setter;
261261

262+
// header related methods
263+
264+
create
265+
add-header!,
266+
curl-header-setter;
267+
262268
// Curl slist
263269

264270
create
@@ -325,8 +331,6 @@ define module curl-easy
325331
// curl-easy-recv,
326332
curl-easy-reset,
327333
curl-easy-unescape,
328-
curl-slist-append,
329-
curl-slist-free-all,
330334
curl-version;
331335

332336
// callbacks

src/curl-easy.dylan

+23
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ define abstract class <curl> (<object>) end;
607607
define open class <curl-easy> (<curl>)
608608
constant slot curl-handle :: <curl-easy-handle> = c-curl-easy-init(),
609609
init-keyword: handle:;
610+
slot curl-headers :: <curlopt-slistpoint> = null-pointer(<curlopt-slistpoint>),
611+
init-keyword: headers:;
610612
end;
611613

612614
define method initialize
@@ -619,6 +621,7 @@ end method;
619621

620622
define function curl-easy-cleanup
621623
(curl :: <curl-easy>) => ()
624+
curl-slist-free-all(curl.curl-headers);
622625
c-curl-easy-cleanup(curl.curl-handle)
623626
end;
624627

@@ -628,6 +631,20 @@ define function curl-easy-dup
628631
handle: c-curl-easy-duphandle(curl.curl-handle))
629632
end;
630633

634+
define function add-header!
635+
(curl :: <curl-easy>, #rest headers) => (curl :: <curl-easy>)
636+
for (header in headers)
637+
curl.curl-headers := curl-slist-append(curl.curl-headers, header)
638+
end;
639+
curl
640+
end;
641+
642+
define method curl-header-setter
643+
(header :: <string>, curl :: <curl-easy>) => (header :: <string>)
644+
add-header!(curl, header);
645+
header
646+
end;
647+
631648
define function curl-easy-escape
632649
(curl :: <curl-easy>, url :: <string>, #key length :: false-or(<integer>) = #f)
633650
=> (escaped-url :: <string>)
@@ -641,6 +658,11 @@ end;
641658

642659
define function curl-easy-perform
643660
(curl :: <curl-easy>) => ()
661+
// set headers
662+
unless (null-pointer?(curl.curl-headers))
663+
curl.curl-httpheader := curl.curl-headers
664+
end;
665+
// perform request
644666
let curl-code = c-curl-easy-perform(curl.curl-handle);
645667
unless (curl-code = $curle-ok)
646668
signal(make(<curl-perform-error>, code: curl-code))
@@ -649,6 +671,7 @@ end;
649671

650672
define function curl-easy-reset
651673
(curl :: <curl-easy>) => ()
674+
curl.curl-headers := null-pointer(<curlopt-slistpoint>);
652675
c-curl-easy-reset(curl.curl-handle)
653676
end;
654677

tests/test-httpbin.dylan

+6-12
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,13 @@ define test test-auth-basic (tags: #("io", "httpbin"))
4747
end test;
4848

4949
define test test-auth-bearer (tags: #("io", "httpbin"))
50-
let headers = null-pointer(<curlopt-slistpoint>);
5150
let bearer = "hunter42";
52-
block ()
53-
headers := curl-slist-append(headers, concatenate("Authorization: Bearer ", bearer));
54-
with-curl-easy (curl = make(<curl-easy>),
55-
url = httpbin("/bearer"),
56-
httpheader = headers)
57-
curl-easy-perform(curl);
58-
assert-equal(200, curl.curl-response-code);
59-
end with-curl-easy;
60-
cleanup
61-
curl-slist-free-all(headers);
62-
end block;
51+
with-curl-easy (curl = make(<curl-easy>),
52+
url = httpbin("/bearer"))
53+
curl.curl-header := concatenate("Authorization: Bearer ", bearer);
54+
curl-easy-perform(curl);
55+
assert-equal(200, curl.curl-response-code);
56+
end with-curl-easy;
6357
end test test-auth-bearer;
6458

6559
define test test-auth-digest (tags: #("io", "httpbin"))

tests/test-option-headers.dylan

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ License: See License.txt in this distribution for details.
55

66
define test test-option-headers (tags: #("io", "httpbin"))
77
with-curl-global($curl-global-default)
8-
let headers = null-pointer(<curlopt-slistpoint>);
9-
headers := curl-slist-append(headers, "X-Custom-Header: Chucho");
10-
headers := curl-slist-append(headers, "Another-Header: Good");
118
with-curl-easy (curl = make(<curl-easy>),
129
url = httpbin("/headers"),
13-
httpheader = headers)
10+
header = "Content-Type: application/json")
11+
curl.curl-header := "Authorization: Bearer your_token_here";
12+
curl.curl-header := "X-friend: Foo";
13+
add-header!(curl,
14+
"X-Custom-Header: Chucho",
15+
"Another-Header: Good");
1416
curl-easy-perform(curl);
15-
curl-slist-free-all(headers);
1617
assert-true(#t);
1718
end with-curl-easy;
1819
end with-curl-global;

0 commit comments

Comments
 (0)