1 /* Copyright (c) 2013-2018 the Civetweb developers
2  * Copyright (c) 2013 No Face Press, LLC
3  * License http://opensource.org/licenses/mit-license.php MIT License
4  */
5 
6 // Simple example program on how to use Embedded C++ interface.
7 
8 #include "CivetServer.h"
9 #include <cstring>
10 
11 #ifdef _WIN32
12 #include <windows.h>
13 #else
14 #include <unistd.h>
15 #endif
16 
17 #define DOCUMENT_ROOT "."
18 #define PORT "8081"
19 #define EXAMPLE_URI "/example"
20 #define EXIT_URI "/exit"
21 
22 
23 /* Exit flag for main loop */
24 volatile bool exitNow = false;
25 
26 
27 class ExampleHandler : public CivetHandler
28 {
29   public:
30 	bool
handleGet(CivetServer * server,struct mg_connection * conn)31 	handleGet(CivetServer *server, struct mg_connection *conn)
32 	{
33 		mg_printf(conn,
34 		          "HTTP/1.1 200 OK\r\nContent-Type: "
35 		          "text/html\r\nConnection: close\r\n\r\n");
36 		mg_printf(conn, "<html><body>\r\n");
37 		mg_printf(conn,
38 		          "<h2>This is an example text from a C++ handler</h2>\r\n");
39 		mg_printf(conn,
40 		          "<p>To see a page from the A handler <a "
41 		          "href=\"a\">click here</a></p>\r\n");
42 		mg_printf(conn,
43                   "<form action=\"a\" method=\"get\">"
44                   "To see a page from the A handler with a parameter "
45                   "<input type=\"submit\" value=\"click here\" "
46                   "name=\"param\" \\> (GET)</form>\r\n");
47         mg_printf(conn,
48                   "<form action=\"a\" method=\"post\">"
49                   "To see a page from the A handler with a parameter "
50                   "<input type=\"submit\" value=\"click here\" "
51                   "name=\"param\" \\> (POST)</form>\r\n");
52 		mg_printf(conn,
53 		          "<p>To see a page from the A/B handler <a "
54 		          "href=\"a/b\">click here</a></p>\r\n");
55 		mg_printf(conn,
56 		          "<p>To see a page from the *.foo handler <a "
57 		          "href=\"xy.foo\">click here</a></p>\r\n");
58 		mg_printf(conn,
59 		          "<p>To see a page from the WebSocket handler <a "
60 		          "href=\"ws\">click here</a></p>\r\n");
61 		mg_printf(conn,
62 		          "<p>To exit <a href=\"%s\">click here</a></p>\r\n",
63 		          EXIT_URI);
64 		mg_printf(conn, "</body></html>\r\n");
65 		return true;
66 	}
67 };
68 
69 class ExitHandler : public CivetHandler
70 {
71   public:
72 	bool
handleGet(CivetServer * server,struct mg_connection * conn)73 	handleGet(CivetServer *server, struct mg_connection *conn)
74 	{
75 		mg_printf(conn,
76 		          "HTTP/1.1 200 OK\r\nContent-Type: "
77 		          "text/plain\r\nConnection: close\r\n\r\n");
78 		mg_printf(conn, "Bye!\n");
79 		exitNow = true;
80 		return true;
81 	}
82 };
83 
84 class AHandler : public CivetHandler
85 {
86   private:
87 	bool
handleAll(const char * method,CivetServer * server,struct mg_connection * conn)88 	handleAll(const char *method,
89 	          CivetServer *server,
90 	          struct mg_connection *conn)
91 	{
92 		std::string s = "";
93 		mg_printf(conn,
94 		          "HTTP/1.1 200 OK\r\nContent-Type: "
95 		          "text/html\r\nConnection: close\r\n\r\n");
96 		mg_printf(conn, "<html><body>");
97 		mg_printf(conn, "<h2>This is the A handler for \"%s\" !</h2>", method);
98 		if (CivetServer::getParam(conn, "param", s)) {
99 			mg_printf(conn, "<p>param set to %s</p>", s.c_str());
100 		} else {
101 			mg_printf(conn, "<p>param not set</p>");
102 		}
103 		mg_printf(conn, "</body></html>\n");
104 		return true;
105 	}
106 
107   public:
108 	bool
handleGet(CivetServer * server,struct mg_connection * conn)109 	handleGet(CivetServer *server, struct mg_connection *conn)
110 	{
111 		return handleAll("GET", server, conn);
112 	}
113 	bool
handlePost(CivetServer * server,struct mg_connection * conn)114 	handlePost(CivetServer *server, struct mg_connection *conn)
115 	{
116 		return handleAll("POST", server, conn);
117 	}
118 };
119 
120 class ABHandler : public CivetHandler
121 {
122   public:
123 	bool
handleGet(CivetServer * server,struct mg_connection * conn)124 	handleGet(CivetServer *server, struct mg_connection *conn)
125 	{
126 		mg_printf(conn,
127 		          "HTTP/1.1 200 OK\r\nContent-Type: "
128 		          "text/html\r\nConnection: close\r\n\r\n");
129 		mg_printf(conn, "<html><body>");
130 		mg_printf(conn, "<h2>This is the AB handler!!!</h2>");
131 		mg_printf(conn, "</body></html>\n");
132 		return true;
133 	}
134 };
135 
136 class FooHandler : public CivetHandler
137 {
138   public:
139 	bool
handleGet(CivetServer * server,struct mg_connection * conn)140 	handleGet(CivetServer *server, struct mg_connection *conn)
141 	{
142 		/* Handler may access the request info using mg_get_request_info */
143 		const struct mg_request_info *req_info = mg_get_request_info(conn);
144 
145 		mg_printf(conn,
146 		          "HTTP/1.1 200 OK\r\nContent-Type: "
147 		          "text/html\r\nConnection: close\r\n\r\n");
148 
149 		mg_printf(conn, "<html><body>\n");
150 		mg_printf(conn, "<h2>This is the Foo GET handler!!!</h2>\n");
151 		mg_printf(conn,
152 		          "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>\n",
153 		          req_info->request_method,
154 		          req_info->request_uri,
155 		          req_info->http_version);
156 		mg_printf(conn, "</body></html>\n");
157 
158 		return true;
159 	}
160 	bool
handlePost(CivetServer * server,struct mg_connection * conn)161 	handlePost(CivetServer *server, struct mg_connection *conn)
162 	{
163 		/* Handler may access the request info using mg_get_request_info */
164 		const struct mg_request_info *req_info = mg_get_request_info(conn);
165 		long long rlen, wlen;
166 		long long nlen = 0;
167 		long long tlen = req_info->content_length;
168 		char buf[1024];
169 
170 		mg_printf(conn,
171 		          "HTTP/1.1 200 OK\r\nContent-Type: "
172 		          "text/html\r\nConnection: close\r\n\r\n");
173 
174 		mg_printf(conn, "<html><body>\n");
175 		mg_printf(conn, "<h2>This is the Foo POST handler!!!</h2>\n");
176 		mg_printf(conn,
177 		          "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>\n",
178 		          req_info->request_method,
179 		          req_info->request_uri,
180 		          req_info->http_version);
181 		mg_printf(conn, "<p>Content Length: %li</p>\n", (long)tlen);
182 		mg_printf(conn, "<pre>\n");
183 
184 		while (nlen < tlen) {
185 			rlen = tlen - nlen;
186 			if (rlen > sizeof(buf)) {
187 				rlen = sizeof(buf);
188 			}
189 			rlen = mg_read(conn, buf, (size_t)rlen);
190 			if (rlen <= 0) {
191 				break;
192 			}
193 			wlen = mg_write(conn, buf, (size_t)rlen);
194 			if (wlen != rlen) {
195 				break;
196 			}
197 			nlen += wlen;
198 		}
199 
200 		mg_printf(conn, "\n</pre>\n");
201 		mg_printf(conn, "</body></html>\n");
202 
203 		return true;
204 	}
205 
206     #define fopen_recursive fopen
207 
208     bool
handlePut(CivetServer * server,struct mg_connection * conn)209         handlePut(CivetServer *server, struct mg_connection *conn)
210     {
211         /* Handler may access the request info using mg_get_request_info */
212         const struct mg_request_info *req_info = mg_get_request_info(conn);
213         long long rlen, wlen;
214         long long nlen = 0;
215         long long tlen = req_info->content_length;
216         FILE * f;
217         char buf[1024];
218         int fail = 0;
219 
220 #ifdef _WIN32
221         _snprintf(buf, sizeof(buf), "D:\\somewhere\\%s\\%s", req_info->remote_user, req_info->local_uri);
222         buf[sizeof(buf)-1] = 0;
223         if (strlen(buf)>255) {
224             /* Windows will not work with path > 260 (MAX_PATH), unless we use
225              * the unicode API. However, this is just an example code: A real
226              * code will probably never store anything to D:\\somewhere and
227              * must be adapted to the specific needs anyhow. */
228             fail = 1;
229             f = NULL;
230         } else {
231             f = fopen_recursive(buf, "wb");
232         }
233 #else
234         snprintf(buf, sizeof(buf), "~/somewhere/%s/%s", req_info->remote_user, req_info->local_uri);
235         buf[sizeof(buf)-1] = 0;
236         if (strlen(buf)>1020) {
237             /* The string is too long and probably truncated. Make sure an
238              * UTF-8 string is never truncated between the UTF-8 code bytes.
239              * This example code must be adapted to the specific needs. */
240             fail = 1;
241             f = NULL;
242         } else {
243             f = fopen_recursive(buf, "w");
244         }
245 #endif
246 
247         if (!f) {
248             fail = 1;
249         } else {
250             while (nlen < tlen) {
251                 rlen = tlen - nlen;
252                 if (rlen > sizeof(buf)) {
253                     rlen = sizeof(buf);
254                 }
255                 rlen = mg_read(conn, buf, (size_t)rlen);
256                 if (rlen <= 0) {
257                     fail = 1;
258                     break;
259                 }
260                 wlen = fwrite(buf, 1, (size_t)rlen, f);
261                 if (wlen != rlen) {
262                     fail = 1;
263                     break;
264                 }
265                 nlen += wlen;
266             }
267             fclose(f);
268         }
269 
270         if (fail) {
271             mg_printf(conn,
272                 "HTTP/1.1 409 Conflict\r\n"
273                 "Content-Type: text/plain\r\n"
274                 "Connection: close\r\n\r\n");
275         } else {
276             mg_printf(conn,
277                 "HTTP/1.1 201 Created\r\n"
278                 "Content-Type: text/plain\r\n"
279                 "Connection: close\r\n\r\n");
280         }
281 
282         return true;
283     }
284 };
285 
286 class WsStartHandler : public CivetHandler
287 {
288   public:
289 	bool
handleGet(CivetServer * server,struct mg_connection * conn)290 	handleGet(CivetServer *server, struct mg_connection *conn)
291 	{
292 
293 	mg_printf(conn,
294 	          "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
295 	          "close\r\n\r\n");
296 
297 	mg_printf(conn, "<!DOCTYPE html>\n");
298 	mg_printf(conn, "<html>\n<head>\n");
299 	mg_printf(conn, "<meta charset=\"UTF-8\">\n");
300 	mg_printf(conn, "<title>Embedded websocket example</title>\n");
301 
302 #ifdef USE_WEBSOCKET
303 	/* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ...
304 	 * xhtml style */
305 	mg_printf(conn, "<script>\n");
306 	mg_printf(
307 	    conn,
308 	    "var i=0\n"
309 	    "function load() {\n"
310 	    "  var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
311 	    "  connection = new WebSocket(wsproto + '//' + window.location.host + "
312 	    "'/websocket');\n"
313 	    "  websock_text_field = "
314 	    "document.getElementById('websock_text_field');\n"
315 	    "  connection.onmessage = function (e) {\n"
316 	    "    websock_text_field.innerHTML=e.data;\n"
317 	    "    i=i+1;"
318 	    "    connection.send(i);\n"
319 	    "  }\n"
320 	    "  connection.onerror = function (error) {\n"
321 	    "    alert('WebSocket error');\n"
322 	    "    connection.close();\n"
323 	    "  }\n"
324 	    "}\n");
325 	/* mg_printf(conn, "]]></script>\n"); ... xhtml style */
326 	mg_printf(conn, "</script>\n");
327 	mg_printf(conn, "</head>\n<body onload=\"load()\">\n");
328 	mg_printf(
329 	    conn,
330 	    "<div id='websock_text_field'>No websocket connection yet</div>\n");
331 #else
332 	mg_printf(conn, "</head>\n<body>\n");
333 	mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n");
334 #endif
335 	mg_printf(conn, "</body>\n</html>\n");
336 
337 	return 1;
338 }
339 };
340 
341 
342 #ifdef USE_WEBSOCKET
343 class WebSocketHandler : public CivetWebSocketHandler {
344 
handleConnection(CivetServer * server,const struct mg_connection * conn)345 	virtual bool handleConnection(CivetServer *server,
346 	                              const struct mg_connection *conn) {
347 		printf("WS connected\n");
348 		return true;
349 	}
350 
handleReadyState(CivetServer * server,struct mg_connection * conn)351 	virtual void handleReadyState(CivetServer *server,
352 	                              struct mg_connection *conn) {
353 		printf("WS ready\n");
354 
355 		const char *text = "Hello from the websocket ready handler";
356 		mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, text, strlen(text));
357 	}
358 
handleData(CivetServer * server,struct mg_connection * conn,int bits,char * data,size_t data_len)359 	virtual bool handleData(CivetServer *server,
360 	                        struct mg_connection *conn,
361 	                        int bits,
362 	                        char *data,
363 	                        size_t data_len) {
364 		printf("WS got %lu bytes: ", (long unsigned)data_len);
365 		fwrite(data, 1, data_len, stdout);
366 		printf("\n");
367 
368 		mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, data, data_len);
369 		return (data_len<4);
370 	}
371 
handleClose(CivetServer * server,const struct mg_connection * conn)372 	virtual void handleClose(CivetServer *server,
373 	                         const struct mg_connection *conn) {
374 		printf("WS closed\n");
375 	}
376 };
377 #endif
378 
379 
380 int
main(int argc,char * argv[])381 main(int argc, char *argv[])
382 {
383 	const char *options[] = {
384 	    "document_root", DOCUMENT_ROOT, "listening_ports", PORT, 0};
385 
386     std::vector<std::string> cpp_options;
387     for (int i=0; i<(sizeof(options)/sizeof(options[0])-1); i++) {
388         cpp_options.push_back(options[i]);
389     }
390 
391 	// CivetServer server(options); // <-- C style start
392 	CivetServer server(cpp_options); // <-- C++ style start
393 
394 	ExampleHandler h_ex;
395 	server.addHandler(EXAMPLE_URI, h_ex);
396 
397 	ExitHandler h_exit;
398 	server.addHandler(EXIT_URI, h_exit);
399 
400 	AHandler h_a;
401 	server.addHandler("/a", h_a);
402 
403 	ABHandler h_ab;
404 	server.addHandler("/a/b", h_ab);
405 
406 	WsStartHandler h_ws;
407 	server.addHandler("/ws", h_ws);
408 
409 #ifdef NO_FILES
410 	/* This handler will handle "everything else", including
411 	 * requests to files. If this handler is installed,
412 	 * NO_FILES should be set. */
413 	FooHandler h_foo;
414 	server.addHandler("", h_foo);
415 
416 	printf("See a page from the \"all\" handler at http://localhost:%s/\n", PORT);
417 #else
418 	FooHandler h_foo;
419 	server.addHandler("**.foo", h_foo);
420 	printf("Browse files at http://localhost:%s/\n", PORT);
421 #endif
422 
423 #ifdef USE_WEBSOCKET
424 	WebSocketHandler h_websocket;
425 	server.addWebSocketHandler("/websocket", h_websocket);
426 	printf("Run websocket example at http://localhost:%s/ws\n", PORT);
427 #endif
428 
429 	printf("Run example at http://localhost:%s%s\n", PORT, EXAMPLE_URI);
430 	printf("Exit at http://localhost:%s%s\n", PORT, EXIT_URI);
431 
432 	while (!exitNow) {
433 #ifdef _WIN32
434 		Sleep(1000);
435 #else
436 		sleep(1);
437 #endif
438 	}
439 
440 	printf("Bye!\n");
441 
442 	return 0;
443 }
444