par_easycurl.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // EASYCURL :: https://github.com/prideout/par
  2. // Wrapper around libcurl for performing simple synchronous HTTP requests.
  3. //
  4. // Distributed under the MIT License, see bottom of file.
  5. // -----------------------------------------------------------------------------
  6. // BEGIN PUBLIC API
  7. // -----------------------------------------------------------------------------
  8. #ifndef PAR_EASYCURL_H
  9. #define PAR_EASYCURL_H
  10. #ifdef __cplusplus
  11. extern "C" {
  12. #endif
  13. typedef unsigned char par_byte;
  14. // Call this before calling any other easycurl function. The flags are
  15. // currently unused, so you can just pass 0.
  16. void par_easycurl_init(unsigned int flags);
  17. // Allocates a memory buffer and downloads a data blob into it.
  18. // Returns 1 for success and 0 otherwise. The byte count should be
  19. // pre-allocated. The caller is responsible for freeing the returned data.
  20. // This does not do any caching!
  21. int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes);
  22. // Downloads a file from the given URL and saves it to disk. Returns 1 for
  23. // success and 0 otherwise.
  24. int par_easycurl_to_file(char const* srcurl, char const* dstpath);
  25. #ifdef __cplusplus
  26. }
  27. #endif
  28. // -----------------------------------------------------------------------------
  29. // END PUBLIC API
  30. // -----------------------------------------------------------------------------
  31. #ifdef PAR_EASYCURL_IMPLEMENTATION
  32. #include <string.h>
  33. #include <stdlib.h>
  34. #include <stdint.h>
  35. #include <curl/curl.h>
  36. #ifdef _MSC_VER
  37. #define strncasecmp _strnicmp
  38. #define strcasecmp _stricmp
  39. #else
  40. #include <strings.h>
  41. #endif
  42. static int _ready = 0;
  43. void par_easycurl_init(unsigned int flags)
  44. {
  45. if (!_ready) {
  46. curl_global_init(CURL_GLOBAL_DEFAULT);
  47. _ready = 1;
  48. }
  49. }
  50. void par_easycurl_shutdown()
  51. {
  52. if (_ready) {
  53. curl_global_cleanup();
  54. }
  55. }
  56. static size_t onheader(void* v, size_t size, size_t nmemb)
  57. {
  58. size_t n = size * nmemb;
  59. char* h = (char*) v;
  60. if (n > 14 && !strncasecmp("Last-Modified:", h, 14)) {
  61. char const* s = h + 14;
  62. time_t r = curl_getdate(s, 0);
  63. if (r != -1) {
  64. // TODO handle last-modified
  65. }
  66. } else if (n > 5 && !strncasecmp("ETag:", h, 5)) {
  67. // TODO handle etag
  68. }
  69. return n;
  70. }
  71. typedef struct {
  72. par_byte* data;
  73. int nbytes;
  74. } par_easycurl_buffer;
  75. static size_t onwrite(char* contents, size_t size, size_t nmemb, void* udata)
  76. {
  77. size_t realsize = size * nmemb;
  78. par_easycurl_buffer* mem = (par_easycurl_buffer*) udata;
  79. mem->data = (par_byte*) realloc(mem->data, mem->nbytes + realsize + 1);
  80. if (!mem->data) {
  81. return 0;
  82. }
  83. memcpy(mem->data + mem->nbytes, contents, realsize);
  84. mem->nbytes += realsize;
  85. mem->data[mem->nbytes] = 0;
  86. return realsize;
  87. }
  88. #if IOS_EXAMPLE
  89. bool curlToMemory(char const* url, uint8_t** data, int* nbytes)
  90. {
  91. NSString* nsurl =
  92. [NSString stringWithCString:url encoding:NSASCIIStringEncoding];
  93. NSMutableURLRequest* request =
  94. [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
  95. [request setTimeoutInterval: TIMEOUT_SECONDS];
  96. NSURLResponse* response = nil;
  97. NSError* error = nil;
  98. // Use the simple non-async API because we're in a secondary thread anyway.
  99. NSData* nsdata = [NSURLConnection sendSynchronousRequest:request
  100. returningResponse:&response
  101. error:&error];
  102. if (error == nil) {
  103. *nbytes = (int) [nsdata length];
  104. *data = (uint8_t*) malloc([nsdata length]);
  105. memcpy(*data, [nsdata bytes], [nsdata length]);
  106. return true;
  107. }
  108. BLAZE_ERROR("%s\n", [[error localizedDescription] UTF8String]);
  109. return false;
  110. }
  111. #endif
  112. int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes)
  113. {
  114. char errbuf[CURL_ERROR_SIZE] = {0};
  115. par_easycurl_buffer buffer = {(par_byte*) malloc(1), 0};
  116. long code = 0;
  117. long status = 0;
  118. CURL* handle = curl_easy_init();
  119. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  120. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  121. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  122. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  123. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  124. curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, onwrite);
  125. curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
  126. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  127. curl_easy_setopt(handle, CURLOPT_URL, url);
  128. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  129. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  130. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  131. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  132. curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errbuf);
  133. curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
  134. CURLcode res = curl_easy_perform(handle);
  135. if (res != CURLE_OK) {
  136. printf("CURL Error: %s\n", errbuf);
  137. return 0;
  138. }
  139. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  140. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  141. if (status == 304 || status >= 400) {
  142. return 0;
  143. }
  144. *data = buffer.data;
  145. *nbytes = buffer.nbytes;
  146. curl_easy_cleanup(handle);
  147. return 1;
  148. }
  149. int par_easycurl_to_file(char const* srcurl, char const* dstpath)
  150. {
  151. long code = 0;
  152. long status = 0;
  153. FILE* filehandle = fopen(dstpath, "wb");
  154. if (!filehandle) {
  155. printf("Unable to open %s for writing.\n", dstpath);
  156. return 0;
  157. }
  158. CURL* handle = curl_easy_init();
  159. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  160. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  161. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  162. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  163. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  164. curl_easy_setopt(handle, CURLOPT_WRITEDATA, filehandle);
  165. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  166. curl_easy_setopt(handle, CURLOPT_URL, srcurl);
  167. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  168. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  169. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  170. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  171. curl_easy_perform(handle);
  172. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  173. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  174. fclose(filehandle);
  175. if (status == 304 || status >= 400) {
  176. remove(dstpath);
  177. return 0;
  178. }
  179. curl_easy_cleanup(handle);
  180. return 1;
  181. }
  182. #endif // PAR_EASYCURL_IMPLEMENTATION
  183. #endif // PAR_EASYCURL_H
  184. // par_easycurl is distributed under the MIT license:
  185. //
  186. // Copyright (c) 2019 Philip Rideout
  187. //
  188. // Permission is hereby granted, free of charge, to any person obtaining a copy
  189. // of this software and associated documentation files (the "Software"), to deal
  190. // in the Software without restriction, including without limitation the rights
  191. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  192. // copies of the Software, and to permit persons to whom the Software is
  193. // furnished to do so, subject to the following conditions:
  194. //
  195. // The above copyright notice and this permission notice shall be included in
  196. // all copies or substantial portions of the Software.
  197. //
  198. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  199. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  200. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  201. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  202. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  203. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  204. // SOFTWARE.