|
@@ -137,14 +137,20 @@ static const char *http_dos_date(uint32_t dos_date)
|
|
static const char text_plain[] = "text/plain; charset=\"UTF-8\"";
|
|
static const char text_plain[] = "text/plain; charset=\"UTF-8\"";
|
|
|
|
|
|
enum hsp_flags {
|
|
enum hsp_flags {
|
|
- HSP_CLOSE = 1,
|
|
|
|
- HSP_CRLF = 2
|
|
|
|
|
|
+ HSP_CLOSE = 1,
|
|
|
|
+ HSP_CRLF = 2,
|
|
|
|
+ HSP_CLOSE_SOCKET = 4
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static void httpd_print_request(const httpd_req_t *req)
|
|
|
|
+{
|
|
|
|
+ printf("[HTTP] %s %s\n", http_method_str(req->method), req->uri);
|
|
|
|
+}
|
|
|
|
+
|
|
static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
unsigned int rcode,
|
|
unsigned int rcode,
|
|
const char *body, size_t blen,
|
|
const char *body, size_t blen,
|
|
- enum hsp_flags flags)
|
|
|
|
|
|
+ enum hsp_flags flags, unsigned int refresh)
|
|
{
|
|
{
|
|
char *header = NULL;
|
|
char *header = NULL;
|
|
esp_err_t err;
|
|
esp_err_t err;
|
|
@@ -157,6 +163,12 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
|
|
const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
|
|
bool redirect = rcode >= 300 && rcode <= 399;
|
|
bool redirect = rcode >= 300 && rcode <= 399;
|
|
|
|
|
|
|
|
+ static const char refresher[] = "Refresh: %u;url=/";
|
|
|
|
+ char refresh_str[sizeof(refresher)-2 + sizeof(unsigned int)*3];
|
|
|
|
+ refresh_str[0] = '\0';
|
|
|
|
+ if (refresh)
|
|
|
|
+ snprintf(refresh_str, sizeof refresh_str, refresher, refresh);
|
|
|
|
+
|
|
if (redirect) {
|
|
if (redirect) {
|
|
size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
|
|
size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
|
|
flags |= HSP_CRLF;
|
|
flags |= HSP_CRLF;
|
|
@@ -167,11 +179,11 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
"Content-Length: %zu\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Date %s\r\n"
|
|
"Date %s\r\n"
|
|
"Location: %.*s\r\n"
|
|
"Location: %.*s\r\n"
|
|
- "%s"
|
|
|
|
|
|
+ "%s%s"
|
|
"\r\n"
|
|
"\r\n"
|
|
"%3u Redirect ",
|
|
"%3u Redirect ",
|
|
rcode, text_plain, blen + blenadj,
|
|
rcode, text_plain, blen + blenadj,
|
|
- now, (int)blen, body, closer, rcode);
|
|
|
|
|
|
+ now, (int)blen, body, closer, refresh_str, rcode);
|
|
} else {
|
|
} else {
|
|
size_t blenadj = (flags & HSP_CRLF) ? 2 : 0;
|
|
size_t blenadj = (flags & HSP_CRLF) ? 2 : 0;
|
|
|
|
|
|
@@ -179,10 +191,12 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
"HTTP/1.1 %u\r\n"
|
|
"HTTP/1.1 %u\r\n"
|
|
"Content-Type: %s\r\n"
|
|
"Content-Type: %s\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
|
|
+ "Cache-Control: no-store\r\n"
|
|
"Date: %s\r\n"
|
|
"Date: %s\r\n"
|
|
- "%s"
|
|
|
|
|
|
+ "%s%s"
|
|
"\r\n",
|
|
"\r\n",
|
|
- rcode, text_plain, blen + blenadj, now, closer);
|
|
|
|
|
|
+ rcode, text_plain, blen + blenadj, now,
|
|
|
|
+ closer, refresh_str);
|
|
}
|
|
}
|
|
|
|
|
|
if (!header)
|
|
if (!header)
|
|
@@ -199,12 +213,13 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
if (header)
|
|
if (header)
|
|
free(header);
|
|
free(header);
|
|
|
|
|
|
- return err;
|
|
|
|
|
|
+ /* Sending ESP_FAIL causes the socket to be immediately closed */
|
|
|
|
+ return err ? err : (flags & HSP_CLOSE_SOCKET) ? ESP_FAIL : ESP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
-#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF)
|
|
|
|
|
|
+#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF, 0)
|
|
|
|
|
|
-static esp_err_t httpd_err_404(httpd_req_t *req)
|
|
|
|
|
|
+static esp_err_t httpd_err_enoent(httpd_req_t *req)
|
|
{
|
|
{
|
|
return HTTP_ERR(req, 404, "URI not found");
|
|
return HTTP_ERR(req, 404, "URI not found");
|
|
}
|
|
}
|
|
@@ -214,6 +229,11 @@ static esp_err_t httpd_send_ok(httpd_req_t *req)
|
|
return HTTP_ERR(req, 200, "OK");
|
|
return HTTP_ERR(req, 200, "OK");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static esp_err_t httpd_err_enomem(httpd_req_t *req)
|
|
|
|
+{
|
|
|
|
+ return HTTP_ERR(req, 503, "Out of memory");
|
|
|
|
+}
|
|
|
|
+
|
|
static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
|
|
static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
|
|
{
|
|
{
|
|
char *response = NULL;
|
|
char *response = NULL;
|
|
@@ -236,7 +256,8 @@ static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
|
|
len = 0;
|
|
len = 0;
|
|
|
|
|
|
esp_err_t rv = httpd_send_plain(req, err ? 400 : 200, response, len,
|
|
esp_err_t rv = httpd_send_plain(req, err ? 400 : 200, response, len,
|
|
- HSP_CLOSE|HSP_CRLF);
|
|
|
|
|
|
+ HSP_CLOSE|HSP_CLOSE_SOCKET|HSP_CRLF,
|
|
|
|
+ reboot_time+5);
|
|
if (response)
|
|
if (response)
|
|
free(response);
|
|
free(response);
|
|
|
|
|
|
@@ -252,40 +273,69 @@ static esp_err_t httpd_firmware_update(httpd_req_t *req)
|
|
return httpd_update_done(req, "Firmware", rv);
|
|
return httpd_update_done(req, "Firmware", rv);
|
|
}
|
|
}
|
|
|
|
|
|
-static esp_err_t httpd_set_config(httpd_req_t *req)
|
|
|
|
|
|
+static esp_err_t httpd_set_config(httpd_req_t *req, const char *query)
|
|
{
|
|
{
|
|
- FILE *f = httpd_fopen_read(req);
|
|
|
|
- if (!f)
|
|
|
|
- return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
|
|
|
+ FILE *f;
|
|
|
|
+ size_t qlen;
|
|
|
|
|
|
- int rv = read_config(f);
|
|
|
|
- fclose(f);
|
|
|
|
- if (rv) {
|
|
|
|
- rv = FWUPDATE_ERR_CONFIG_READ;
|
|
|
|
- } else {
|
|
|
|
- rv = save_config();
|
|
|
|
- if (rv)
|
|
|
|
- rv = FWUPDATE_ERR_CONFIG_SAVE;
|
|
|
|
|
|
+ int rv1 = 0;
|
|
|
|
+ if (query) {
|
|
|
|
+ rv1 = set_config_url_string(query);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int rv2 = 0;
|
|
|
|
+ f = NULL;
|
|
|
|
+ if (req->content_len) {
|
|
|
|
+ f = httpd_fopen_read(req);
|
|
|
|
+ if (!f)
|
|
|
|
+ return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
}
|
|
}
|
|
|
|
+ rv2 = read_config(f, true);
|
|
|
|
+ if (f)
|
|
|
|
+ fclose(f);
|
|
|
|
|
|
- return httpd_update_done(req, "Configuration", rv);
|
|
|
|
|
|
+ return httpd_update_done(req, "Configuration", rv1 ? rv1 : rv2);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static esp_err_t httpd_set_lang(httpd_req_t *req, const char *query)
|
|
|
|
+{
|
|
|
|
+ if (query && *query) {
|
|
|
|
+ setenv_config("LANG", query);
|
|
|
|
+ read_config(NULL, true); /* Save configuration */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return httpd_send_plain(req, 302, "/", 1, 0, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define STRING_MATCHES(str, len, what) \
|
|
|
|
+ (((len) == sizeof(what)-1) && !memcmp((str), (what), sizeof(what)-1))
|
|
|
|
+
|
|
static esp_err_t httpd_sys_post_handler(httpd_req_t *req)
|
|
static esp_err_t httpd_sys_post_handler(httpd_req_t *req)
|
|
{
|
|
{
|
|
- printf("[POST] len = %zu uri = \"%s\"\n",
|
|
|
|
- req->content_len, req->uri);
|
|
|
|
|
|
+ httpd_print_request(req);
|
|
|
|
|
|
- if (!req->content_len)
|
|
|
|
|
|
+ if (!httpd_req_get_hdr_value_len(req, "Content-Length"))
|
|
return HTTP_ERR(req, 411, "Length required");
|
|
return HTTP_ERR(req, 411, "Length required");
|
|
|
|
|
|
- if (!strcmp(req->uri, "/sys/fwupdate"))
|
|
|
|
|
|
+ if (strncmp(req->uri, "/sys/", 5))
|
|
|
|
+ return httpd_err_enoent(req); /* This should never happen */
|
|
|
|
+
|
|
|
|
+ const char *file = req->uri + 5;
|
|
|
|
+ size_t filelen = strcspn(file, "?");
|
|
|
|
+ const char *query = NULL;
|
|
|
|
+ if (file[filelen] == '?')
|
|
|
|
+ query = &file[filelen] + 1;
|
|
|
|
+
|
|
|
|
+ if (STRING_MATCHES(file, filelen, "fwupdate"))
|
|
return httpd_firmware_update(req);
|
|
return httpd_firmware_update(req);
|
|
|
|
|
|
- if (!strcmp(req->uri, "/sys/setconfig"))
|
|
|
|
- return httpd_set_config(req);
|
|
|
|
|
|
+ if (STRING_MATCHES(file, filelen, "setconfig"))
|
|
|
|
+ return httpd_set_config(req, query);
|
|
|
|
+
|
|
|
|
+ if (STRING_MATCHES(file, filelen, "lang"))
|
|
|
|
+ return httpd_set_lang(req, query);
|
|
|
|
|
|
- return httpd_err_404(req);
|
|
|
|
|
|
+ return httpd_err_enoent(req);
|
|
}
|
|
}
|
|
|
|
|
|
static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
@@ -295,6 +345,7 @@ static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
|
|
|
httpd_resp_set_type(req, text_plain);
|
|
httpd_resp_set_type(req, text_plain);
|
|
|
|
+ httpd_resp_set_hdr(req, "Cache-Control", "no-store");
|
|
|
|
|
|
int rv = write_config(f);
|
|
int rv = write_config(f);
|
|
fclose(f);
|
|
fclose(f);
|
|
@@ -303,10 +354,12 @@ static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
|
|
|
|
static esp_err_t httpd_sys_get_handler(httpd_req_t *req)
|
|
static esp_err_t httpd_sys_get_handler(httpd_req_t *req)
|
|
{
|
|
{
|
|
|
|
+ httpd_print_request(req);
|
|
|
|
+
|
|
if (!strcmp(req->uri, "/sys/getconfig"))
|
|
if (!strcmp(req->uri, "/sys/getconfig"))
|
|
return httpd_get_config(req);
|
|
return httpd_get_config(req);
|
|
|
|
|
|
- return httpd_err_404(req);
|
|
|
|
|
|
+ return httpd_err_enoent(req);
|
|
}
|
|
}
|
|
|
|
|
|
INCBIN_EXTERN(wwwzip);
|
|
INCBIN_EXTERN(wwwzip);
|
|
@@ -346,7 +399,7 @@ static const struct mime_type mime_types[] = {
|
|
static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
{
|
|
{
|
|
static const char index_filename[] = "index.html";
|
|
static const char index_filename[] = "index.html";
|
|
- static const char default_lang[] = "en";
|
|
|
|
|
|
+ static const char fallback_lang[] = "en";
|
|
size_t buffer_size = UNZ_BUFSIZE;
|
|
size_t buffer_size = UNZ_BUFSIZE;
|
|
const char *uri, *enduri;
|
|
const char *uri, *enduri;
|
|
bool add_index;
|
|
bool add_index;
|
|
@@ -357,6 +410,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
int err = 0;
|
|
int err = 0;
|
|
size_t len;
|
|
size_t len;
|
|
|
|
|
|
|
|
+ httpd_print_request(req);
|
|
|
|
+
|
|
uri = req->uri;
|
|
uri = req->uri;
|
|
while (*uri == '/')
|
|
while (*uri == '/')
|
|
uri++; /* Skip leading slashes */
|
|
uri++; /* Skip leading slashes */
|
|
@@ -381,16 +436,10 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
add_index = false; /* Try the plain filename first */
|
|
add_index = false; /* Try the plain filename first */
|
|
}
|
|
}
|
|
|
|
|
|
- MSG("requesting: /%.*s\n", enduri - uri, uri);
|
|
|
|
- if (first_slash)
|
|
|
|
- MSG("first_slash = %.*s\n", enduri - first_slash, first_slash);
|
|
|
|
- else
|
|
|
|
- MSG("first_slash = NULL\n");
|
|
|
|
-
|
|
|
|
- const char *lang = getenv("LANG");
|
|
|
|
|
|
+ const char *lang = getenv_def("LANG", "");
|
|
const size_t lang_size = lang ? strlen(lang)+1 : 0;
|
|
const size_t lang_size = lang ? strlen(lang)+1 : 0;
|
|
- const size_t lang_space = (lang_size < sizeof default_lang
|
|
|
|
- ? sizeof default_lang : lang_size) + 1;
|
|
|
|
|
|
+ const size_t lang_space = (lang_size < sizeof fallback_lang
|
|
|
|
+ ? sizeof fallback_lang : lang_size) + 1;
|
|
const size_t filename_buffer_size =
|
|
const size_t filename_buffer_size =
|
|
(enduri - uri) + lang_space + 2 + sizeof index_filename;
|
|
(enduri - uri) + lang_space + 2 + sizeof index_filename;
|
|
|
|
|
|
@@ -400,7 +449,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
buffer = malloc(buffer_size);
|
|
buffer = malloc(buffer_size);
|
|
zip = malloc(sizeof *zip);
|
|
zip = malloc(sizeof *zip);
|
|
if (!buffer || !zip) {
|
|
if (!buffer || !zip) {
|
|
- err = HTTP_ERR(req, 503, "Out of memory");
|
|
|
|
|
|
+ err = httpd_err_enomem(req);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -440,8 +489,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 2:
|
|
case 2:
|
|
- filename = filebase - sizeof default_lang;
|
|
|
|
- memcpy(filename, default_lang, sizeof default_lang - 1);
|
|
|
|
|
|
+ filename = filebase - sizeof fallback_lang;
|
|
|
|
+ memcpy(filename, fallback_lang, sizeof fallback_lang - 1);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
filename = filebase;
|
|
filename = filebase;
|
|
@@ -462,10 +511,10 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
size_t filelen = endfile - filename;
|
|
size_t filelen = endfile - filename;
|
|
|
|
|
|
if (!found) {
|
|
if (!found) {
|
|
- err = httpd_err_404(req);
|
|
|
|
|
|
+ err = httpd_err_enoent(req);
|
|
goto out;
|
|
goto out;
|
|
} else if (m) {
|
|
} else if (m) {
|
|
- err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0);
|
|
|
|
|
|
+ err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0, 0);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -508,6 +557,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
len = snprintf(buffer, buffer_size-2,
|
|
len = snprintf(buffer, buffer_size-2,
|
|
"HTTP/1.1 %s\r\n"
|
|
"HTTP/1.1 %s\r\n"
|
|
"Date: %s\r\n"
|
|
"Date: %s\r\n"
|
|
|
|
+ "Cache-Control: max-age=10\r\n"
|
|
"ETag: %s\r\n",
|
|
"ETag: %s\r\n",
|
|
response,
|
|
response,
|
|
http_now(),
|
|
http_now(),
|
|
@@ -521,6 +571,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
"Content-Type: %s%s\r\n"
|
|
"Content-Type: %s%s\r\n"
|
|
"Content-Length: %u\r\n"
|
|
"Content-Length: %u\r\n"
|
|
"Allow: GET, HEAD\r\n"
|
|
"Allow: GET, HEAD\r\n"
|
|
|
|
+ "Connection: close\r\n"
|
|
"Last-Modified: %s\r\n",
|
|
"Last-Modified: %s\r\n",
|
|
mime_type->mime, mime_extra,
|
|
mime_type->mime, mime_extra,
|
|
fileinfo.uncompressed_size,
|
|
fileinfo.uncompressed_size,
|
|
@@ -645,6 +696,7 @@ static const httpd_uri_t uri_handlers[] = {
|
|
void my_httpd_stop(void)
|
|
void my_httpd_stop(void)
|
|
{
|
|
{
|
|
if (httpd) {
|
|
if (httpd) {
|
|
|
|
+ esp_unregister_shutdown_handler(my_httpd_stop);
|
|
httpd_stop(httpd);
|
|
httpd_stop(httpd);
|
|
httpd = NULL;
|
|
httpd = NULL;
|
|
}
|
|
}
|
|
@@ -670,6 +722,8 @@ void my_httpd_start(void)
|
|
if (httpd_start(&server, &config) != ESP_OK)
|
|
if (httpd_start(&server, &config) != ESP_OK)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ esp_register_shutdown_handler(my_httpd_stop);
|
|
|
|
+
|
|
httpd = server;
|
|
httpd = server;
|
|
for (size_t i = 0; i < ARRAY_SIZE(uri_handlers); i++) {
|
|
for (size_t i = 0; i < ARRAY_SIZE(uri_handlers); i++) {
|
|
const httpd_uri_t * const handler = &uri_handlers[i];
|
|
const httpd_uri_t * const handler = &uri_handlers[i];
|