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. #endif // PAR_EASYCURL_H
  30. // -----------------------------------------------------------------------------
  31. // END PUBLIC API
  32. // -----------------------------------------------------------------------------
  33. #ifdef PAR_EASYCURL_IMPLEMENTATION
  34. #include <strings.h>
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #include <stdint.h>
  38. #include <curl/curl.h>
  39. static int _ready = 0;
  40. void par_easycurl_init(unsigned int flags)
  41. {
  42. if (!_ready) {
  43. curl_global_init(CURL_GLOBAL_SSL);
  44. _ready = 1;
  45. }
  46. }
  47. void par_easycurl_shutdown()
  48. {
  49. if (_ready) {
  50. curl_global_cleanup();
  51. }
  52. }
  53. static size_t onheader(void* v, size_t size, size_t nmemb)
  54. {
  55. size_t n = size * nmemb;
  56. char* h = (char*) v;
  57. if (n > 14 && !strncasecmp("Last-Modified:", h, 14)) {
  58. char const* s = h + 14;
  59. time_t r = curl_getdate(s, 0);
  60. if (r != -1) {
  61. // TODO handle last-modified
  62. }
  63. } else if (n > 5 && !strncasecmp("ETag:", h, 5)) {
  64. // TODO handle etag
  65. }
  66. return n;
  67. }
  68. typedef struct {
  69. par_byte* data;
  70. int nbytes;
  71. } par_easycurl_buffer;
  72. static size_t onwrite(char* contents, size_t size, size_t nmemb, void* udata)
  73. {
  74. size_t realsize = size * nmemb;
  75. par_easycurl_buffer* mem = (par_easycurl_buffer*) udata;
  76. mem->data = (par_byte*) realloc(mem->data, mem->nbytes + realsize + 1);
  77. if (!mem->data) {
  78. return 0;
  79. }
  80. memcpy(mem->data + mem->nbytes, contents, realsize);
  81. mem->nbytes += realsize;
  82. mem->data[mem->nbytes] = 0;
  83. return realsize;
  84. }
  85. #if IOS_EXAMPLE
  86. bool curlToMemory(char const* url, uint8_t** data, int* nbytes)
  87. {
  88. NSString* nsurl =
  89. [NSString stringWithCString:url encoding:NSASCIIStringEncoding];
  90. NSMutableURLRequest* request =
  91. [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
  92. [request setTimeoutInterval : TIMEOUT_SECONDS];
  93. NSURLResponse* response = nil;
  94. NSError* error = nil;
  95. // Use the simple non-async API because we're in a secondary thread anyway.
  96. NSData* nsdata = [NSURLConnection sendSynchronousRequest:request
  97. returningResponse:&response
  98. error:&error];
  99. if (error == nil) {
  100. *nbytes = (int) [nsdata length];
  101. *data = (uint8_t*) malloc([nsdata length]);
  102. memcpy(*data, [nsdata bytes], [nsdata length]);
  103. return true;
  104. }
  105. BLAZE_ERROR("%s\n", [[error localizedDescription] UTF8String]);
  106. return false;
  107. }
  108. #endif
  109. int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes)
  110. {
  111. char errbuf[CURL_ERROR_SIZE] = {0};
  112. par_easycurl_buffer buffer = {(par_byte*) malloc(1), 0};
  113. long code = 0;
  114. long status = 0;
  115. CURL* handle = curl_easy_init();
  116. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  117. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  118. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  119. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  120. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  121. curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, onwrite);
  122. curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
  123. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  124. curl_easy_setopt(handle, CURLOPT_URL, url);
  125. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  126. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  127. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  128. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  129. curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errbuf);
  130. curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
  131. CURLcode res = curl_easy_perform(handle);
  132. if (res != CURLE_OK) {
  133. printf("CURL Error: %s\n", errbuf);
  134. return 0;
  135. }
  136. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  137. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  138. if (status == 304 || status >= 400) {
  139. return 0;
  140. }
  141. *data = buffer.data;
  142. *nbytes = buffer.nbytes;
  143. curl_easy_cleanup(handle);
  144. return 1;
  145. }
  146. int par_easycurl_to_file(char const* srcurl, char const* dstpath)
  147. {
  148. long code = 0;
  149. long status = 0;
  150. FILE* filehandle = fopen(dstpath, "wb");
  151. if (!filehandle) {
  152. printf("Unable to open %s for writing.\n", dstpath);
  153. return 0;
  154. }
  155. CURL* handle = curl_easy_init();
  156. curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
  157. curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
  158. curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
  159. curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
  160. curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
  161. curl_easy_setopt(handle, CURLOPT_WRITEDATA, filehandle);
  162. curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
  163. curl_easy_setopt(handle, CURLOPT_URL, srcurl);
  164. curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
  165. curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
  166. curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
  167. curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
  168. curl_easy_perform(handle);
  169. curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
  170. curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
  171. fclose(filehandle);
  172. if (status == 304 || status >= 400) {
  173. remove(dstpath);
  174. return 0;
  175. }
  176. curl_easy_cleanup(handle);
  177. return 1;
  178. }
  179. #endif // PAR_EASYCURL_IMPLEMENTATION