/**
* copyright Copyright (c) 2009-2025 Shenzhen TP-LINK Technologies Co.Ltd.
*
* file mult_thread_web.c
* brief
*
* author An Ran <anran@tp-link.com.hk>
* version 1.0.0
* date 08Aug25
*
* history \arg, 1.0.0, 08Aug25, An Ran, Create the file
*/
/**************************************************************************************************/
/* INCLUDE FILES */
/**************************************************************************************************/
#include "mult_thread_web.h"
/**************************************************************************************************/
/* DEFINES */
/**************************************************************************************************/
/**************************************************************************************************/
/* TYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* EXTERN_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* VARIABLES */
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_FUNCTIONS */
/**************************************************************************************************/
/**
* 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
*/
static void send_headers(int client_socket, int status, const char *status_msg,
const char *content_type, long content_length)
{
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
*/
static 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),
"<html><head><title>%d %s</title></head>"
"<body><h1>%d %s</h1><p>%s</p></body></html>",
status, status_msg, status, status_msg, message);
send_headers(client_socket, status, status_msg, "text/html", length);
send(client_socket, response, length, 0);
}
// 获取文件MIME类型
/**
* 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),
"<html><head><title>Index of %s</title></head>"
"<body><h1>Index of %s</h1><ul>", 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,
"<li><a href=\"%s\">%s</a> - %s</li>",
entry->d_name, entry->d_name, size_str);
}
length += snprintf(response + length, sizeof(response) - length, "</ul></body></html>");
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 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 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)
{
return;
}
/* 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)
{
return;
}
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(void *argv)
* brief Handle HTTP requests
*
* param[in] void *argv Socket file descriptor
*
* note
*/
void *handle_request(void *argv)
{
/*
*buffer Stores the decoded string
*client_socket Socket descriptor
*bytes_read Received data length
*method Method type
*/
char buffer[BUFFER_SIZE];
int client_socket = *((int *)argv);
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)
{
close(client_socket);
return NULL;
}
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");
close(client_socket);
return NULL;
}
else if (1 == method )
{
/* Process POST requests */
handle_post_request(client_socket, buffer, bytes_read);
close(client_socket);
return NULL;
}
else if (2 == method)
{
/* Process GET requests */
handle_get_request(client_socket, buffer);
close(client_socket);
return NULL;
}
return NULL;
}
/**************************************************************************************************/
/* GLOBAL_FUNCTIONS */
/**************************************************************************************************/
int main()
{
/**
* server_socket The server listens to the socket
* opt Socket option value
* server_addr Server address information
*/
int server_socket = -1;
int opt = 1;
struct sockaddr_in server_addr;
/* 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");
/* Main loop: Accept and process connections */
while (1)
{
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int *client_socket = malloc(sizeof(int));
pthread_t tid;
if (!client_socket)
{
/* Legality check */
perror("malloc failed!");
free(client_socket);
continue;
}
*client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
if (0 > *client_socket)
{
perror("accept failed");
continue;
}
/* Create a new thread to process the request */
if (0 != pthread_create(&tid, NULL, handle_request, (void *)client_socket))
{
/* Legality check */
perror("pthread failed!");
close(*client_socket);
free(client_socket);
continue;
}
pthread_detach(tid);
}
close(server_socket);
return 0;
}以上代码哪里有问题
最新发布