lab4 webserver

This commit is contained in:
2025-11-02 13:37:19 +01:00
parent 81af17d42c
commit 895958d058
12 changed files with 470 additions and 0 deletions

209
lab4/server.c Normal file
View File

@@ -0,0 +1,209 @@
/*
Реализовать Web-Server использующий многопроцессный
способ обработки клиентов через fork(). Веб-сервер должен
работать в виде демона и обеспечивать базовую поддержку
протокола HTTP, на уровне метода GET (по желанию методы
HEAD и POST). Проверку Web-Serverа необходимо
осуществлять на статических текстовых и двоичных данных,
таких как изображения. Предусмотреть контроль и
журналирование ошибок (либо в файл, либо через syslog).
Обеспечить одновременную работу сервера с множественным
числом клиентов.
*/
#include "daemon.h"
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <unistd.h>
static int stop = 0;
static char www_root[512];
void signal_handler(int sig) {
switch (sig) {
case SIGTERM:
case SIGINT:
syslog(LOG_INFO, "Terminate signal catched. Stopping daemon...");
stop = 1;
break;
}
}
int create_server_socket(int port) {
struct sockaddr_in addr;
int listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0) {
syslog(LOG_ERR, "Unable to create server socket");
exit(1);
}
int opt = 1;
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
syslog(LOG_WARNING, "Unable to set SO_REUSEADDR");
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
syslog(LOG_ERR, "Unable to bind socket to port %d", port);
exit(1);
}
return listener;
}
const char *get_content_type(const char *path) {
const char *ext = strrchr(path, '.');
if (!ext)
return "application/octet-stream";
if (strcmp(ext, ".html") == 0)
return "text/html; charset=utf-8";
if (strcmp(ext, ".txt") == 0)
return "text/plain; charset=utf-8";
if (strcmp(ext, ".png") == 0)
return "image/png";
if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0)
return "image/jpeg";
if (strcmp(ext, ".css") == 0)
return "text/css";
if (strcmp(ext, ".js") == 0)
return "application/javascript";
return "application/octet-stream";
}
void send_file(int client_sock, const char *filepath) {
int fd = open(filepath, O_RDONLY);
if (fd < 0) {
/* Файл не найден - отправляем 404 */
const char *response = "HTTP/1.0 404 Not Found\r\n"
"Content-Type: text/html\r\n\r\n"
"<h1>404 Not Found</h1>";
write(client_sock, response, strlen(response));
syslog(LOG_INFO, "File not found: %s", filepath);
return;
}
/* Получаем размер файла */
struct stat st;
fstat(fd, &st);
/* Отправляем заголовки HTTP */
char header[256];
snprintf(header, sizeof(header),
"HTTP/1.0 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %ld\r\n\r\n",
get_content_type(filepath), st.st_size);
write(client_sock, header, strlen(header));
/* Отправляем содержимое файла */
char buf[4096];
int bytes;
while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
write(client_sock, buf, bytes);
}
close(fd);
syslog(LOG_INFO, "Sent file: %s", filepath);
}
void handle_client(int client_sock) {
char buf[1024];
int bytes_read = read(client_sock, buf, sizeof(buf) - 1);
if (bytes_read <= 0) {
syslog(LOG_ERR, "Unable to read from client");
return;
}
buf[bytes_read] = '\0';
/* Парсим первую строку: GET /path HTTP/1.x */
char method[16], path[256], protocol[16];
if (sscanf(buf, "%s %s %s", method, path, protocol) != 3) {
syslog(LOG_ERR, "Invalid HTTP request");
return;
}
/* Поддерживаем только GET */
if (strcmp(method, "GET") != 0) {
const char *response = "HTTP/1.0 501 Not Implemented\r\n\r\n";
write(client_sock, response, strlen(response));
syslog(LOG_INFO, "Unsupported method: %s", method);
return;
}
/* Формируем путь к файлу */
char filepath[512];
snprintf(filepath, sizeof(filepath), "%s", www_root);
/* Если запрашивают /, отдаём index.html */
if (strcmp(path, "/") == 0) {
strcat(filepath, "/index.html");
} else {
strcat(filepath, path);
}
syslog(LOG_INFO, "Request: %s %s -> %s", method, path, filepath);
/* Отправляем файл */
send_file(client_sock, filepath);
}
int server(const char *root, int port) {
snprintf(www_root, sizeof(www_root), "%s", root);
openlog("MY-WEB-SERVER", 0, LOG_DAEMON);
daemonize();
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
/* Автоматическая очистка завершенных процессов */
signal(SIGCHLD, SIG_IGN);
int client_sock;
int server_socket = create_server_socket(port);
listen(server_socket, 5);
syslog(LOG_INFO, "Server started on port %d, serving %s", port, www_root);
while (!stop) {
client_sock = accept(server_socket, NULL, NULL);
if (client_sock < 0) {
if (stop) {
break;
}
syslog(LOG_ERR, "Unable to accept client connection");
continue;
}
switch (fork()) {
case -1:
syslog(LOG_ERR, "Unable to fork");
close(client_sock);
continue;
case 0:
close(server_socket);
handle_client(client_sock);
close(client_sock);
exit(0);
default:
close(client_sock);
}
}
close(server_socket);
syslog(LOG_INFO, "Server stopped");
closelog();
return 0;
}