/* * Copyright (c) 2023 Fancy Code. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "http_servers.h" #include "ILog.h" #include "curl_serve.h" #include #include #include #include #include #include static size_t write_cb(char *data, size_t n, size_t l, void *userp) { /* take care of the data here, ignored in this example */ (void)data; (void)userp; ServerHttp *p = (ServerHttp *)userp; const int LENGTH = n * l; // p->reply = (char *)realloc(p->reply, LENGTH); char *newData = (char *)malloc(p->replyLength + LENGTH); if (newData) { if (p->reply) { memcpy(newData, p->reply, p->replyLength); free(p->reply); p->reply = NULL; } memcpy(newData + p->replyLength, data, LENGTH); p->replyLength += LENGTH; p->reply = newData; } return n * l; } void HttpServersGet(ServerHttp *param) { if (!param) { LogError("null pointer.\n"); return; } CURL *curl; CURLcode res; curl = CurlEasyMake(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, param->url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); param->code = res; /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* always cleanup */ curl_easy_cleanup(curl); } } void HttpServersPost(ServerHttp *param) { if (!param) { LogError("null pointer.\n"); return; } CURL *curl; CURLcode res; /* In windows, this will init the winsock stuff */ curl_global_init(CURL_GLOBAL_ALL); /* get a curl handle */ curl = CurlEasyMake(); if (curl) { /* First set the URL that is about to receive our POST. This URL can just as well be an https:// URL if that is what should receive the data. */ curl_easy_setopt(curl, CURLOPT_URL, param->url); struct curl_slist *headers = NULL; /* default type with postfields is application/x-www-form-urlencoded, change it if you want */ headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* Now specify the POST data */ if (param->postData) { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, param->postData); } else { const char *NULL_POST_DATA = "{}"; curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL_POST_DATA); } curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000L); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* always cleanup */ curl_easy_cleanup(curl); /* free headers */ curl_slist_free_all(headers); } curl_global_cleanup(); } void HttpServersPostFile(ServerHttp *param) { if (!param) { LogError("null pointer.\n"); return; } CURL *curl = NULL; CURLcode res; struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; struct curl_slist *headerlist = NULL; static const char buf[] = "Expect:"; curl_global_init(CURL_GLOBAL_ALL); /* Fill in the file upload field */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "sendfile", CURLFORM_FILE, param->filePath, CURLFORM_END); /* Fill in the filename field */ curl_formadd( &formpost, &lastptr, CURLFORM_COPYNAME, "filename", CURLFORM_COPYCONTENTS, param->filePath, CURLFORM_END); /* Fill in the submit field too, even if this is rarely needed */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "submit", CURLFORM_COPYCONTENTS, "send", CURLFORM_END); /* get a curl handle */ curl = CurlEasyMake(); /* initialize custom header list (stating that Expect: 100-continue is not wanted */ headerlist = curl_slist_append(headerlist, buf); if (curl) { /* what URL that receives this POST */ curl_easy_setopt(curl, CURLOPT_URL, param->url); // if((argc == 2) && (!strcmp(argv[1], "noexpectheader"))) /* only disable 100-continue header if explicitly requested */ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* always cleanup */ curl_easy_cleanup(curl); /* then cleanup the formpost chain */ curl_formfree(formpost); /* free slist */ curl_slist_free_all(headerlist); } } static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) { size_t retcode; unsigned long nread; /* in real-world cases, this would probably get this data differently as this fread() stuff is exactly what the library already would do by default internally */ retcode = fread(ptr, size, nmemb, stream); if (retcode > 0) { nread = (unsigned long)retcode; fprintf(stderr, "*** We read %lu bytes from file\n", nread); } return retcode; } void HttpServersPut(ServerHttp *param) { if (!param) { LogError("null pointer.\n"); return; } CURL *curl; CURLcode res; FILE *hd_src; struct stat file_info; struct curl_slist *headers = NULL; char *file; if (!param->filePath) { LogError("file path is needed.\n"); return; } file = param->filePath; /* get the file size of the local file */ stat(file, &file_info); /* get a FILE * of the same file, could also be made with fdopen() from the previous descriptor, but hey this is just an example! */ hd_src = fopen(file, "rb"); if (!hd_src) { LogError("fopen files failed.\n"); return; } /* In windows, this will init the winsock stuff */ curl_global_init(CURL_GLOBAL_ALL); /* get a curl handle */ curl = CurlEasyMake(); if (curl) { /* we want to use our own read function */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); /* enable uploading (implies PUT over HTTP) */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* specify target URL, and note that this URL should include a file name, not only a directory */ curl_easy_setopt(curl, CURLOPT_URL, param->url); /* now specify which file to upload */ curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); /* provide the size of the upload, we typecast the value to curl_off_t since we must be sure to use the correct data size */ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); // Add header char **cpp = NULL; for (cpp = param->header; *cpp; cpp++) { headers = curl_slist_append(headers, *cpp); } if (headers) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); } /* Now run off and do what you have been told! */ res = curl_easy_perform(curl); /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* always cleanup */ curl_easy_cleanup(curl); /* free headers */ if (headers) { curl_slist_free_all(headers); } } fclose(hd_src); /* close the local file */ curl_global_cleanup(); }