/**
copyright Copyright © 2009-2025 Shenzhen TP-LINK Technologies Co.Ltd.
file mult_plex_web.h
brief Multi-plex implementation of httpd
author An Ran anran@tp-link.com.hk
version 1.0.0
date 14Aug25
*/
//
/* INCLUDE FILES */
//
#include “mult_plex_web.h”
//
/* DEFINES */
//
//
/* TYPES */
//
//
/* EXTERN_PROTOTYPES */
//
//
/* LOCAL_PROTOTYPES */
//
//
/* VARIABLES */
//
//
/* LOCAL_FUNCTIONS */
//
/**
fn static int check_http_method(const char *buffer)
brief Check if the HTTP request method is supported
param[in] const char *buffer
return 0 Other method, 1 Get method, 2 Post method
note
*/
static int check_http_method(const char buffer)
{
if (0 == strncmp(buffer, "GET ", 4))
{
/ Get method /
return 1;
}
else if (0 == strncmp(buffer, "POST ", 5))
{
/ Post method /
return 2;
}
else
{
/ Other method */
return 0;
}
}
/**
fn static void send_headers(int client_socket, int status, const char *status_msg,
const char *content_type, long content_length)
brief Send HTTP response headers
param[in] client_socket Socket descriptor
status HTTP status code
status_msg The text description corresponding to the status code
content_type The MIME type of the response content
content_length The length of the bytes in the response body
note
*/
void send_headers(int client_socket, int status, const char *status_msg,
const char content_type, long content_length)
{
/ Buffer */
char headers[BUFFER_SIZE];
snprintf(headers, sizeof(headers),
“HTTP/1.1 %d %s\r\n”
“Server: Simple-C-WebServer\r\n”
“Content-Type: %s\r\n”
“Content-Length: %ld\r\n”
“Connection: close\r\n”
“\r\n”,
status, status_msg, content_type, content_length);
send(client_socket, headers, strlen(headers), 0);
}
/**
fn static void send_error(int client_socket, int status,
const char *status_msg, const char *message)
brief Send an error response
param[in] client_socket Socket descriptor
status HTTP status code
status_msg The text description corresponding to the status code
content_type Customize error details
note
*/
void send_error(int client_socket, int status, const char *status_msg, const char message)
{
/ Buffer */
char response[BUFFER_SIZE];
/* Records the number of bytes that have been written to the buffer at the moment*/
int length = 0;
length = snprintf(response, sizeof(response),
“%d %s”
“
%d %s
%s
”,
status, status_msg, status, status_msg, message);
send_headers(client_socket, status, status_msg, “text/html”, length);
send(client_socket, response, length, 0);
}
/**
fn static const char *get_mime_type(const char *filename)
brief Get the MIME type of the file
param[in] filename Full file name or file path
note
*/
static const char *get_mime_type(const char filename)
{
/ A pointer to the starting position of the extension in the file name */
const char *ext = strrchr(filename, ‘.’);
if (!ext)
{
return “application/octet-stream”;
}
if (strcmp(ext, “.html”) == 0 || strcmp(ext, “.htm”) == 0)
{
return “text/html”;
}
if (strcmp(ext, “.txt”) == 0)
{
return “text/plain”;
}
if (strcmp(ext, “.css”) == 0)
{
return “text/css”;
}
if (strcmp(ext, “.js”) == 0)
{
return “application/javascript”;
}
if (strcmp(ext, “.jpg”) == 0 || strcmp(ext, “.jpeg”) == 0)
{
return “image/jpeg”;
}
if (strcmp(ext, “.png”) == 0)
{
return “image/png”;
}
if (strcmp(ext, “.gif”) == 0)
{
return “image/gif”;
}
if (strcmp(ext, “.pdf”) == 0)
{
return “application/pdf”;
}
if (strcmp(ext, “.zip”) == 0)
{
return “application/zip”;
}
return “application/octet-stream”;
}
/**
fn static void send_file(int client_socket, const char *filename)
brief Send the file content
param[in] client_socket Socket descriptor
filename The requested file path
note
/
static void send_file(int client_socket, const char filename)
{
/
file_stat Store file metadata
dir A directory flow pointer that is used to iterate \
through the contents of the catalog
response HTML response buffer
entry Catalog entry information
length Records the number of bytes that have been \
written to the buffer at the moment
path Temporarily stores the full path to the directory entry
entry_stat Stores the metadata of individual files within a directory
size_str File size formatting string
fd File descriptor
buffer File read/write buffer
bytes_read Records the number of bytes per read() read
*/
struct stat file_stat = {0};
struct stat entry_stat = {0};
char response[BUFFER_SIZE * 4];
char path[PATH_MAX] = {0};
char buffer[BUFFER_SIZE];
char size_str[20] = {0};
int length = 0;
int fd = -1;
ssize_t bytes_read = 0;
DIR *dir = NULL;
const char *mime_type = NULL;
struct dirent *entry = NULL;
if (0 > stat(filename, &file_stat))
{
send_error(client_socket, 404, “Not Found”, “File not found”);
return;
}
if (S_ISDIR(file_stat.st_mode))
{
/* If it’s a catalog, list the contents of the catalog */
dir = opendir(filename);
if (!dir)
{
send_error(client_socket, 403, “Forbidden”, “Cannot list directory”);
return;
}
length = snprintf(response, sizeof(response), “Index of %s” “
Index of %s
”, filename, filename); while (NULL != (entry = readdir(dir))) { /* Skip hidden files and current/parent directories */ if (‘.’ == entry->d_name[0]) { continue; } snprintf(path, sizeof(path), “%s/%s”, filename, entry->d_name); stat(path, &entry_stat); if (S_ISDIR(entry_stat.st_mode)) { strcpy(size_str, “<DIR>”); } else { snprintf(size_str, sizeof(size_str), “%ld bytes”, entry_stat.st_size); } length += snprintf(response + length, sizeof(response) - length, “
<a href="%s">%s - %s”, entry->d_name, entry->d_name, size_str); } length += snprintf(response + length, sizeof(response) - length, “”); closedir(dir); send_headers(client_socket, 200, “OK”, “text/html”, length); send(client_socket, response, length, 0); return;
}
/* Send the file content */
fd = open(filename, O_RDONLY);
if (0 > fd)
{
send_error(client_socket, 403, “Forbidden”, “Cannot open file”);
return;
}
/* Send HTTP headers */
mime_type = get_mime_type(filename);
send_headers(client_socket, 200, “OK”, mime_type, file_stat.st_size);
/* Send the file content */
while (0 < (bytes_read = read(fd, buffer, sizeof(buffer))))
{
send(client_socket, buffer, bytes_read, 0);
}
close(fd);
}
/**
fn static void url_decode(const char *src, char *dest, size_t dest_size)
brief URL decoding
param[in] src Temporarily store the original path
dest Stores the decoded string
dest_size Store the space size of the decoded string
param[out] dest Stores the decoded string
note
*/
static void url_decode(const char *src, char dest, size_t dest_size)
{
/ Space maximum */
char *end = dest + dest_size - 1;
while (*src && dest < end)
{
if (*src == ‘%’ && isxdigit(src[1]) && isxdigit(src[2]))
{
char hex[3] = {src[1], src[2], ‘\0’};
*dest++ = (char)strtol(hex, NULL, 16);
src += 3;
}
else if (*src == ‘+’)
{
*dest++ = ’ ';
src++;
}
else
{
*dest++ = *src++;
}
}
*dest = ‘\0’;
}
/**
fn static int parse_request_path(const char *buffer,
char *decoded_path, size_t decoded_size)
brief Check the path format and URL process
param[in] buffer Received data buffer
decoded_path Stores the decoded string
decoded_size Store the space size of the decoded string
note
*/
static int parse_request_path(const char *buffer, char decoded_path, size_t decoded_size)
{
/ Temporarily store the original path and path length */
char raw_path[BUFFER_SIZE];
size_t path_len;
/* Skip the "GET " */
const char *path_start = buffer + 4;
/* Check if the path in the HTTP header is formatted correctly */
const char *path_end = strchr(path_start, ’ ');
if (!path_end)
{
return -1;
}
/* Temporarily store the original path */
path_len = path_end - path_start;
if (path_len >= sizeof(raw_path))
{
return -1;
}
memcpy(raw_path, path_start, path_len);
raw_path[path_len] = ‘\0’;
/* URL decoding */
url_decode(raw_path, decoded_path, decoded_size);
return 0;
}
/**
fn static int build_safe_path(const char *root_dir, const char *decoded_path,
char *full_path, size_t full_path_size)
brief Build a secure output directory
param[in] root_dir Root path
decoded_path Stores the decoded string
full_path The full path of the output
full_path_size Buffer size
note
*/
static int build_safe_path(const char *root_dir, const char *decoded_path,
char full_path, size_t full_path_size)
{
/ Prevent directory traversal attacks */
if (strstr(decoded_path, “…”))
{
return -1;
}
snprintf(full_path, full_path_size, “%s%s”, root_dir, decoded_path);
/* Automatically add Index.html */
if (full_path[strlen(full_path) - 1] == ‘/’)
{
strncat(full_path, “Index.html”, full_path_size - strlen(full_path) - 1);
}
return 0;
}
/**
fn static void handle_get_request(int client_socket, char *buffer)
brief Process GET requests
param[in] client_socket client socket
buffer Received data buffer
note
*/
static void handle_get_request(int client_socket, char buffer)
{
/
*decoded_path Stores the decoded string
*full_path The full path of the output
*/
char decoded_path[BUFFER_SIZE];
char full_path[BUFFER_SIZE];
/* URL process */
if (0 != parse_request_path(buffer, decoded_path, sizeof(decoded_path)))
{
send_error(client_socket, 400, “Bad Request”, “Invalid request”);
return;
}
/* Build a safe path */
if (0 != build_safe_path(ROOT_DIR, decoded_path, full_path, sizeof(full_path)))
{
send_error(client_socket, 403, “Forbidden”, “Directory traversal not allowed”);
return;
}
/* Send files */
printf(“Request: %s -> %s\n”, decoded_path, full_path);
send_file(client_socket, full_path);
return;
}
/**
fn static void handle_post_request(int client_socket, char *buffer, ssize_t bytes_read)
brief Process POST requests
param[in] client_socket client socket
buffer Received data buffer
bytes_read Received data length
note
*/
static void handle_post_request(int client_socket, char *buffer, ssize_t bytes_read)
{
/**
content_length_start Look for the “Content-Length” field in the HTTP response header
body_start Look for the “Message body” field in the HTTP response header
post_data Stored the post data
response Respond to messages
total_read copy the length of the data has been read
content_length Expected message body length
body_len Actual message body length
n The size of the bytes read
*/
char *content_length_start = NULL;
char *body_start = NULL;
char *post_data = NULL;
const char *response = NULL;
int total_read = 0;
int content_length = 0;
int body_len = 0;
int n = 0;
/* get Content-Length */
content_length_start = strstr(buffer, "Content-Length: ");
if (!content_length_start)
{
perror(“get Content-Length failed”);
exit(EXIT_FAILURE);
}
/* skip "Content-Length: "*/
content_length_start += 16;
content_length = atoi(content_length_start);
/* read POST data */
post_data = malloc(content_length + 1);
if (!post_data)
{
perror(“get POST data failed”);
exit(EXIT_FAILURE);
}
body_start = strstr(buffer, “\r\n\r\n”);
/* Copy the data in the buffer to your local location */
if (body_start)
{
body_start += 4;
body_len = bytes_read - (body_start - buffer);
memcpy(post_data, body_start, body_len);
total_read = body_len;
}
/* Read the remaining data */
while (total_read < content_length)
{
n = recv(client_socket, post_data + total_read,
content_length - total_read, 0);
if (n <= 0)
{
break;
}
total_read += n;
}
post_data[total_read] = ‘\0’;
/* Print the POST data */
printf(“Received POST data: %s\n”, post_data);
/* Send the response */
response = “{“callback”:“congratulations!”}”;
send_headers(client_socket, 200, “OK”, “application/json”, strlen(response));
send(client_socket, response, strlen(response), 0);
free(post_data);
}
//
/* PUBLIC_FUNCTIONS /
//
/*
fn void handle_request(int client_socket)
brief Handle HTTP requests
param[in] int client_socket Socket file descriptor
note
/
void handle_request(int client_socket)
{
/
*buffer Stores the decoded string
*bytes_read Received data length
*method Method type
*/
char buffer[BUFFER_SIZE];
int method = 0;
ssize_t bytes_read = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
/* Receive data fails to be handled */
if (0 >= bytes_read)
{
return;
}
buffer[bytes_read] = ‘\0’;
/* Check the HTTP method /
method = check_http_method(buffer);
if (0 == method)
{
send_error(client_socket, 501, “Not Implemented”, “Only GET and POST methods are supported”);
return;
}
else if (1 == method )
{
/ Process GET requests /
handle_get_request(client_socket, buffer);
return;
}
else if (2 == method)
{
/ Process POST requests */
handle_post_request(client_socket, buffer, bytes_read);
return;
}
}
//
/* GLOBAL_FUNCTIONS */
//
int main()
{
/**
server_socket The server listens to the socket
opt Socket option value
server_addr Server address information
max_ser_socket
*/
int server_socket = -1;
int opt = 1;
struct sockaddr_in server_addr;
int max_ser_socket = -1; fd_set rdset, rdtmp; FD_ZERO(&rdset); /* Create a server socket / server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket < 0) { perror(“socket creation failed”); exit(EXIT_FAILURE); } / Set SO_REUSEADDR options / if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror(“setsockopt failed”); close(server_socket); exit(EXIT_FAILURE); } / Bind addresses and ports / memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); if (0 > bind(server_socket, (struct sockaddr )&server_addr, sizeof(server_addr)) ) { perror(“bind failed”); close(server_socket); exit(EXIT_FAILURE); } / Start listening / if (0 > listen(server_socket, BACKLOG)) { perror(“listen failed”); close(server_socket); exit(EXIT_FAILURE); } printf(“Server running on port %d, serving files from %s\n”, PORT, ROOT_DIR); printf(“Open http://localhost in your browser\n”); max_ser_socket = server_socket; FD_SET(server_socket, &rdset); / Main loop: Accept and process connections / while (1) { / * client_addr Stores the client’s address information * client_addr_len Used to store the length of client_addr structure * client_socket Store the socket descriptor of the client * fd Traverse the file descriptor collection / int fd = 0; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int client_socket = -1; / A copy of rdset (a collection of read file descriptors). / rdtmp = rdset; / Call select listens for file descriptors in the rdtmp collection / select(max_ser_socket + 1, &rdtmp, NULL, NULL, NULL); / Loop through each file descriptor from 0 to max_ser_socket / for (; fd < max_ser_socket + 1; fd++) { / There is a reading event / if (FD_ISSET(fd, &rdtmp)) { / New connections / if (fd == server_socket) { / Accept the connection */ client_socket = accept(server_socket, (struct sockaddr )&client_addr, &client_addr_len); if (0 > client_socket) { perror(“accept failed!”); close(client_socket); continue; } / Add the new client socket and update the max_ser_socket / FD_SET(client_socket, &rdset); max_ser_socket = client_socket > max_ser_socket ? client_socket : max_ser_socket; } else { / Connected clients */ handle_request(fd); close(fd); FD_CLR(fd, &rdset); } } } } close(server_socket); return 0;
}给我生成一个以上代码的设计流程图,要抽象出来实现的功能方法和目的