rest.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Copyright (c) 2018 the CivetWeb developers
  3. * Revisited version: Copyright (c) 2022 the CivetWeb developers
  4. * MIT License
  5. */
  6. /* Simple demo of a REST callback. */
  7. #ifdef _WIN32
  8. #include <windows.h>
  9. #else
  10. #include <unistd.h>
  11. #endif
  12. #include <stdarg.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include "cJSON.h"
  17. #include "civetweb.h"
  18. #define PORT "8089"
  19. #define HOST_INFO "http://localhost:8089"
  20. #define EXAMPLE_URI "/res/*/*"
  21. #define EXIT_URI "/exit"
  22. int exitNow = 0;
  23. static int
  24. SendJSON(struct mg_connection *conn, cJSON *json_obj)
  25. {
  26. char *json_str = cJSON_PrintUnformatted(json_obj);
  27. size_t json_str_len = strlen(json_str);
  28. /* Send HTTP message header (+1 for \n) */
  29. mg_send_http_ok(conn, "application/json; charset=utf-8", json_str_len + 1);
  30. /* Send HTTP message content */
  31. mg_write(conn, json_str, json_str_len);
  32. /* Add a newline. This is not required, but the result is more
  33. * human-readable in a debuger. */
  34. mg_write(conn, "\n", 1);
  35. /* Free string allocated by cJSON_Print* */
  36. cJSON_free(json_str);
  37. return (int)json_str_len;
  38. }
  39. static unsigned request = 0; /* demo data: request counter */
  40. static int
  41. ExampleGET(struct mg_connection *conn, const char *p1, const char *p2)
  42. {
  43. cJSON *obj = cJSON_CreateObject();
  44. if (!obj) {
  45. /* insufficient memory? */
  46. mg_send_http_error(conn, 500, "Server error");
  47. return 500;
  48. }
  49. printf("GET %s/%s\n", p1, p2);
  50. cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION);
  51. cJSON_AddStringToObject(obj, "path1", p1);
  52. cJSON_AddStringToObject(obj, "path2", p2);
  53. cJSON_AddNumberToObject(obj, "request", ++request);
  54. SendJSON(conn, obj);
  55. cJSON_Delete(obj);
  56. return 200;
  57. }
  58. static int
  59. ExampleDELETE(struct mg_connection *conn, const char *p1, const char *p2)
  60. {
  61. printf("DELETE %s/%s\n", p1, p2);
  62. mg_send_http_error(conn,
  63. 204,
  64. "%s",
  65. ""); /* Return "deleted" = "204 No Content" */
  66. return 204;
  67. }
  68. static int
  69. ExamplePUT(struct mg_connection *conn, const char *p1, const char *p2)
  70. {
  71. char buffer[1024];
  72. int dlen = mg_read(conn, buffer, sizeof(buffer) - 1);
  73. cJSON *obj, *elem;
  74. unsigned newvalue;
  75. printf("PUT %s/%s\n", p1, p2);
  76. if ((dlen < 1) || (dlen >= sizeof(buffer))) {
  77. mg_send_http_error(conn, 400, "%s", "No request body data");
  78. return 400;
  79. }
  80. buffer[dlen] = 0;
  81. obj = cJSON_Parse(buffer);
  82. if (obj == NULL) {
  83. mg_send_http_error(conn, 400, "%s", "Invalid request body data");
  84. return 400;
  85. }
  86. elem = cJSON_GetObjectItemCaseSensitive(obj, "request");
  87. if (!cJSON_IsNumber(elem)) {
  88. cJSON_Delete(obj);
  89. mg_send_http_error(conn,
  90. 400,
  91. "%s",
  92. "No \"request\" number in body data");
  93. return 400;
  94. }
  95. newvalue = (unsigned)elem->valuedouble;
  96. if ((double)newvalue != elem->valuedouble) {
  97. cJSON_Delete(obj);
  98. mg_send_http_error(conn,
  99. 400,
  100. "%s",
  101. "Invalid \"request\" number in body data");
  102. return 400;
  103. }
  104. request = newvalue;
  105. cJSON_Delete(obj);
  106. mg_send_http_error(conn, 201, "%s", ""); /* Return "201 Created" */
  107. return 201;
  108. }
  109. #if 0 /* Old version: User code had to split the url. */
  110. static int
  111. mg_vsplit(const char *url, const char *pattern, va_list va)
  112. {
  113. int ret = 0;
  114. while (*url && *pattern) {
  115. if (*url == *pattern) {
  116. url++;
  117. pattern++;
  118. } else if (*pattern == '*') {
  119. char *p = va_arg(va, char *);
  120. size_t l = va_arg(va, size_t);
  121. if (p == NULL || l == 0) {
  122. return 0;
  123. }
  124. while ((*url != '/') && (*url != 0)) {
  125. if (l == 0) {
  126. return 0;
  127. }
  128. l--;
  129. *p = *url;
  130. p++;
  131. url++;
  132. }
  133. *p = 0;
  134. pattern++;
  135. ret++;
  136. } else {
  137. return 0;
  138. }
  139. }
  140. return ret;
  141. }
  142. static int
  143. mg_split(const char *url, const char *pattern, ...)
  144. {
  145. int ret;
  146. va_list va;
  147. va_start(va, pattern);
  148. ret = mg_vsplit(url, pattern, va);
  149. va_end(va);
  150. return ret;
  151. }
  152. #endif
  153. static int
  154. ExampleHandler(struct mg_connection *conn, void *cbdata)
  155. {
  156. char path1[1024], path2[1024];
  157. const struct mg_request_info *ri = mg_get_request_info(conn);
  158. const char *url = ri->local_uri;
  159. size_t url_len = strlen(url);
  160. /* Pattern matching */
  161. #if 0 /* Old version: User code had to split the url. */
  162. if (2
  163. != mg_split(
  164. url, EXAMPLE_URI, path1, sizeof(path1), path2, sizeof(path2))) {
  165. mg_send_http_error(conn, 404, "Invalid path: %s\n", url);
  166. return 404;
  167. }
  168. #else /* New version: User mg_match. */
  169. struct mg_match_context mcx;
  170. mcx.case_sensitive = 0;
  171. ptrdiff_t ret = mg_match(EXAMPLE_URI, url, &mcx);
  172. if ((ret != url_len) || (mcx.num_matches != 2)) {
  173. /* Note: Could have done this with a $ at the end of the match
  174. * pattern as well. Then we would have to check for a return value
  175. * of -1 only. Here we use this version as minumum modification
  176. * of the existing code. */
  177. printf("Match ret: %i\n", (int)ret);
  178. mg_send_http_error(conn, 404, "Invalid path: %s\n", url);
  179. return 404;
  180. }
  181. memcpy(path1, mcx.match[0].str, mcx.match[0].len);
  182. path1[mcx.match[0].len] = 0;
  183. memcpy(path2, mcx.match[1].str, mcx.match[1].len);
  184. path2[mcx.match[1].len] = 0;
  185. #endif
  186. (void)cbdata; /* currently unused */
  187. /* According to method */
  188. if (0 == strcmp(ri->request_method, "GET")) {
  189. return ExampleGET(conn, path1, path2);
  190. }
  191. if ((0 == strcmp(ri->request_method, "PUT"))
  192. || (0 == strcmp(ri->request_method, "POST"))
  193. || (0 == strcmp(ri->request_method, "PATCH"))) {
  194. /* In this example, do the same for PUT, POST and PATCH */
  195. return ExamplePUT(conn, path1, path2);
  196. }
  197. if (0 == strcmp(ri->request_method, "DELETE")) {
  198. return ExampleDELETE(conn, path1, path2);
  199. }
  200. /* this is not a GET request */
  201. mg_send_http_error(
  202. conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported");
  203. return 405;
  204. }
  205. static int
  206. ExitHandler(struct mg_connection *conn, void *cbdata)
  207. {
  208. mg_printf(conn,
  209. "HTTP/1.1 200 OK\r\nContent-Type: "
  210. "text/plain\r\nConnection: close\r\n\r\n");
  211. mg_printf(conn, "Server will shut down.\n");
  212. mg_printf(conn, "Bye!\n");
  213. exitNow = 1;
  214. return 1;
  215. }
  216. static int
  217. log_message(const struct mg_connection *conn, const char *message)
  218. {
  219. puts(message);
  220. return 1;
  221. }
  222. int
  223. main(int argc, char *argv[])
  224. {
  225. const char *options[] = {"listening_ports",
  226. PORT,
  227. "request_timeout_ms",
  228. "10000",
  229. "error_log_file",
  230. "error.log",
  231. 0};
  232. struct mg_callbacks callbacks;
  233. struct mg_context *ctx;
  234. int err = 0;
  235. /* Init libcivetweb. */
  236. mg_init_library(0);
  237. /* Callback will print error messages to console */
  238. memset(&callbacks, 0, sizeof(callbacks));
  239. callbacks.log_message = log_message;
  240. /* Start CivetWeb web server */
  241. ctx = mg_start(&callbacks, 0, options);
  242. /* Check return value: */
  243. if (ctx == NULL) {
  244. fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");
  245. return EXIT_FAILURE;
  246. }
  247. /* Add handler EXAMPLE_URI, to explain the example */
  248. mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
  249. mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
  250. /* Show some info */
  251. printf("Start example: %s%s\n", HOST_INFO, EXAMPLE_URI);
  252. printf("Exit example: %s%s\n", HOST_INFO, EXIT_URI);
  253. /* Wait until the server should be closed */
  254. while (!exitNow) {
  255. #ifdef _WIN32
  256. Sleep(1000);
  257. #else
  258. sleep(1);
  259. #endif
  260. }
  261. /* Stop the server */
  262. mg_stop(ctx);
  263. printf("Server stopped.\n");
  264. printf("Bye!\n");
  265. return EXIT_SUCCESS;
  266. }