1 /*
2 * Copyright (c) 2018 the CivetWeb developers
3 * MIT License
4 */
5
6 /* Simple demo of a REST callback. */
7 #ifdef _WIN32
8 #include <windows.h>
9 #else
10 #include <unistd.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16
17 #include "cJSON.h"
18 #include "civetweb.h"
19
20
21 #ifdef NO_SSL
22 #define PORT "8089"
23 #define HOST_INFO "http://localhost:8089"
24 #else
25 #define PORT "8089r,8843s"
26 #define HOST_INFO "https://localhost:8843"
27 #endif
28
29 #define EXAMPLE_URI "/example"
30 #define EXIT_URI "/exit"
31
32 int exitNow = 0;
33
34
35 static int
SendJSON(struct mg_connection * conn,cJSON * json_obj)36 SendJSON(struct mg_connection *conn, cJSON *json_obj)
37 {
38 char *json_str = cJSON_PrintUnformatted(json_obj);
39 size_t json_str_len = strlen(json_str);
40
41 /* Send HTTP message header */
42 mg_send_http_ok(conn, "application/json; charset=utf-8", json_str_len);
43
44 /* Send HTTP message content */
45 mg_write(conn, json_str, json_str_len);
46
47 /* Free string allocated by cJSON_Print* */
48 cJSON_free(json_str);
49
50 return (int)json_str_len;
51 }
52
53
54 static unsigned request = 0; /* demo data: request counter */
55
56
57 static int
ExampleGET(struct mg_connection * conn)58 ExampleGET(struct mg_connection *conn)
59 {
60 cJSON *obj = cJSON_CreateObject();
61
62 if (!obj) {
63 /* insufficient memory? */
64 mg_send_http_error(conn, 500, "Server error");
65 return 500;
66 }
67
68
69 cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION);
70 cJSON_AddNumberToObject(obj, "request", ++request);
71 SendJSON(conn, obj);
72 cJSON_Delete(obj);
73
74 return 200;
75 }
76
77
78 static int
ExampleDELETE(struct mg_connection * conn)79 ExampleDELETE(struct mg_connection *conn)
80 {
81 request = 0;
82 mg_send_http_error(conn,
83 204,
84 "%s",
85 ""); /* Return "deleted" = "204 No Content" */
86
87 return 204;
88 }
89
90
91 static int
ExamplePUT(struct mg_connection * conn)92 ExamplePUT(struct mg_connection *conn)
93 {
94 char buffer[1024];
95 int dlen = mg_read(conn, buffer, sizeof(buffer) - 1);
96 cJSON *obj, *elem;
97 unsigned newvalue;
98
99 if ((dlen < 1) || (dlen >= sizeof(buffer))) {
100 mg_send_http_error(conn, 400, "%s", "No request body data");
101 return 400;
102 }
103 buffer[dlen] = 0;
104
105 obj = cJSON_Parse(buffer);
106 if (obj == NULL) {
107 mg_send_http_error(conn, 400, "%s", "Invalid request body data");
108 return 400;
109 }
110
111 elem = cJSON_GetObjectItemCaseSensitive(obj, "request");
112
113 if (!cJSON_IsNumber(elem)) {
114 cJSON_Delete(obj);
115 mg_send_http_error(conn,
116 400,
117 "%s",
118 "No \"request\" number in body data");
119 return 400;
120 }
121
122 newvalue = (unsigned)elem->valuedouble;
123
124 if ((double)newvalue != elem->valuedouble) {
125 cJSON_Delete(obj);
126 mg_send_http_error(conn,
127 400,
128 "%s",
129 "Invalid \"request\" number in body data");
130 return 400;
131 }
132
133 request = newvalue;
134 cJSON_Delete(obj);
135
136 mg_send_http_error(conn, 201, "%s", ""); /* Return "201 Created" */
137
138 return 201;
139 }
140
141
142 static int
ExamplePOST(struct mg_connection * conn)143 ExamplePOST(struct mg_connection *conn)
144 {
145 /* In this example, do the same for PUT and POST */
146 return ExamplePUT(conn);
147 }
148
149
150 static int
ExamplePATCH(struct mg_connection * conn)151 ExamplePATCH(struct mg_connection *conn)
152 {
153 /* In this example, do the same for PUT and PATCH */
154 return ExamplePUT(conn);
155 }
156
157
158 static int
ExampleHandler(struct mg_connection * conn,void * cbdata)159 ExampleHandler(struct mg_connection *conn, void *cbdata)
160 {
161
162 const struct mg_request_info *ri = mg_get_request_info(conn);
163 (void)cbdata; /* currently unused */
164
165 if (0 == strcmp(ri->request_method, "GET")) {
166 return ExampleGET(conn);
167 }
168 if (0 == strcmp(ri->request_method, "PUT")) {
169 return ExamplePUT(conn);
170 }
171 if (0 == strcmp(ri->request_method, "POST")) {
172 return ExamplePOST(conn);
173 }
174 if (0 == strcmp(ri->request_method, "DELETE")) {
175 return ExampleDELETE(conn);
176 }
177 if (0 == strcmp(ri->request_method, "PATCH")) {
178 return ExamplePATCH(conn);
179 }
180
181 /* this is not a GET request */
182 mg_send_http_error(
183 conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported");
184 return 405;
185 }
186
187
188 int
ExitHandler(struct mg_connection * conn,void * cbdata)189 ExitHandler(struct mg_connection *conn, void *cbdata)
190 {
191 mg_printf(conn,
192 "HTTP/1.1 200 OK\r\nContent-Type: "
193 "text/plain\r\nConnection: close\r\n\r\n");
194 mg_printf(conn, "Server will shut down.\n");
195 mg_printf(conn, "Bye!\n");
196 exitNow = 1;
197 return 1;
198 }
199
200
201 int
log_message(const struct mg_connection * conn,const char * message)202 log_message(const struct mg_connection *conn, const char *message)
203 {
204 puts(message);
205 return 1;
206 }
207
208
209 int
main(int argc,char * argv[])210 main(int argc, char *argv[])
211 {
212 const char *options[] = {"listening_ports",
213 PORT,
214 "request_timeout_ms",
215 "10000",
216 "error_log_file",
217 "error.log",
218 #ifndef NO_SSL
219 "ssl_certificate",
220 "../../resources/cert/server.pem",
221 "ssl_protocol_version",
222 "3",
223 "ssl_cipher_list",
224 "DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
225 #endif
226 "enable_auth_domain_check",
227 "no",
228 0};
229
230 struct mg_callbacks callbacks;
231 struct mg_context *ctx;
232 int err = 0;
233
234 /* Check if libcivetweb has been built with all required features. */
235 #ifndef NO_SSL
236 if (!mg_check_feature(2)) {
237 fprintf(stderr,
238 "Error: Embedded example built with SSL support, "
239 "but civetweb library build without.\n");
240 err = 1;
241 }
242
243
244 mg_init_library(MG_FEATURES_SSL);
245
246 #else
247 mg_init_library(0);
248
249 #endif
250 if (err) {
251 fprintf(stderr, "Cannot start CivetWeb - inconsistent build.\n");
252 return EXIT_FAILURE;
253 }
254
255
256 /* Callback will print error messages to console */
257 memset(&callbacks, 0, sizeof(callbacks));
258 callbacks.log_message = log_message;
259
260 /* Start CivetWeb web server */
261 ctx = mg_start(&callbacks, 0, options);
262
263 /* Check return value: */
264 if (ctx == NULL) {
265 fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");
266 return EXIT_FAILURE;
267 }
268
269 /* Add handler EXAMPLE_URI, to explain the example */
270 mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
271 mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
272
273 /* Show sone info */
274 printf("Start example: %s%s\n", HOST_INFO, EXAMPLE_URI);
275 printf("Exit example: %s%s\n", HOST_INFO, EXIT_URI);
276
277
278 /* Wait until the server should be closed */
279 while (!exitNow) {
280 #ifdef _WIN32
281 Sleep(1000);
282 #else
283 sleep(1);
284 #endif
285 }
286
287 /* Stop the server */
288 mg_stop(ctx);
289
290 printf("Server stopped.\n");
291 printf("Bye!\n");
292
293 return EXIT_SUCCESS;
294 }
295