v0.4.8 - Implement web server functionality for embedded admin interface - serve HTML/CSS/JS from /api/ endpoint with proper MIME types, CORS headers, and performance optimizations
This commit is contained in:
165
src/api.c
Normal file
165
src/api.c
Normal file
@@ -0,0 +1,165 @@
|
||||
// Define _GNU_SOURCE to ensure all POSIX features are available
|
||||
#define _GNU_SOURCE
|
||||
|
||||
// API module for serving embedded web content
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <libwebsockets.h>
|
||||
#include "api.h"
|
||||
#include "embedded_web_content.h"
|
||||
|
||||
// Forward declarations for logging functions
|
||||
void log_info(const char* message);
|
||||
void log_success(const char* message);
|
||||
void log_error(const char* message);
|
||||
void log_warning(const char* message);
|
||||
|
||||
// Handle HTTP request for embedded files (assumes GET)
|
||||
int handle_embedded_file_request(struct lws* wsi, const char* requested_uri) {
|
||||
log_info("Handling embedded file request");
|
||||
|
||||
const char* file_path;
|
||||
|
||||
// Handle /api requests
|
||||
char temp_path[256];
|
||||
if (strcmp(requested_uri, "/api") == 0) {
|
||||
// /api -> serve index.html
|
||||
file_path = "/";
|
||||
} else if (strncmp(requested_uri, "/api/", 5) == 0) {
|
||||
// Extract file path from /api/ prefix and add leading slash for lookup
|
||||
snprintf(temp_path, sizeof(temp_path), "/%s", requested_uri + 5); // Add leading slash
|
||||
file_path = temp_path;
|
||||
} else {
|
||||
log_warning("Embedded file request without /api prefix");
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get embedded file
|
||||
embedded_file_t* file = get_embedded_file(file_path);
|
||||
if (!file) {
|
||||
log_warning("Embedded file not found");
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate session data
|
||||
struct embedded_file_session_data* session_data = malloc(sizeof(struct embedded_file_session_data));
|
||||
if (!session_data) {
|
||||
log_error("Failed to allocate embedded file session data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
session_data->type = 1; // Embedded file
|
||||
session_data->data = file->data;
|
||||
session_data->size = file->size;
|
||||
session_data->content_type = file->content_type;
|
||||
session_data->headers_sent = 0;
|
||||
session_data->body_sent = 0;
|
||||
|
||||
// Store session data
|
||||
lws_set_wsi_user(wsi, session_data);
|
||||
|
||||
// Prepare HTTP response headers
|
||||
unsigned char buf[LWS_PRE + 1024];
|
||||
unsigned char *p = &buf[LWS_PRE];
|
||||
unsigned char *start = p;
|
||||
unsigned char *end = &buf[sizeof(buf) - 1];
|
||||
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)file->content_type, strlen(file->content_type), &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_add_http_header_content_length(wsi, file->size, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add CORS headers (same as NIP-11 for consistency)
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:", (unsigned char*)"*", 1, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-headers:", (unsigned char*)"content-type, accept", 20, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-methods:", (unsigned char*)"GET, OPTIONS", 12, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add Connection: close to ensure connection closes after response
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"connection:", (unsigned char*)"close", 5, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end)) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write headers
|
||||
if (lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS) < 0) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
session_data->headers_sent = 1;
|
||||
|
||||
// Request callback for body transmission
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
log_success("Embedded file headers sent, body transmission scheduled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle HTTP_WRITEABLE for embedded files
|
||||
int handle_embedded_file_writeable(struct lws* wsi) {
|
||||
struct embedded_file_session_data* session_data = (struct embedded_file_session_data*)lws_wsi_user(wsi);
|
||||
if (!session_data || session_data->headers_sent == 0 || session_data->body_sent == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate buffer for data transmission
|
||||
unsigned char *buf = malloc(LWS_PRE + session_data->size);
|
||||
if (!buf) {
|
||||
log_error("Failed to allocate buffer for embedded file transmission");
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy data to buffer
|
||||
memcpy(buf + LWS_PRE, session_data->data, session_data->size);
|
||||
|
||||
// Write data
|
||||
int write_result = lws_write(wsi, buf + LWS_PRE, session_data->size, LWS_WRITE_HTTP);
|
||||
|
||||
// Free the transmission buffer
|
||||
free(buf);
|
||||
|
||||
if (write_result < 0) {
|
||||
log_error("Failed to write embedded file data");
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Mark as sent and clean up
|
||||
session_data->body_sent = 1;
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
|
||||
log_success("Embedded file served successfully");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user