1 /*
2  * Copyright (c) 2019 Antmicro Ltd
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr.h>
8 #include <posix/pthread.h>
9 #include <data/json.h>
10 
11 #include "civetweb.h"
12 
13 #define HTTP_PORT	8080
14 #define HTTPS_PORT	4443
15 
16 #define CIVETWEB_MAIN_THREAD_STACK_SIZE		CONFIG_MAIN_STACK_SIZE
17 
18 /* Use samllest possible value of 1024 (see the line 18619 of civetweb.c) */
19 #define MAX_REQUEST_SIZE_BYTES			1024
20 
21 K_THREAD_STACK_DEFINE(civetweb_stack, CIVETWEB_MAIN_THREAD_STACK_SIZE);
22 
23 struct civetweb_info {
24 	const char *version;
25 	const char *os;
26 	uint32_t features;
27 	const char *feature_list;
28 	const char *build;
29 	const char *compiler;
30 	const char *data_model;
31 };
32 
33 #define FIELD(struct_, member_, type_) { \
34 	.field_name = #member_, \
35 	.field_name_len = sizeof(#member_) - 1, \
36 	.offset = offsetof(struct_, member_), \
37 	.type = type_ \
38 }
39 
send_ok(struct mg_connection * conn)40 void send_ok(struct mg_connection *conn)
41 {
42 	mg_printf(conn,
43 		  "HTTP/1.1 200 OK\r\n"
44 		  "Content-Type: text/html\r\n"
45 		  "Connection: close\r\n\r\n");
46 }
47 
hello_world_handler(struct mg_connection * conn,void * cbdata)48 int hello_world_handler(struct mg_connection *conn, void *cbdata)
49 {
50 	send_ok(conn);
51 	mg_printf(conn, "<html><body>");
52 	mg_printf(conn, "<h3>Hello World from Zephyr!</h3>");
53 	mg_printf(conn, "See also:\n");
54 	mg_printf(conn, "<ul>\n");
55 	mg_printf(conn, "<li><a href=/info>system info</a></li>\n");
56 	mg_printf(conn, "<li><a href=/history>cookie demo</a></li>\n");
57 	mg_printf(conn, "</ul>\n");
58 	mg_printf(conn, "</body></html>\n");
59 
60 	return 200;
61 }
62 
system_info_handler(struct mg_connection * conn,void * cbdata)63 int system_info_handler(struct mg_connection *conn, void *cbdata)
64 {
65 	static const struct json_obj_descr descr[] = {
66 		FIELD(struct civetweb_info, version, JSON_TOK_STRING),
67 		FIELD(struct civetweb_info, os, JSON_TOK_STRING),
68 		FIELD(struct civetweb_info, feature_list, JSON_TOK_STRING),
69 		FIELD(struct civetweb_info, build, JSON_TOK_STRING),
70 		FIELD(struct civetweb_info, compiler, JSON_TOK_STRING),
71 		FIELD(struct civetweb_info, data_model, JSON_TOK_STRING),
72 	};
73 
74 	struct civetweb_info info = {};
75 	char info_str[1024] = {};
76 	int ret;
77 	int size;
78 
79 	size = mg_get_system_info(info_str, sizeof(info_str));
80 
81 	ret = json_obj_parse(info_str, size, descr, ARRAY_SIZE(descr), &info);
82 
83 	send_ok(conn);
84 
85 	if (ret < 0) {
86 		mg_printf(conn, "Could not retrieve: %d\n", ret);
87 		return 500;
88 	}
89 
90 
91 	mg_printf(conn, "<html><body>");
92 
93 	mg_printf(conn, "<h3>Server info</h3>");
94 	mg_printf(conn, "<ul>\n");
95 	mg_printf(conn, "<li>host os - %s</li>\n", info.os);
96 	mg_printf(conn, "<li>server - civetweb %s</li>\n", info.version);
97 	mg_printf(conn, "<li>compiler - %s</li>\n", info.compiler);
98 	mg_printf(conn, "<li>board - %s</li>\n", CONFIG_BOARD);
99 	mg_printf(conn, "</ul>\n");
100 
101 	mg_printf(conn, "</body></html>\n");
102 
103 	return 200;
104 }
105 
history_handler(struct mg_connection * conn,void * cbdata)106 int history_handler(struct mg_connection *conn, void *cbdata)
107 {
108 	const struct mg_request_info *req_info = mg_get_request_info(conn);
109 	const char *cookie = mg_get_header(conn, "Cookie");
110 	char history_str[64];
111 
112 	mg_get_cookie(cookie, "history", history_str, sizeof(history_str));
113 
114 	mg_printf(conn, "HTTP/1.1 200 OK\r\n");
115 	mg_printf(conn, "Connection: close\r\n");
116 	mg_printf(conn, "Set-Cookie: history='%s'\r\n", req_info->local_uri);
117 	mg_printf(conn, "Content-Type: text/html\r\n\r\n");
118 
119 	mg_printf(conn, "<html><body>");
120 
121 	mg_printf(conn, "<h3>Your URI is: %s<h3>\n", req_info->local_uri);
122 
123 	if (history_str[0] == 0) {
124 		mg_printf(conn, "<h5>This is your first visit.</h5>\n");
125 	} else {
126 		mg_printf(conn, "<h5>your last /history visit was: %s</h5>\n",
127 			  history_str);
128 	}
129 
130 	mg_printf(conn, "Some cookie-saving links to try:\n");
131 	mg_printf(conn, "<ul>\n");
132 	mg_printf(conn, "<li><a href=/history/first>first</a></li>\n");
133 	mg_printf(conn, "<li><a href=/history/second>second</a></li>\n");
134 	mg_printf(conn, "<li><a href=/history/third>third</a></li>\n");
135 	mg_printf(conn, "<li><a href=/history/fourth>fourth</a></li>\n");
136 	mg_printf(conn, "<li><a href=/history/fifth>fifth</a></li>\n");
137 	mg_printf(conn, "</ul>\n");
138 
139 	mg_printf(conn, "</body></html>\n");
140 
141 	return 200;
142 }
143 
main_pthread(void * arg)144 void *main_pthread(void *arg)
145 {
146 	static const char * const options[] = {
147 		"listening_ports",
148 		STRINGIFY(HTTP_PORT),
149 		"num_threads",
150 		"1",
151 		"max_request_size",
152 		STRINGIFY(MAX_REQUEST_SIZE_BYTES),
153 		NULL
154 	};
155 
156 	struct mg_callbacks callbacks;
157 	struct mg_context *ctx;
158 
159 	(void)arg;
160 
161 	memset(&callbacks, 0, sizeof(callbacks));
162 	ctx = mg_start(&callbacks, 0, (const char **)options);
163 
164 	if (ctx == NULL) {
165 		printf("Unable to start the server.");
166 		return 0;
167 	}
168 
169 	mg_set_request_handler(ctx, "/$", hello_world_handler, 0);
170 	mg_set_request_handler(ctx, "/info$", system_info_handler, 0);
171 	mg_set_request_handler(ctx, "/history", history_handler, 0);
172 
173 	return 0;
174 }
175 
main(void)176 void main(void)
177 {
178 	pthread_attr_t civetweb_attr;
179 	pthread_t civetweb_thread;
180 
181 	(void)pthread_attr_init(&civetweb_attr);
182 	(void)pthread_attr_setstack(&civetweb_attr, &civetweb_stack,
183 				    CIVETWEB_MAIN_THREAD_STACK_SIZE);
184 
185 	(void)pthread_create(&civetweb_thread, &civetweb_attr,
186 			     &main_pthread, 0);
187 }
188