Skip to content

Commit a2bc091

Browse files
committed
arbitrary request headers for ncurl
1 parent f55b8bd commit a2bc091

File tree

9 files changed

+103
-77
lines changed

9 files changed

+103
-77
lines changed

NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#### New Features
44

55
* New `stream()` interface exposes low-level byte stream functionality in the NNG library, intended for communicating with non-NNG endpoints, including but not limited to websocket servers.
6-
* `ncurl()` adds an 'async' option to perform HTTP requests asynchronously, returning immediately with a 'recvAio'. Also adds explicit arguments for HTTP method, content type and authorization headers and request data.
6+
* `ncurl()` adds an 'async' option to perform HTTP requests asynchronously, returning immediately with a 'recvAio'. Also adds explicit arguments for HTTP method, headers (which takes a named list or character vector) and request data.
77
* New `messenger()` function implements a console-based 2-way messaging system using NNG's scalability protocols [currently experimental and undergoing early-stage testing].
88

99
#### Updates

R/ncurl.R

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
#'
77
#' @param url the URL address.
88
#' @param async [default FALSE] logical value whether to perform actions async.
9-
#' @param method (optional) the HTTP method.
10-
#' @param ctype (optional) the 'Content-type' header.
11-
#' @param auth (optional) the 'Authorization' header.
9+
#' @param method (optional) the HTTP method (defaults to 'GET' if not specified).
10+
#' @param headers (optional) a named list or character vector specifying the
11+
#' HTTP request headers e.g. \code{list(`Content-Type` = "text/plain")} or
12+
#' \code{c(Authorization = "Bearer APIKEY")}.
1213
#' @param data (optional) the request data to be submitted.
1314
#'
1415
#' @return Named list of 2 elements:
@@ -39,18 +40,18 @@
3940
#'
4041
#' @examples
4142
#' ncurl("http://httpbin.org/get")
42-
#' ncurl("http://httpbin.org/put", ,"PUT", "text/plain", "Bearer APIKEY", "hello world")
43-
#' ncurl("http://httpbin.org/post", ,"POST", "application/json", ,'{"key": "value"}')
43+
#' ncurl("http://httpbin.org/put", ,"PUT", list(Authorization = "Bearer APIKEY"), "hello world")
44+
#' ncurl("http://httpbin.org/post", ,"POST", c(`Content-Type` = "application/json"),'{"k":"v"}')
4445
#'
4546
#' @export
4647
#'
47-
ncurl <- function(url, async = FALSE, method = NULL, ctype = NULL, auth = NULL, data = NULL) {
48+
ncurl <- function(url, async = FALSE, method = NULL, headers = NULL, data = NULL) {
4849

4950
data <- if (!missing(data)) writeBin(object = data, con = raw())
5051

5152
if (missing(async) || !isTRUE(async)) {
5253

53-
res <- .Call(rnng_ncurl, url, method, ctype, auth, data)
54+
res <- .Call(rnng_ncurl, url, method, headers, data)
5455
missing(res) && return(invisible())
5556
if (is.integer(res)) {
5657
logerror(res)
@@ -65,7 +66,7 @@ ncurl <- function(url, async = FALSE, method = NULL, ctype = NULL, auth = NULL,
6566

6667
} else {
6768

68-
aio <- .Call(rnng_ncurl_aio, url, method, ctype, auth, data)
69+
aio <- .Call(rnng_ncurl_aio, url, method, headers, data)
6970
is.integer(aio) && {
7071
logerror(aio)
7172
return(invisible(`class<-`(aio, "errorValue")))

README.Rmd

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,9 @@ For advanced use, supports additional HTTP methods such as POST or PUT.
411411

412412
```{r ncurladv}
413413
414-
res <- ncurl("http://httpbin.org/post", async = TRUE,
415-
"POST", "application/json", "Bearer APIKEY", '{"key": "value"}')
414+
res <- ncurl("http://httpbin.org/post", async = TRUE, method = "POST",
415+
headers = c(`Content-Type` = "application/json", Authorization = "Bearer APIKEY"),
416+
data = '{"key": "value"}')
416417
res
417418
418419
call_aio(res)$data

README.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ aio
370370
#> < recvAio >
371371
#> - $data for message data
372372
aio$data |> str()
373-
#> num [1:100000000] 1.3356 -0.0658 0.6899 -0.0952 -0.1385 ...
373+
#> num [1:100000000] -1.898 0.604 0.988 0.395 -0.633 ...
374374
```
375375

376376
As `call_aio()` is blocking and will wait for completion, an alternative
@@ -407,7 +407,7 @@ sub |> recv(mode = "character", keep.raw = FALSE)
407407

408408
pub |> send(c("other", "this other topic will not be received"), mode = "raw", echo = FALSE)
409409
sub |> recv(mode = "character", keep.raw = FALSE)
410-
#> 2022-03-31 16:21:28 [ 8 ] Try again
410+
#> 2022-04-02 19:46:07 [ 8 ] Try again
411411

412412
# specify NULL to subscribe to ALL topics
413413
sub |> subscribe(topic = NULL)
@@ -418,7 +418,7 @@ sub |> recv("character", keep.raw = FALSE)
418418
sub |> unsubscribe(topic = NULL)
419419
pub |> send(c("newTopic", "this topic will now not be received"), mode = "raw", echo = FALSE)
420420
sub |> recv("character", keep.raw = FALSE)
421-
#> 2022-03-31 16:21:28 [ 8 ] Try again
421+
#> 2022-04-02 19:46:07 [ 8 ] Try again
422422

423423
# however the topics explicitly subscribed to are still received
424424
pub |> send(c("examples", "this example will still be received"), mode = "raw", echo = FALSE)
@@ -474,7 +474,7 @@ aio2$data
474474
# after the survey expires, the second resolves into a timeout error
475475
Sys.sleep(0.5)
476476
aio2$data
477-
#> 2022-03-31 16:21:29 [ 5 ] Timed out
477+
#> 2022-04-02 19:46:08 [ 5 ] Timed out
478478
#> 'errorValue' int 5
479479

480480
close(sur)
@@ -504,25 +504,26 @@ ncurl("http://httpbin.org/headers")
504504
#> [1] 7b 0a 20 20 22 68 65 61 64 65 72 73 22 3a 20 7b 0a 20 20 20 20 22 48 6f 73
505505
#> [26] 74 22 3a 20 22 68 74 74 70 62 69 6e 2e 6f 72 67 22 2c 20 0a 20 20 20 20 22
506506
#> [51] 58 2d 41 6d 7a 6e 2d 54 72 61 63 65 2d 49 64 22 3a 20 22 52 6f 6f 74 3d 31
507-
#> [76] 2d 36 32 34 35 63 36 66 39 2d 35 62 64 37 34 31 65 61 35 35 38 36 61 36 63
508-
#> [101] 66 30 65 34 64 36 61 61 61 22 0a 20 20 7d 0a 7d 0a
507+
#> [76] 2d 36 32 34 38 39 39 66 30 2d 35 61 32 31 32 64 64 61 36 33 65 66 38 31 66
508+
#> [101] 31 30 64 39 39 66 62 63 30 22 0a 20 20 7d 0a 7d 0a
509509
#>
510510
#> $data
511-
#> [1] "{\n \"headers\": {\n \"Host\": \"httpbin.org\", \n \"X-Amzn-Trace-Id\": \"Root=1-6245c6f9-5bd741ea5586a6cf0e4d6aaa\"\n }\n}\n"
511+
#> [1] "{\n \"headers\": {\n \"Host\": \"httpbin.org\", \n \"X-Amzn-Trace-Id\": \"Root=1-624899f0-5a212dda63ef81f10d99fbc0\"\n }\n}\n"
512512
```
513513

514514
For advanced use, supports additional HTTP methods such as POST or PUT.
515515

516516
``` r
517-
res <- ncurl("http://httpbin.org/post", async = TRUE,
518-
"POST", "application/json", "Bearer APIKEY", '{"key": "value"}')
517+
res <- ncurl("http://httpbin.org/post", async = TRUE, method = "POST",
518+
headers = c(`Content-Type` = "application/json", Authorization = "Bearer APIKEY"),
519+
data = '{"key": "value"}')
519520
res
520521
#> < recvAio >
521522
#> - $data for message data
522523
#> - $raw for raw message
523524

524525
call_aio(res)$data
525-
#> [1] "{\n \"args\": {}, \n \"data\": \"{\\\"key\\\": \\\"value\\\"}\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Authorization\": \"Bearer APIKEY\", \n \"Content-Length\": \"16\", \n \"Content-Type\": \"application/json\", \n \"Host\": \"httpbin.org\", \n \"X-Amzn-Trace-Id\": \"Root=1-6245c6f9-7f1995a270c8f351194cfa3a\"\n }, \n \"json\": {\n \"key\": \"value\"\n }, \n \"origin\": \"79.173.189.204\", \n \"url\": \"http://httpbin.org/post\"\n}\n"
526+
#> [1] "{\n \"args\": {}, \n \"data\": \"{\\\"key\\\": \\\"value\\\"}\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Authorization\": \"Bearer APIKEY\", \n \"Content-Length\": \"16\", \n \"Content-Type\": \"application/json\", \n \"Host\": \"httpbin.org\", \n \"X-Amzn-Trace-Id\": \"Root=1-624899f0-1c6f78c46ce661ac017ee613\"\n }, \n \"json\": {\n \"key\": \"value\"\n }, \n \"origin\": \"78.145.225.121\", \n \"url\": \"http://httpbin.org/post\"\n}\n"
526527
```
527528

528529
In this respect, it may be used as a performant and lightweight method

man/ncurl.Rd

Lines changed: 7 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/aio.c

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ SEXP rnng_stream_close(SEXP stream) {
893893

894894
/* ncurl aio ---------------------------------------------------------------- */
895895

896-
SEXP rnng_ncurl_aio(SEXP http, SEXP method, SEXP ctype, SEXP auth, SEXP data) {
896+
SEXP rnng_ncurl_aio(SEXP http, SEXP method, SEXP headers, SEXP data) {
897897

898898
nng_url *url;
899899
nng_http_client *client;
@@ -930,25 +930,40 @@ SEXP rnng_ncurl_aio(SEXP http, SEXP method, SEXP ctype, SEXP auth, SEXP data) {
930930
return Rf_ScalarInteger(xc);
931931
}
932932
}
933-
if (ctype != R_NilValue) {
934-
const char *cty = CHAR(STRING_ELT(ctype, 0));
935-
xc = nng_http_req_set_header(req, "Content-Type", cty);
936-
if (xc) {
937-
nng_http_req_free(req);
938-
nng_http_client_free(client);
939-
nng_url_free(url);
940-
return Rf_ScalarInteger(xc);
941-
}
942-
}
943-
if (auth != R_NilValue) {
944-
const char *aut = CHAR(STRING_ELT(auth, 0));
945-
xc = nng_http_req_set_header(req, "Authorization", aut);
946-
if (xc) {
947-
nng_http_req_free(req);
948-
nng_http_client_free(client);
949-
nng_url_free(url);
950-
return Rf_ScalarInteger(xc);
933+
if (headers != R_NilValue) {
934+
R_xlen_t hlen = Rf_xlength(headers);
935+
SEXP names = PROTECT(Rf_getAttrib(headers, R_NamesSymbol));
936+
switch (TYPEOF(headers)) {
937+
case STRSXP:
938+
for (R_xlen_t i = 0; i < hlen; i++) {
939+
const char *head = CHAR(STRING_ELT(headers, i));
940+
const char *name = CHAR(STRING_ELT(names, i));
941+
xc = nng_http_req_set_header(req, name, head);
942+
if (xc) {
943+
nng_http_req_free(req);
944+
nng_http_client_free(client);
945+
nng_url_free(url);
946+
UNPROTECT(1);
947+
return Rf_ScalarInteger(xc);
948+
}
949+
}
950+
break;
951+
case VECSXP:
952+
for (R_xlen_t i = 0; i < hlen; i++) {
953+
const char *head = CHAR(STRING_ELT(VECTOR_ELT(headers, i), 0));
954+
const char *name = CHAR(STRING_ELT(names, i));
955+
xc = nng_http_req_set_header(req, name, head);
956+
if (xc) {
957+
nng_http_req_free(req);
958+
nng_http_client_free(client);
959+
nng_url_free(url);
960+
UNPROTECT(1);
961+
return Rf_ScalarInteger(xc);
962+
}
963+
}
964+
break;
951965
}
966+
UNPROTECT(1);
952967
}
953968
if (data != R_NilValue) {
954969
unsigned char *dp = RAW(data);

src/init.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ static const R_CallMethodDef CallEntries[] = {
7373
{"rnng_listener_set_uint64", (DL_FUNC) &rnng_listener_set_uint64, 3},
7474
{"rnng_listener_start", (DL_FUNC) &rnng_listener_start, 1},
7575
{"rnng_messenger", (DL_FUNC) &rnng_messenger, 1},
76-
{"rnng_ncurl", (DL_FUNC) &rnng_ncurl, 5},
77-
{"rnng_ncurl_aio", (DL_FUNC) &rnng_ncurl_aio, 5},
76+
{"rnng_ncurl", (DL_FUNC) &rnng_ncurl, 4},
77+
{"rnng_ncurl_aio", (DL_FUNC) &rnng_ncurl_aio, 4},
7878
{"rnng_protocol_open", (DL_FUNC) &rnng_protocol_open, 1},
7979
{"rnng_recv", (DL_FUNC) &rnng_recv, 2},
8080
{"rnng_recv_aio", (DL_FUNC) &rnng_recv_aio, 2},

src/nanonext.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ extern SEXP rnng_listener_set_string(SEXP, SEXP, SEXP);
6464
extern SEXP rnng_listener_set_uint64(SEXP, SEXP, SEXP);
6565
extern SEXP rnng_listener_start(SEXP);
6666
extern SEXP rnng_messenger(SEXP);
67-
extern SEXP rnng_ncurl(SEXP, SEXP, SEXP, SEXP, SEXP);
68-
extern SEXP rnng_ncurl_aio(SEXP, SEXP, SEXP, SEXP, SEXP);
67+
extern SEXP rnng_ncurl(SEXP, SEXP, SEXP, SEXP);
68+
extern SEXP rnng_ncurl_aio(SEXP, SEXP, SEXP, SEXP);
6969
extern SEXP rnng_protocol_open(SEXP);
7070
extern SEXP rnng_recv(SEXP, SEXP);
7171
extern SEXP rnng_recv_aio(SEXP, SEXP);

src/utils.c

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ SEXP rnng_scm(void) {
3939

4040
/* ncurl - minimalist http client ------------------------------------------- */
4141

42-
SEXP rnng_ncurl(SEXP http, SEXP method, SEXP ctype, SEXP auth, SEXP data) {
42+
SEXP rnng_ncurl(SEXP http, SEXP method, SEXP headers, SEXP data) {
4343

4444
nng_url *url;
4545
nng_http_client *client;
@@ -78,25 +78,40 @@ SEXP rnng_ncurl(SEXP http, SEXP method, SEXP ctype, SEXP auth, SEXP data) {
7878
return Rf_ScalarInteger(xc);
7979
}
8080
}
81-
if (ctype != R_NilValue) {
82-
const char *cty = CHAR(STRING_ELT(ctype, 0));
83-
xc = nng_http_req_set_header(req, "Content-Type", cty);
84-
if (xc) {
85-
nng_http_req_free(req);
86-
nng_http_client_free(client);
87-
nng_url_free(url);
88-
return Rf_ScalarInteger(xc);
89-
}
90-
}
91-
if (auth != R_NilValue) {
92-
const char *aut = CHAR(STRING_ELT(auth, 0));
93-
xc = nng_http_req_set_header(req, "Authorization", aut);
94-
if (xc) {
95-
nng_http_req_free(req);
96-
nng_http_client_free(client);
97-
nng_url_free(url);
98-
return Rf_ScalarInteger(xc);
81+
if (headers != R_NilValue) {
82+
R_xlen_t hlen = Rf_xlength(headers);
83+
SEXP names = PROTECT(Rf_getAttrib(headers, R_NamesSymbol));
84+
switch (TYPEOF(headers)) {
85+
case STRSXP:
86+
for (R_xlen_t i = 0; i < hlen; i++) {
87+
const char *head = CHAR(STRING_ELT(headers, i));
88+
const char *name = CHAR(STRING_ELT(names, i));
89+
xc = nng_http_req_set_header(req, name, head);
90+
if (xc) {
91+
nng_http_req_free(req);
92+
nng_http_client_free(client);
93+
nng_url_free(url);
94+
UNPROTECT(1);
95+
return Rf_ScalarInteger(xc);
96+
}
97+
}
98+
break;
99+
case VECSXP:
100+
for (R_xlen_t i = 0; i < hlen; i++) {
101+
const char *head = CHAR(STRING_ELT(VECTOR_ELT(headers, i), 0));
102+
const char *name = CHAR(STRING_ELT(names, i));
103+
xc = nng_http_req_set_header(req, name, head);
104+
if (xc) {
105+
nng_http_req_free(req);
106+
nng_http_client_free(client);
107+
nng_url_free(url);
108+
UNPROTECT(1);
109+
return Rf_ScalarInteger(xc);
110+
}
111+
}
112+
break;
99113
}
114+
UNPROTECT(1);
100115
}
101116
if (data != R_NilValue) {
102117
unsigned char *dp = RAW(data);

0 commit comments

Comments
 (0)