par_easycurl.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // EASYCURL :: https://github.com/prideout/par
  2. // Wrapper around libcurl for performing simple synchronous HTTP requests.
  3. //
  4. // The MIT License
  5. // Copyright (c) 2015 Philip Rideout
  6. // -----------------------------------------------------------------------------
  7. // BEGIN PUBLIC API
  8. // -----------------------------------------------------------------------------
  9. #ifndef PAR_EASYCURL_H
  10. #define PAR_EASYCURL_H
  11. #ifdef __cplusplus
  12. extern "C" {
  13. #endif
  14. typedef unsigned char par_byte;
  15. // Call this before calling any other easycurl function. The flags are
  16. // currently unused, so you can just pass 0.
  17. void par_easycurl_init(unsigned int flags);
  18. // Allocates a memory buffer and downloads a data blob into it.
  19. // Returns 1 for success and 0 otherwise. The byte count should be
  20. // pre-allocated. The caller is responsible for freeing the returned data.
  21. // This does not do any caching!
  22. int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes);
  23. // Downloads a file from the given URL and saves it to disk. Returns 1 for
  24. // success and 0 otherwise.
  25. int par_easycurl_to_file(char const* srcurl, char const* dstpath);
  26. #ifdef __cplusplus
  27. }
  28. #endif
  29. // -----------------------------------------------------------------------------
  30. // END PUBLIC API
  31. // -----------------------------------------------------------------------------
  32. #ifdef PAR_EASYCURL_IMPLEMENTATION
  33. #include <strings.h>
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <stdint.h>
  37. #include <curl/curl.h>
  38. static int _ready = 0;
  39. void par_easycurl_init(unsigned int flags)
  40. {
  41. if (!_ready) {
  42. curl_global_init(CURL_GLOBAL_SSL);
  43. _ready = 1;
  44. }
  45. }
  46. void par_easycurl_shutdown()
  47. {
  48. if (_ready) {
  49. curl_global_cleanup();
  50. }
  51. }
  52. static size_t onheader(void* v, size_t size, size_t nmemb)
  53. {
  54. size_t n = size * nmemb;
  55. char* h = (char*) v;
  56. if (n > 14 && !strncasecmp("Last-Modified:", h, 14)) {
  57. char const* s = h + 14;
  58. time_t r = curl_getdate(s, 0);
  59. if (r != -1) {
  60. // TODO handle last-modified
  61. }
  62. } else if (n > 5 && !strncasecmp("ETag:", h, 5)) {
  63. // TODO handle etag
  64. }
  65. return n;
  66. }
  67. typedef struct {
  68. par_byte* data;
  69. int nbytes;
  70. } par_easycurl_buffer;
  71. static size_t onwrite(char* contents, size_t size, size_t nmemb, void* udata)
  72. {
  73. size_t realsize = size * nmemb;
  74. par_easycurl_buffer* mem = (par_easycurl_buffer*) udata;
  75. mem->data = (par_byte*) realloc(mem->data, mem->nbytes + realsize + 1);
  76. if (!mem->data) {
  77. return 0;
  78. }
  79. memcpy(mem->data + mem->nbytes, contents, realsize);
  80. mem->nbytes += realsize;
  81. mem->data[mem->nbytes] = 0;
  82. return realsize;
  83. }
  84. #if IOS_EXAMPLE
  85. bool curlToMemory(char const* url, uint8_t** data, int* nbytes)
  86. {
  87. NSString* nsurl =
  88. [NSString stringWithCString:url encoding:NSASCIIStringEncoding];
  89. NSMutableURLRequest* request =
  90. [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
  91. [request setTimeoutInterval : TIMEOUT_SECONDS];
  92. NSURLResponse* response = nil;
  93. NSError* error = nil;
  94. // Use the simple non-async API because we're in a secondary thread anyway.
  95. NSData* nsdata = [NSURLConnection sendSynchronousRequest:request
  96. returningResponse:&response
  97. error:&error];
  98. if (error == nil) {
  99. *nbytes = (int) [nsdata length];
  100. *data = (uint8_t*) malloc([nsdata length]);
  101. memcpy(*data, [nsdata bytes], [nsdata length]);
  102. return true;
  103. }
  104. BLAZE_ERROR("%s\n", [[error localizedDescription] UTF8String]);
  105. return false;
  106. }
  107. #endif
  108. int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes)
  109. {
  110. char errbuf[CURL_ERROR_SIZE] = {0};
  111. par_easycurl_buffer buffer = {(par_byte*) malloc(1), 0};
  112. long code = 0;
  113. long status = 0;
  114. CURL* handle = curl_easy_init();
  115. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  116. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  117. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  118. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  119. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  120. curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, onwrite);
  121. curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
  122. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  123. curl_easy_setopt(handle, CURLOPT_URL, url);
  124. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  125. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  126. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  127. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  128. curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errbuf);
  129. curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
  130. CURLcode res = curl_easy_perform(handle);
  131. if (res != CURLE_OK) {
  132. printf("CURL Error: %s\n", errbuf);
  133. return 0;
  134. }
  135. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  136. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  137. if (status == 304 || status >= 400) {
  138. return 0;
  139. }
  140. *data = buffer.data;
  141. *nbytes = buffer.nbytes;
  142. curl_easy_cleanup(handle);
  143. return 1;
  144. }
  145. int par_easycurl_to_file(char const* srcurl, char const* dstpath)
  146. {
  147. long code = 0;
  148. long status = 0;
  149. FILE* filehandle = fopen(dstpath, "wb");
  150. if (!filehandle) {
  151. printf("Unable to open %s for writing.\n", dstpath);
  152. return 0;
  153. }
  154. CURL* handle = curl_easy_init();
  155. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  156. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  157. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  158. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  159. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  160. curl_easy_setopt(handle, CURLOPT_WRITEDATA, filehandle);
  161. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  162. curl_easy_setopt(handle, CURLOPT_URL, srcurl);
  163. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  164. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  165. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  166. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  167. curl_easy_perform(handle);
  168. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  169. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  170. fclose(filehandle);
  171. if (status == 304 || status >= 400) {
  172. remove(dstpath);
  173. return 0;
  174. }
  175. curl_easy_cleanup(handle);
  176. return 1;
  177. }
  178. #endif // PAR_EASYCURL_IMPLEMENTATION
  179. #endif // PAR_EASYCURL_H