Skip to content

Commit

Permalink
fix: web server stack
Browse files Browse the repository at this point in the history
  • Loading branch information
Tasssadar committed Jul 9, 2024
1 parent 52c7db0 commit 8481b79
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 65 deletions.
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"maintainer": true
}
],
"version": "14.0.1",
"version": "14.0.2",
"frameworks": ["espidf", "arduino"],
"platforms": "espressif32"
}
123 changes: 65 additions & 58 deletions src/rbwebserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
#include "esp_spiffs.h"
#include "freertos/task.h"

#include "rbdns.h"
#include "rbwebserver.h"
#include "rbwebserver_internal.h"
#include "rbdns.h"

#include "mpaland-printf/printf.h"

#define TAG "RbWebServer"

Expand Down Expand Up @@ -53,29 +55,30 @@ static const mime_map meme_types[] = {

static const char* default_mime_type = "text/plain";

static void (*extra_path_callback)(const char *path, int out_fd) = NULL;
static void (*extra_path_callback)(const char* path, int out_fd) = NULL;
static not_found_response_t (*not_found_callback)(const char* request_path) = NULL;

static void *ws_protocol = NULL;
static const char *working_directory = "/notset";
static void* ws_protocol = NULL;
static const char* working_directory = "/notset";
static uint16_t web_server_stack_size = 3584;

void rb_web_set_extra_callback(void (*callback)(const char *path, int out_fd)) {
void rb_web_set_extra_callback(void (*callback)(const char* path, int out_fd)) {
extra_path_callback = callback;
}

void rb_web_set_not_found_callback(not_found_response_t (*callback)(const char* request_path)) {
not_found_callback = callback;
}

void rb_web_set_wsprotocol(void *wsProtocolInstance) {
if(ws_protocol != NULL && ws_protocol != wsProtocolInstance) {
void rb_web_set_wsprotocol(void* wsProtocolInstance) {
if (ws_protocol != NULL && ws_protocol != wsProtocolInstance) {
ESP_LOGE(TAG, "rb_web_set_wsprotocol was called twice with different instances!");
}
ws_protocol = wsProtocolInstance;
}

void rb_web_clear_wsprotocol(void *wsProtocolInstance) {
if(ws_protocol == wsProtocolInstance) {
void rb_web_clear_wsprotocol(void* wsProtocolInstance) {
if (ws_protocol == wsProtocolInstance) {
ws_protocol = NULL;
} else {
ESP_LOGE(TAG, "rb_web_clear_wsprotocol was called twice with different instance than rb_web_set_wsprotocol!");
Expand Down Expand Up @@ -122,7 +125,7 @@ static ssize_t rio_read(rio_t* rp, char* usrbuf, size_t n) {
rp->rio_cnt = recv(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf), MSG_DONTWAIT);
if (rp->rio_cnt < 0) {
if(errno == EAGAIN) {
if (errno == EAGAIN) {
vTaskDelay(pdMS_TO_TICKS(10));
} else if (errno != EINTR) /* interrupted by sig handler return */
return -1;
Expand Down Expand Up @@ -255,40 +258,42 @@ static int prepare_gzip(http_request* req) {
return open(req->filename, O_RDONLY);
}

static int is_local_host(const char *hostHeader) {
static int is_local_host(const char* hostHeader) {
const int hostHeaderLen = strlen(hostHeader) - 2; // ignore \r\n
if(hostHeaderLen < 0) {
if (hostHeaderLen < 0) {
return true;
}

if(hostHeaderLen >= 7 && hostHeaderLen <= 15) {
if (hostHeaderLen >= 7 && hostHeaderLen <= 15) {
int dots = 0;
bool is_ip = true;
for(int i = 0; i < hostHeaderLen; ++i) {
for (int i = 0; i < hostHeaderLen; ++i) {
char c = hostHeader[i];
if(c == '.') {
if (c == '.') {
++dots;
} else if(c < '0' || c > '9') {
} else if (c < '0' || c > '9') {
is_ip = false;
break;
}
}

if(is_ip && dots == 3) {
if (is_ip && dots == 3) {
return true;
}
}

const char *localHostname = rb_dn_get_local_hostname();
if(strlen(localHostname) == hostHeaderLen && memcmp(localHostname, hostHeader, hostHeaderLen) == 0) {
const char* localHostname = rb_dn_get_local_hostname();
if (strlen(localHostname) == hostHeaderLen && memcmp(localHostname, hostHeader, hostHeaderLen) == 0) {
return true;
}
return false;
}

static void parse_request(int fd, http_request* req) {
rio_t rio;
char buf[MAXLINE], method[10], uri[MAXLINE];
char buf[MAXLINE];
char uri[128];
char method[10];

uint8_t websocket_upgrade_headers = 0;

Expand All @@ -300,7 +305,7 @@ static void parse_request(int fd, http_request* req) {
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);

sscanf(buf, "%9s %255s", method, uri); /* version is not cared */
sscanf(buf, "%9s %127s", method, uri); /* version is not cared */
/* read all */
while (buf[0] != '\n' && buf[1] != '\n') { /* \n || \r\n */
rio_readlineb(&rio, buf, MAXLINE);
Expand All @@ -311,24 +316,24 @@ static void parse_request(int fd, http_request* req) {
req->end++;
} else if (strstr(buf, "Accept-Encoding: ") == buf && strstr(buf + 17, "gzip")) {
req->servingGzip = 1;
} else if(strstr(buf, "Upgrade: websocket") == buf) {
} else if (strstr(buf, "Upgrade: websocket") == buf) {
++websocket_upgrade_headers;
} else if(strstr(buf, "Connection: ") == buf && strstr(buf+12, "Upgrade")) {
} else if (strstr(buf, "Connection: ") == buf && strstr(buf + 12, "Upgrade")) {
++websocket_upgrade_headers;
}
// len(Sec-WebSocket-Key: ) + len(key) + \r\n
else if(strlen(buf) == 19 + 24 + 2 && strstr(buf, "Sec-WebSocket-Key: ") == buf) {
else if (strlen(buf) == 19 + 24 + 2 && strstr(buf, "Sec-WebSocket-Key: ") == buf) {
++websocket_upgrade_headers;
memcpy(req->ws_key, buf + 19, 24);
} else if(strstr(buf, "Sec-WebSocket-Version: ") == buf) {
} else if (strstr(buf, "Sec-WebSocket-Version: ") == buf) {
sscanf(buf + 23, "%ld", &req->ws_version);
} else if(strncmp(buf, "Host: ", 6) == 0) {
req->non_local_hostname = !is_local_host(buf+6);
} else if (strncmp(buf, "Host: ", 6) == 0) {
req->non_local_hostname = !is_local_host(buf + 6);
}
}

// Zero out the version if not all headers were received
if(req->ws_version != 0 && (websocket_upgrade_headers != 3 || strcmp(method, "GET") != 0)) {
if (req->ws_version != 0 && (websocket_upgrade_headers != 3 || strcmp(method, "GET") != 0)) {
req->ws_version = 0;
}

Expand Down Expand Up @@ -369,17 +374,16 @@ void client_error(int fd, int status, const char* msg, const char* longmsg) {
writen(fd, buf, strlen(buf));
}

#define REDIRECT_RESPONSE \
"HTTP/1.1 302 Temporary Redirect\r\n"\
#define REDIRECT_RESPONSE \
"HTTP/1.1 302 Temporary Redirect\r\n" \
"Location: http://"

static void temporary_redirect(int fd, const char *location) {
writen(fd, REDIRECT_RESPONSE, sizeof(REDIRECT_RESPONSE)-1);
static void temporary_redirect(int fd, const char* location) {
writen(fd, REDIRECT_RESPONSE, sizeof(REDIRECT_RESPONSE) - 1);
writen(fd, location, strlen(location));
writen(fd, "\r\n\r\n", 4);
}


static ssize_t sendfile(char* buf, const size_t bufsize, int out_fd, int in_fd, off_t* offset, size_t count) {
size_t chunk;
ssize_t res;
Expand Down Expand Up @@ -453,16 +457,15 @@ static void serve_static(int out_fd, int in_fd, http_request* req,
close(out_fd);
break;
}

}

static int serve_not_found_cb(int out_fd, http_request *req) {
if(!not_found_callback) {
static int serve_not_found_cb(int out_fd, http_request* req) {
if (!not_found_callback) {
return 0;
}

not_found_response_t nfr = not_found_callback(req->filename + strlen(working_directory));
if(!nfr.data || !nfr.size) {
if (!nfr.data || !nfr.size) {
return 0;
}

Expand All @@ -478,22 +481,23 @@ static int serve_not_found_cb(int out_fd, http_request *req) {
}

off_t pos = req->offset;
while(pos < req->end) {
while (pos < req->end) {
size_t chunk = req->end - pos;
if(chunk > 256) chunk = 256;
if (chunk > 256)
chunk = 256;
writen(out_fd, nfr.data + pos, chunk);
pos += chunk;
}

return 1;
}

static void process_serve_file(int fd, struct sockaddr_in* clientaddr, http_request *req) {
static void process_serve_file(int fd, struct sockaddr_in* clientaddr, http_request* req) {
struct stat sbuf;
int status = 200;
int ffd = prepare_gzip(req);
if (ffd <= 0) {
if(!serve_not_found_cb(fd, req)) {
if (!serve_not_found_cb(fd, req)) {
status = 404;
client_error(fd, status, "Not found", "File not found");
}
Expand Down Expand Up @@ -522,29 +526,28 @@ static int process(int fd, struct sockaddr_in* clientaddr) {

parse_request(fd, &req);

char *extra_itr;
char* extra_itr;

if(req.non_local_hostname) {
if (req.non_local_hostname) {
temporary_redirect(fd, rb_dn_get_local_hostname());
return 0;
} else if(req.ws_version != 0) {
if(ws_protocol == NULL) {
} else if (req.ws_version != 0) {
if (ws_protocol == NULL) {
client_error(fd, 400, "WS not enabled", "");
return 0;
}

if(rb_web_handle_websocket_switch_request(fd, &req) == 0) {
if (rb_web_handle_websocket_switch_request(fd, &req) == 0) {
return 1;
}
return 0;
} else if((extra_itr = strstr(req.filename, EXTRA_DIRECTORY_SUFFIX)) != NULL &&
(extra_itr - req.filename) == strlen(working_directory)) {
if(extra_path_callback == NULL) {
} else if ((extra_itr = strstr(req.filename, EXTRA_DIRECTORY_SUFFIX)) != NULL && (extra_itr - req.filename) == strlen(working_directory)) {
if (extra_path_callback == NULL) {
client_error(fd, 400, "Error", "No extra_path_callback specified.");
return 0;
}

extra_path_callback(extra_itr + sizeof(EXTRA_DIRECTORY_SUFFIX)-1, fd);
extra_path_callback(extra_itr + sizeof(EXTRA_DIRECTORY_SUFFIX) - 1, fd);
close(fd);
return 0;
} else {
Expand All @@ -558,12 +561,12 @@ static void tiny_web_task(void* portPtr) {

struct sockaddr_in clientaddr;
int listenfd, connfd;
socklen_t clientlen = sizeof clientaddr;
socklen_t clientlen = sizeof(clientaddr);
TaskHandle_t stopping_task = NULL;

listenfd = open_listenfd(port);
if (listenfd > 0) {
ESP_LOGI(TAG, "Listening on port %d", port);
ESP_LOGD(TAG, "Listening on port %d %d", port, listenfd);
} else {
ESP_LOGE(TAG, "failed to start: %s", strerror(errno));
goto fail;
Expand All @@ -585,7 +588,7 @@ static void tiny_web_task(void* portPtr) {
timeout.tv_usec = 0;
setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));

if(process(connfd, &clientaddr) <= 0) {
if (process(connfd, &clientaddr) <= 0) {
close(connfd);
} else {
rb_web_ws_new_connection(ws_protocol, connfd);
Expand All @@ -603,13 +606,13 @@ static void tiny_web_task(void* portPtr) {
xTaskNotifyWait(0, 0xFFFFFFFF, (uint32_t*)&stopping_task, portMAX_DELAY);

exit:
if(stopping_task)
if (stopping_task)
xTaskNotify(stopping_task, 0, eNoAction);
vTaskDelete(NULL);
}

TaskHandle_t rb_web_start(int port) {
if(!esp_spiffs_mounted(NULL)) {
if (!esp_spiffs_mounted(NULL)) {
esp_vfs_spiffs_conf_t conf = {
.base_path = "/data",
.partition_label = NULL,
Expand All @@ -633,10 +636,14 @@ TaskHandle_t rb_web_start(int port) {
return rb_web_start_no_spiffs(port, "/data");
}

TaskHandle_t rb_web_start_no_spiffs(int port, const char *working_directory_path) {
void rb_web_set_task_stack(uint16_t size) {
web_server_stack_size = size;
}

TaskHandle_t rb_web_start_no_spiffs(int port, const char* working_directory_path) {
TaskHandle_t task;
working_directory = working_directory_path;
xTaskCreate(&tiny_web_task, "rbctrl_web", 3072, (void*)port, 2, &task);
xTaskCreate(&tiny_web_task, "rbctrl_web", web_server_stack_size, (void*)port, 2, &task);
return task;
}

Expand Down Expand Up @@ -669,6 +676,6 @@ esp_err_t rb_web_add_file(const char* filename, const char* data, size_t len) {
return res;
}

const char *rb_web_get_files_root(void) {
const char* rb_web_get_files_root(void) {
return working_directory;
}
10 changes: 5 additions & 5 deletions src/rbwebserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ TaskHandle_t rb_web_start(int port);
/**
* \brief Start serving files without mounting the spiffs.
*/
TaskHandle_t rb_web_start_no_spiffs(int port, const char *working_directory_path);
TaskHandle_t rb_web_start_no_spiffs(int port, const char* working_directory_path);

/**
* \brief Stop http server. Pass in the task handle returned by rb_web_start.
Expand All @@ -32,27 +32,27 @@ esp_err_t rb_web_add_file(const char* filename, const char* data, size_t len);
/**
* \brief Returns path to root of files served by the web server. Without trailing /.
*/
const char *rb_web_get_files_root(void);
const char* rb_web_get_files_root(void);

/**
* Set a callback to call whenever URL in directory /extra/ is accessed.
*/
void rb_web_set_extra_callback(void (*callback)(const char* request_path, int out_fd));

typedef struct {
const uint8_t *data;
const uint8_t* data;
size_t size;
uint8_t is_gzipped;
} not_found_response_t;

/**
* Set a callback to call whenever a 404 would have been returned.
* Return zero-filled not_found_response_t to continue with 404, otherwise fill in the fields.
*
*
*/
void rb_web_set_not_found_callback(not_found_response_t (*callback)(const char* request_path));


void rb_web_set_task_stack(uint16_t size);

#ifdef __cplusplus
};
Expand Down
2 changes: 1 addition & 1 deletion src/rbwebserver_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
extern "C" {
#endif

#define LISTENQ 8 /* second argument to listen() */
#define LISTENQ 2 /* second argument to listen() */
#define MAXLINE 256 /* max length of a line */
#define RIO_BUFSIZE 256
#define FILENAME_SIZE 64
Expand Down

0 comments on commit 8481b79

Please sign in to comment.