Skip to content

Commit

Permalink
feat: add webserver not found callback
Browse files Browse the repository at this point in the history
  • Loading branch information
Tasssadar committed Jun 25, 2024
1 parent 0c1d80c commit 2f63e2c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 14 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": "13.1.0",
"version": "13.2.0",
"frameworks": ["espidf", "arduino"],
"platforms": "espressif32"
}
84 changes: 71 additions & 13 deletions src/rbwebserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@

#define TAG "RbWebServer"

#define WORKING_DIRECTORY "/spiffs"

#define EXTRA_DIRECTORY WORKING_DIRECTORY "/extra/"
#define EXTRA_DIRECTORY_SUFFIX "/extra/"

typedef struct {
int rio_fd; /* descriptor for this buf */
Expand Down Expand Up @@ -56,13 +54,19 @@ 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 not_found_response_t (*not_found_callback)(const char* request_path) = NULL;

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

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) {
ESP_LOGE(TAG, "rb_web_set_wsprotocol was called twice with different instances!");
Expand Down Expand Up @@ -208,7 +212,7 @@ static int open_listenfd(int port) {
static void url_decode(char* src, char* dest, int max) {
char* p = src;

int prefix_len = snprintf(dest, FILENAME_SIZE, "%s/", WORKING_DIRECTORY);
int prefix_len = snprintf(dest, FILENAME_SIZE, "%s/", working_directory);
dest += prefix_len;
max -= prefix_len;

Expand Down Expand Up @@ -406,8 +410,7 @@ static ssize_t sendfile(char* buf, const size_t bufsize, int out_fd, int in_fd,
return res;
}

static void serve_static(int out_fd, int in_fd, http_request* req,
size_t total_size) {
static void serve_headers_success(int out_fd, http_request* req, size_t total_size) {
char buf[256];
int buf_len = 0;
if (req->offset > 0) {
Expand All @@ -432,6 +435,15 @@ static void serve_static(int out_fd, int in_fd, http_request* req,
buf_len += snprintf(buf + buf_len, sizeof(buf) - buf_len - 1, "Content-Type: %s\r\n\r\n", get_mime_type(req->filename));

writen(out_fd, buf, strlen(buf));
}

static void serve_static(int out_fd, int in_fd, http_request* req,
size_t total_size) {

serve_headers_success(out_fd, req, total_size);

char buf[256];

off_t offset = req->offset; /* copy */
while (offset < req->end) {
if (sendfile(buf, sizeof(buf), out_fd, in_fd, &offset, req->end - req->offset) <= 0) {
Expand All @@ -444,13 +456,47 @@ static void serve_static(int out_fd, int in_fd, http_request* req,

}

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) {
return 0;
}

req->servingGzip = nfr.is_gzipped ? 1 : 0;
if (req->end == 0 || req->end > nfr.size) {
req->end = nfr.size;
}

serve_headers_success(out_fd, req, nfr.size);

if (req->offset >= nfr.size) {
return 1;
}

off_t pos = req->offset;
while(pos < req->end) {
size_t chunk = req->end - pos;
if(chunk > 512) chunk = 512;
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) {
struct stat sbuf;
int status = 200;
int ffd = prepare_gzip(req);
if (ffd <= 0) {
status = 404;
client_error(fd, status, "Not found", "File not found");
if(!serve_not_found_cb(fd, req)) {
status = 404;
client_error(fd, status, "Not found", "File not found");
}
} else {
fstat(ffd, &sbuf);
if (S_ISREG(sbuf.st_mode)) {
Expand All @@ -476,6 +522,8 @@ static int process(int fd, struct sockaddr_in* clientaddr) {

parse_request(fd, &req);

char *extra_itr;

if(req.non_local_hostname) {
temporary_redirect(fd, rb_dn_get_local_hostname());
return 0;
Expand All @@ -489,13 +537,14 @@ static int process(int fd, struct sockaddr_in* clientaddr) {
return 1;
}
return 0;
} else if(strncmp(req.filename, EXTRA_DIRECTORY, sizeof(EXTRA_DIRECTORY)-1) == 0) {
} 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(req.filename + sizeof(EXTRA_DIRECTORY)-1, fd);
extra_path_callback(extra_itr + sizeof(EXTRA_DIRECTORY_SUFFIX)-1, fd);
close(fd);
return 0;
} else {
Expand Down Expand Up @@ -556,7 +605,7 @@ static void tiny_web_task(void* portPtr) {
TaskHandle_t rb_web_start(int port) {
if(!esp_spiffs_mounted(NULL)) {
esp_vfs_spiffs_conf_t conf = {
.base_path = WORKING_DIRECTORY,
.base_path = "/data",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
Expand All @@ -575,22 +624,31 @@ 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) {
TaskHandle_t task;
working_directory = working_directory_path;
xTaskCreate(&tiny_web_task, "rbctrl_web", 3072, (void*)port, 2, &task);
return task;
}

void rb_web_stop(TaskHandle_t web_task) {
xTaskNotify(web_task, (uint32_t)xTaskGetCurrentTaskHandle(), eSetValueWithOverwrite);
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);

working_directory = "/notset";
extra_path_callback = NULL;
not_found_callback = NULL;
}

esp_err_t rb_web_add_file(const char* filename, const char* data, size_t len) {
char buf[256];
int fd;
esp_err_t res = ESP_OK;

snprintf(buf, sizeof(buf), "%s/%s", WORKING_DIRECTORY, filename);
snprintf(buf, sizeof(buf), "%s/%s", working_directory, filename);

fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
Expand All @@ -606,5 +664,5 @@ esp_err_t rb_web_add_file(const char* filename, const char* data, size_t len) {
}

const char *rb_web_get_files_root(void) {
return WORKING_DIRECTORY;
return working_directory;
}
20 changes: 20 additions & 0 deletions src/rbwebserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ extern "C" {
*/
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);

/**
* \brief Stop http server. Pass in the task handle returned by rb_web_start.
*/
Expand All @@ -34,6 +39,21 @@ const char *rb_web_get_files_root(void);
*/
void rb_web_set_extra_callback(void (*callback)(const char* request_path, int out_fd));

typedef struct {
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));



#ifdef __cplusplus
};
#endif

0 comments on commit 2f63e2c

Please sign in to comment.