par_easycurl.h 6.2 KB

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