1
9
17
18
19
28
29
30
31
32
36
37
38
39
40
58
59
60
61
62
63
64
65
66
71
72
78
79
80
81
82
83
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
121
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
192
193
194
195
201
202
203
204
210
211
212
213
219
220
221
222
223
224
225
226
/* ... */
#include <string.h>
#include <fcntl.h>
#include "esp_http_server.h"
#include "esp_chip_info.h"
#include "esp_random.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "cJSON.h"8 includes
static const char *REST_TAG = "esp-rest";
#define REST_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(REST_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
}{...} \
}{...} while (0)...
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
#define SCRATCH_BUFSIZE (10240)
typedef struct rest_server_context {
char base_path[ESP_VFS_PATH_MAX + 1];
char scratch[SCRATCH_BUFSIZE];
}{ ... } rest_server_context_t;
#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath)
{
const char *type = "text/plain";
if (CHECK_FILE_EXTENSION(filepath, ".html")) {
type = "text/html";
}{...} else if (CHECK_FILE_EXTENSION(filepath, ".js")) {
type = "application/javascript";
}{...} else if (CHECK_FILE_EXTENSION(filepath, ".css")) {
type = "text/css";
}{...} else if (CHECK_FILE_EXTENSION(filepath, ".png")) {
type = "image/png";
}{...} else if (CHECK_FILE_EXTENSION(filepath, ".ico")) {
type = "image/x-icon";
}{...} else if (CHECK_FILE_EXTENSION(filepath, ".svg")) {
type = "text/xml";
}{...}
return httpd_resp_set_type(req, type);
}{ ... }
static esp_err_t rest_common_get_handler(httpd_req_t *req)
{
char filepath[FILE_PATH_MAX];
rest_server_context_t *rest_context = (rest_server_context_t *)req->user_ctx;
strlcpy(filepath, rest_context->base_path, sizeof(filepath));
if (req->uri[strlen(req->uri) - 1] == '/') {
strlcat(filepath, "/index.html", sizeof(filepath));
}{...} else {
strlcat(filepath, req->uri, sizeof(filepath));
}{...}
int fd = open(filepath, O_RDONLY, 0);
if (fd == -1) {
ESP_LOGE(REST_TAG, "Failed to open file : %s", filepath);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
return ESP_FAIL;
}{...}
set_content_type_from_file(req, filepath);
char *chunk = rest_context->scratch;
ssize_t read_bytes;
do {
read_bytes = read(fd, chunk, SCRATCH_BUFSIZE);
if (read_bytes == -1) {
ESP_LOGE(REST_TAG, "Failed to read file : %s", filepath);
}{...} else if (read_bytes > 0) {
if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) {
close(fd);
ESP_LOGE(REST_TAG, "File sending failed!");
httpd_resp_sendstr_chunk(req, NULL);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
return ESP_FAIL;
}{...}
}{...}
}{...} while (read_bytes > 0);
close(fd);
ESP_LOGI(REST_TAG, "File sending complete");
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}{ ... }
static esp_err_t light_brightness_post_handler(httpd_req_t *req)
{
int total_len = req->content_len;
int cur_len = 0;
char *buf = ((rest_server_context_t *)(req->user_ctx))->scratch;
int received = 0;
if (total_len >= SCRATCH_BUFSIZE) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
return ESP_FAIL;
}{...}
while (cur_len < total_len) {
received = httpd_req_recv(req, buf + cur_len, total_len);
if (received <= 0) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
return ESP_FAIL;
}{...}
cur_len += received;
}{...}
buf[total_len] = '\0';
cJSON *root = cJSON_Parse(buf);
int red = cJSON_GetObjectItem(root, "red")->valueint;
int green = cJSON_GetObjectItem(root, "green")->valueint;
int blue = cJSON_GetObjectItem(root, "blue")->valueint;
ESP_LOGI(REST_TAG, "Light control: red = %d, green = %d, blue = %d", red, green, blue);
cJSON_Delete(root);
httpd_resp_sendstr(req, "Post control value successfully");
return ESP_OK;
}{ ... }
static esp_err_t system_info_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/json");
cJSON *root = cJSON_CreateObject();
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
cJSON_AddStringToObject(root, "version", IDF_VER);
cJSON_AddNumberToObject(root, "cores", chip_info.cores);
const char *sys_info = cJSON_Print(root);
httpd_resp_sendstr(req, sys_info);
free((void *)sys_info);
cJSON_Delete(root);
return ESP_OK;
}{ ... }
static esp_err_t temperature_data_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/json");
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "raw", esp_random() % 20);
const char *sys_info = cJSON_Print(root);
httpd_resp_sendstr(req, sys_info);
free((void *)sys_info);
cJSON_Delete(root);
return ESP_OK;
}{ ... }
esp_err_t start_rest_server(const char *base_path)
{
REST_CHECK(base_path, "wrong base path", err);
rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t));
REST_CHECK(rest_context, "No memory for rest context", err);
strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path));
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.uri_match_fn = httpd_uri_match_wildcard;
ESP_LOGI(REST_TAG, "Starting HTTP Server");
REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start);
httpd_uri_t system_info_get_uri = {
.uri = "/api/v1/system/info",
.method = HTTP_GET,
.handler = system_info_get_handler,
.user_ctx = rest_context
}{...};
httpd_register_uri_handler(server, &system_info_get_uri);
httpd_uri_t temperature_data_get_uri = {
.uri = "/api/v1/temp/raw",
.method = HTTP_GET,
.handler = temperature_data_get_handler,
.user_ctx = rest_context
}{...};
httpd_register_uri_handler(server, &temperature_data_get_uri);
httpd_uri_t light_brightness_post_uri = {
.uri = "/api/v1/light/brightness",
.method = HTTP_POST,
.handler = light_brightness_post_handler,
.user_ctx = rest_context
}{...};
httpd_register_uri_handler(server, &light_brightness_post_uri);
httpd_uri_t common_get_uri = {
.uri = "/*",
.method = HTTP_GET,
.handler = rest_common_get_handler,
.user_ctx = rest_context
}{...};
httpd_register_uri_handler(server, &common_get_uri);
return ESP_OK;
err_start:
free(rest_context);
err:
return ESP_FAIL;
}{ ... }