Skip to content

Commit af348f0

Browse files
committed
Adding library for making HTTP/HTTPS requests
1 parent f47f950 commit af348f0

File tree

8 files changed

+233
-10
lines changed

8 files changed

+233
-10
lines changed

src/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ endif
6868

6969
LIBS = m pthread dl
7070

71+
# Detect if libcurl is available
72+
CURL_CONFIG := $(shell which curl-config)
73+
ifdef CURL_CONFIG
74+
HAS_LIBCURL := 1
75+
endif
76+
77+
# Add the libcurl flag and sources if found
78+
ifeq ($(HAS_LIBCURL), 1)
79+
DEFINES += RIC_SCRIPT_REQUESTS
80+
LIBS += curl
81+
RIC_SOURCES += $(LIB_DIR)/librequests
82+
endif
83+
7184
CFLAGS = Wall \
7285
Werror \
7386
O2 \

src/ast.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ typedef struct libFunction {
586586
intptr_t p; \
587587
*sb = calloc(sz + 1, sizeof(stackval_t)); \
588588
assert(*sb != NULL); \
589-
p = ((intptr_t)*sb) % sizeof(stackval_t); \
589+
p = ((intptr_t) * sb) % sizeof(stackval_t); \
590590
if (p != 0) { \
591591
p = (sizeof(stackval_t) - (p % sizeof(stackval_t))); \
592592
} \
@@ -600,7 +600,7 @@ typedef struct libFunction {
600600
heapval_t hpbv; \
601601
*hb = calloc(hz + 2, sizeof(heapval_t)); \
602602
assert(*hb != NULL); \
603-
p = ((intptr_t)*hb) % sizeof(heapval_t); \
603+
p = ((intptr_t) * hb) % sizeof(heapval_t); \
604604
p = (sizeof(heapval_t) - (p % sizeof(heapval_t))); \
605605
hpbv.sv.type = INT32TYPE; \
606606
hpbv.sv.i = (int32_t)hz; \

src/lib.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ libFunction_t ric_library[] = {
119119
DECLARE_LIB_FUNCTION("xattrSet", 3, ric_set_xattr),
120120
DECLARE_LIB_FUNCTION("xattrFindKey", 1, ric_find_xattr),
121121
DECLARE_LIB_FUNCTION("xattrRm", 2, ric_remove_xattr),
122+
#endif
123+
#ifdef RIC_SCRIPT_REQUESTS
124+
DECLARE_LIB_FUNCTION("requestGet", 2, ric_requests_get),
125+
DECLARE_LIB_FUNCTION("requestPost", 3, ric_requests_post),
122126
#endif
123127
// libos
124128
DECLARE_LIB_FUNCTION("sleep", 1, ric_sleep),

src/lib.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include "libbigint.h"
2222
#include "libcrypto.h"
2323
#include "libprioqueue.h"
24+
#ifdef RIC_SCRIPT_REQUESTS
25+
#include "librequests.h"
26+
#endif
2427

2528
#define EXPORT_STR(s) XEXPORT_STR(s)
2629
#define XEXPORT_STR(s) #s

src/library/librequests.c

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#include "librequests.h"
2+
3+
#include <curl/curl.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <stdio.h>
7+
#include "hashtable.h"
8+
#include "ast.h"
9+
#include "eval.h"
10+
11+
typedef struct {
12+
char *data;
13+
size_t size;
14+
} ResponseBuffer;
15+
16+
static size_t curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
17+
size_t totalSize = size * nmemb;
18+
ResponseBuffer *mem = (ResponseBuffer *)userp;
19+
20+
char *ptr = realloc(mem->data, mem->size + totalSize + 1);
21+
if (ptr == NULL) return 0; // out of memory
22+
23+
mem->data = ptr;
24+
memcpy(&(mem->data[mem->size]), contents, totalSize);
25+
mem->size += totalSize;
26+
mem->data[mem->size] = 0;
27+
28+
return totalSize;
29+
}
30+
31+
static CURLcode perform_http_request(const char *url, const char *method, int headers_len,
32+
struct curl_slist *headers, const char *post_data,
33+
char **out_response) {
34+
CURL *curl = curl_easy_init();
35+
if (!curl) return CURLE_FAILED_INIT;
36+
37+
ResponseBuffer chunk = {.data = malloc(1012), .size = 0};
38+
if (!chunk.data) return CURLE_OUT_OF_MEMORY;
39+
40+
curl_easy_setopt(curl, CURLOPT_URL, url);
41+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
42+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
43+
if (headers_len > 0) {
44+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
45+
}
46+
47+
if (strcmp(method, "POST") == 0) {
48+
curl_easy_setopt(curl, CURLOPT_POST, 1L);
49+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data ? post_data : "");
50+
}
51+
52+
CURLcode res = curl_easy_perform(curl);
53+
curl_easy_cleanup(curl);
54+
55+
if (res != CURLE_OK) {
56+
free(chunk.data);
57+
return res;
58+
}
59+
60+
*out_response = chunk.data;
61+
return CURLE_OK;
62+
}
63+
64+
static struct curl_slist *build_headers_from_dict(dictionary_t *dict, int *len,
65+
EXPRESSION_PARAMS()) {
66+
struct curl_slist *header_list = NULL;
67+
keyValList_t *kv = dict->keyVals;
68+
int count = 0;
69+
70+
while (kv) {
71+
if (kv->key->type == EXPR_TYPE_TEXT && kv->val->type == EXPR_TYPE_TEXT) {
72+
char header_buf[1024];
73+
char *key = NULL;
74+
char *val = NULL;
75+
stackval_t sv;
76+
void *sp = PROVIDE_CONTEXT()->sp;
77+
size_t *sc = PROVIDE_CONTEXT()->sc;
78+
int *interactive = PROVIDE_CONTEXT()->interactive;
79+
80+
/* Evaluate the key expression */
81+
evaluate_expression(kv->key, EXPRESSION_ARGS());
82+
POP_VAL(&sv, sp, sc);
83+
84+
if (sv.type != TEXT) {
85+
fprintf(stderr, "error: expression for headers must be a string, was: %d\n", sv.type);
86+
if (!*interactive) {
87+
exit(1);
88+
}
89+
return NULL;
90+
}
91+
key = sv.t;
92+
/* Evaluate the value expression */
93+
evaluate_expression(kv->val, EXPRESSION_ARGS());
94+
POP_VAL(&sv, sp, sc);
95+
96+
if (sv.type != TEXT) {
97+
fprintf(stderr, "error: expression for headers must be a string, was: %d\n", sv.type);
98+
if (!*interactive) {
99+
exit(1);
100+
}
101+
return NULL;
102+
}
103+
val = sv.t;
104+
105+
snprintf(header_buf, sizeof(header_buf), "%s: %s", key, val);
106+
header_list = curl_slist_append(header_list, header_buf);
107+
count += 1;
108+
}
109+
kv = kv->next;
110+
}
111+
*len = count;
112+
return header_list;
113+
}
114+
115+
static int perform_request(const char *method, EXPRESSION_PARAMS()) {
116+
stackval_t stv_url, stv_headers, stv_body;
117+
char *url = NULL;
118+
dictionary_t *headers_dict = NULL;
119+
char *post_body = NULL;
120+
void *sp = PROVIDE_CONTEXT()->sp;
121+
size_t *sc = PROVIDE_CONTEXT()->sc;
122+
void *hp = PROVIDE_CONTEXT()->hp;
123+
heapval_t *hpv = NULL;
124+
int dummy;
125+
int headers_len = 0;
126+
127+
POP_VAL(&stv_url, sp, sc);
128+
POP_VAL(&stv_headers, sp, sc);
129+
130+
if (strcmp(method, "POST") == 0) {
131+
POP_VAL(&stv_body, sp, sc);
132+
if (stv_body.type != TEXT) {
133+
fprintf(stderr, "error: POST body must be a string.\n");
134+
return 1;
135+
}
136+
post_body = stv_body.t;
137+
}
138+
139+
if (stv_url.type != TEXT || stv_headers.type != DICTTYPE) {
140+
fprintf(stderr, "error: '%s' request expects a string (url) and a dictionary (headers).\n",
141+
method);
142+
return 1;
143+
}
144+
145+
url = stv_url.t;
146+
headers_dict = stv_headers.dict;
147+
148+
struct curl_slist *curl_headers =
149+
build_headers_from_dict(headers_dict, &headers_len, EXPRESSION_ARGS());
150+
151+
char *response_body = malloc(1024);
152+
CURLcode res =
153+
perform_http_request(url, method, headers_len, curl_headers, post_body, &response_body);
154+
curl_slist_free_all(curl_headers);
155+
156+
if (res != CURLE_OK) {
157+
fprintf(stderr, "error: %s HTTP %s request failed: %s\n", url, method,
158+
curl_easy_strerror(res));
159+
PUSH_INT(-1, sp, sc);
160+
return 0;
161+
}
162+
163+
stackval_t stv;
164+
stv.type = TEXT;
165+
stv.t = response_body;
166+
167+
ALLOC_HEAP(&stv, hp, &hpv, &dummy);
168+
PUSH_STRING(response_body, sp, sc);
169+
return 0;
170+
}
171+
172+
int ric_requests_get(LIBRARY_PARAMS()) { return perform_request("GET", EXPRESSION_ARGS()); }
173+
int ric_requests_post(LIBRARY_PARAMS()) { return perform_request("POST", EXPRESSION_ARGS()); }

src/library/librequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef LIBREQUESTS_H
2+
#define LIBREQUESTS_H
3+
4+
/*
5+
*
6+
* The idea of this libio translation unit is
7+
* to provide a library to ric-script that can
8+
* do basic HTTP(S) networking utilities
9+
* such as GET and POST requests
10+
*
11+
*/
12+
13+
#include "lib.h"
14+
#include "hashtable.h"
15+
#include "ast.h"
16+
17+
int ric_requests_get(LIBRARY_PARAMS());
18+
int ric_requests_post(LIBRARY_PARAMS());
19+
20+
#endif

src/library/meson.build

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,33 @@ library_source_list = [
1414
'libprioqueue.c'
1515
]
1616

17+
ric_c_args = [] # Initialize if not already defined
18+
1719
target_os = target_machine.system()
1820

1921
# Handling windows NT vs. unix POSIX differences
20-
if ( target_os == 'windows' )
22+
if target_os == 'windows'
2123
library_source_list += ['libos_nt.c', 'libnet_nt.c']
22-
ric_c_args += ['-DNO_XATTR']
24+
ric_c_args += ['-DNO_XATTR']
2325
else
2426
library_source_list += ['libos.c', 'libnet.c']
2527
endif
2628

27-
if ( target_os == 'darwin' )
29+
if target_os == 'darwin'
2830
library_source_list += ['libxattr_darwin.c']
29-
elif ( target_os == 'linux' )
31+
elif target_os == 'linux'
3032
library_source_list += ['libxattr_linux.c']
3133
else
3234
ric_c_args += ['-DNO_XATTR']
3335
endif
3436

37+
# Try to find libcurl
38+
libcurl_dep = dependency('libcurl', required : false)
39+
40+
if libcurl_dep.found()
41+
ric_c_args += ['-DRIC_SCRIPT_REQUESTS']
42+
library_source_list += ['librequests.c']
43+
deps += [libcurl_dep]
44+
endif
45+
3546
library_sources = files(library_source_list)

src/meson.build

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ external_links = []
1212
external_inc = []
1313
subdir('external')
1414

15+
deps = []
16+
links = []
17+
1518
# Fetching the library sources
1619
subdir('library')
1720

@@ -22,10 +25,6 @@ sources = ['main.c','hashtable.c','lib.c', 'garbage.c',
2225
'prioqueue.c', 'lex.yy.c', 'y.tab.c',
2326
library_sources]
2427

25-
deps = []
26-
27-
links = []
28-
2928
# Multithreading
3029
thread_dep = dependency('threads')
3130
deps += [thread_dep]

0 commit comments

Comments
 (0)