You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
3.5KB

  1. #include "util/request.hpp"
  2. #include <assert.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <curl/curl.h>
  6. namespace rack {
  7. static size_t writeStringCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
  8. std::string *str = (std::string*) userdata;
  9. size_t len = size * nmemb;
  10. str->append(ptr, len);
  11. return len;
  12. }
  13. json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ) {
  14. CURL *curl = curl_easy_init();
  15. if (!curl)
  16. return NULL;
  17. assert(dataJ);
  18. char *reqStr;
  19. if (method != GET_METHOD) {
  20. reqStr = json_dumps(dataJ, 0);
  21. }
  22. // Set URL
  23. if (method == GET_METHOD) {
  24. if (dataJ) {
  25. // Append ?key=value&... to url
  26. url += "?";
  27. bool isFirst = true;
  28. const char *key;
  29. json_t *value;
  30. json_object_foreach(dataJ, key, value) {
  31. if (json_is_string(value)) {
  32. if (!isFirst)
  33. url += "&";
  34. url += key;
  35. url += "=";
  36. const char *str = json_string_value(value);
  37. size_t len = json_string_length(value);
  38. char *escapedStr = curl_easy_escape(curl, str, len);
  39. url += escapedStr;
  40. curl_free(escapedStr);
  41. isFirst = false;
  42. }
  43. }
  44. }
  45. }
  46. curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  47. // Set HTTP method
  48. switch (method) {
  49. case GET_METHOD:
  50. // This is default
  51. break;
  52. case POST_METHOD:
  53. curl_easy_setopt(curl, CURLOPT_POST, true);
  54. break;
  55. case PUT_METHOD:
  56. curl_easy_setopt(curl, CURLOPT_PUT, true);
  57. break;
  58. case DELETE_METHOD:
  59. curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
  60. break;
  61. }
  62. // Set headers
  63. struct curl_slist *headers = NULL;
  64. headers = curl_slist_append(headers, "Accept: application/json");
  65. headers = curl_slist_append(headers, "Content-Type: application/json");
  66. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  67. // Body callbacks
  68. if (method != GET_METHOD)
  69. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqStr);
  70. std::string resText;
  71. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
  72. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText);
  73. // Perform request
  74. printf("Requesting %s\n", url.c_str());
  75. // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  76. CURLcode res = curl_easy_perform(curl);
  77. // Cleanup
  78. if (method != GET_METHOD)
  79. free(reqStr);
  80. curl_easy_cleanup(curl);
  81. curl_slist_free_all(headers);
  82. if (res == CURLE_OK) {
  83. // Parse JSON response
  84. json_error_t error;
  85. json_t *rootJ = json_loads(resText.c_str(), 0, &error);
  86. return rootJ;
  87. }
  88. return NULL;
  89. }
  90. static int xferInfoCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
  91. float *progress = (float*) clientp;
  92. if (progress) {
  93. if (dltotal <= 0)
  94. *progress = 1.0;
  95. else
  96. *progress = (float)dlnow / dltotal;
  97. }
  98. return 0;
  99. }
  100. bool requestDownload(std::string url, std::string filename, float *progress) {
  101. CURL *curl = curl_easy_init();
  102. if (!curl)
  103. return false;
  104. FILE *file = fopen(filename.c_str(), "wb");
  105. if (!file)
  106. return false;
  107. curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  108. curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
  109. curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
  110. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
  111. curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
  112. curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferInfoCallback);
  113. curl_easy_setopt(curl, CURLOPT_XFERINFODATA, progress);
  114. printf("Downloading %s\n", url.c_str());
  115. CURLcode res = curl_easy_perform(curl);
  116. curl_easy_cleanup(curl);
  117. fclose(file);
  118. if (res != CURLE_OK)
  119. remove(filename.c_str());
  120. return res == CURLE_OK;
  121. }
  122. } // namespace rack