Files
cground/libs/request/request.c

152 lines
5.4 KiB
C
Raw Normal View History

2026-01-03 10:20:13 +11:00
#include <groundext.h>
#include <groundvm.h>
2026-03-18 05:53:04 +11:00
#include <curl/curl.h>
2026-01-03 10:20:13 +11:00
#include <stdio.h>
2026-03-18 05:53:04 +11:00
#include <stdbool.h>
CURL* curlHandle = NULL;
GroundStruct template;
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
const char* VALID_REQUEST_TYPES[] = {
"GET",
"POST"
};
2026-01-03 10:20:13 +11:00
typedef struct {
char* data;
size_t size;
} ResponseBuffer;
size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
size_t total_size = size * nmemb;
ResponseBuffer* buffer = (ResponseBuffer*)userdata;
char* new_data = realloc(buffer->data, buffer->size + total_size + 1);
if (new_data == NULL) {
printf("Failed to allocate memory\n");
return 0;
}
buffer->data = new_data;
memcpy(buffer->data + buffer->size, ptr, total_size);
buffer->size += total_size;
buffer->data[buffer->size] = '\0';
return total_size;
}
2026-03-18 05:53:04 +11:00
GroundValue ground_request(GroundScope* scope, List args) {
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
GroundObject obj = *args.values[0].data.customVal;
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
// check arg types
GroundObjectField* address = groundFindField(obj, "address");
if (address == NULL || address->value.type != STRING) {
ERROR("Object does not have address field as string", "ValueError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
GroundObjectField* type = groundFindField(obj, "type");
if (type == NULL || type->value.type != STRING) {
ERROR("Object does not have type field as string", "ValueError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
GroundObjectField* userAgent = groundFindField(obj, "userAgent");
if (userAgent == NULL || userAgent->value.type != STRING) {
ERROR("Object does not have userAgent field as string", "ValueError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
GroundObjectField* httpHeaders = groundFindField(obj, "httpHeaders");
if (httpHeaders == NULL || httpHeaders->value.type != LIST) {
ERROR("Object does not have httpHeaders field as list<string>", "ValueError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
GroundObjectField* verbose = groundFindField(obj, "verbose");
if (verbose == NULL || verbose->value.type != BOOL) {
ERROR("Object does not have verbose field as bool", "ValueError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
// check request type string
bool requestTypeOk = false;
for (int i = 0; i < sizeof(VALID_REQUEST_TYPES)/sizeof(VALID_REQUEST_TYPES[0]); i++) {
if (strcmp(type->value.data.stringVal, VALID_REQUEST_TYPES[i]) == 0) {
requestTypeOk = true;
break;
2026-01-03 10:20:13 +11:00
}
}
2026-03-18 05:53:04 +11:00
if (!requestTypeOk)
ERROR("Invalid request type! Choices: GET, POST", "ValueError");
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
ResponseBuffer buffer = {0};
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
// set curl params
curl_easy_setopt(curlHandle, CURLOPT_URL, address->value.data.stringVal);
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void*)&buffer);
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, userAgent->value.data.stringVal);
curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, verbose->value.data.boolVal);
2026-01-03 10:20:13 +11:00
2026-03-18 05:53:04 +11:00
// append headers to http request
struct curl_slist* httpHeaderList = NULL;
GroundValue* httpHeaderStrings = httpHeaders->value.data.listVal.values;
for (int i = 0; i < httpHeaders->value.data.listVal.size; i++) {
httpHeaderList = curl_slist_append(httpHeaderList, httpHeaderStrings[i].data.stringVal);
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, httpHeaderList);
CURLcode curlStatus = curl_easy_perform(curlHandle);
curl_slist_free_all(httpHeaderList); // dont want em' memory leaks :P - spooopy
// check for any curl errors
if (curlStatus != CURLE_OK) {
char curlErrorMsg[255] = {0};
snprintf(curlErrorMsg, 255, "%s\n", curl_easy_strerror(curlStatus));
ERROR(curlErrorMsg, "CurlError");
2026-01-03 10:20:13 +11:00
}
2026-03-18 05:53:04 +11:00
// get the http response code
long httpCode = 0;
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
// create return value and return it
GroundValue value = groundCreateValue(CUSTOM, &template);
GroundObjectField* status = groundFindField(*value.data.customVal, "statusCode");
status->value.data.intVal = httpCode;
GroundObjectField* data = groundFindField(*value.data.customVal, "data");
data->value.data.stringVal = buffer.data;
value.type = CUSTOM;
return value;
2026-01-03 10:20:13 +11:00
}
void ground_init(GroundScope* scope) {
2026-03-18 05:53:04 +11:00
// init libcurl
curl_global_init(CURL_GLOBAL_ALL);
curlHandle = curl_easy_init();
groundAddValueToScope(scope, "requestInitSuccess", groundCreateValue(BOOL, curlHandle != NULL));
if (curlHandle == NULL) {
return;
}
// create struct
template = groundCreateStruct();
groundAddFieldToStruct(&template, "statusCode", groundCreateValue(INT, 200));
groundAddFieldToStruct(&template, "data", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&template, "ok", groundCreateValue(BOOL, 1));
// create struct that users can pass to this library
GroundStruct requestStruct = groundCreateStruct();
groundAddFieldToStruct(&requestStruct, "address", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&requestStruct, "type", groundCreateValue(STRING, "GET"));
groundAddFieldToStruct(&requestStruct, "userAgent", groundCreateValue(STRING, "Ground/1.0"));
groundAddFieldToStruct(&requestStruct, "httpHeaders", groundCreateValue(LIST, 0));
groundAddFieldToStruct(&requestStruct, "verbose", groundCreateValue(BOOL, 0));
groundAddValueToScope(scope, "request", groundCreateValue(STRUCTVAL, requestStruct));
// create functions
groundAddNativeFunction(scope, "request_Request", ground_request, CUSTOM, 1, CUSTOM, "requestInfo");
}