#include #include #include #include #include CURL* curlHandle = NULL; GroundStruct template; const char* VALID_REQUEST_TYPES[] = { "GET", "POST" }; 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; } GroundValue ground_request(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; // 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"); } GroundObjectField* type = groundFindField(obj, "type"); if (type == NULL || type->value.type != STRING) { ERROR("Object does not have type field as string", "ValueError"); } GroundObjectField* userAgent = groundFindField(obj, "userAgent"); if (userAgent == NULL || userAgent->value.type != STRING) { ERROR("Object does not have userAgent field as string", "ValueError"); } GroundObjectField* httpHeaders = groundFindField(obj, "httpHeaders"); if (httpHeaders == NULL || httpHeaders->value.type != LIST) { ERROR("Object does not have httpHeaders field as list", "ValueError"); } GroundObjectField* verbose = groundFindField(obj, "verbose"); if (verbose == NULL || verbose->value.type != BOOL) { ERROR("Object does not have verbose field as bool", "ValueError"); } // 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; } } if (!requestTypeOk) ERROR("Invalid request type! Choices: GET, POST", "ValueError"); ResponseBuffer buffer = {0}; // 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); // 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); } 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"); } // 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; } void ground_init(GroundScope* scope) { // 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"); }