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.

158 lines
3.7KB

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