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

2
lab4/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.o
webserver

12
lab4/Makefile Normal file
View File

@@ -0,0 +1,12 @@
CC = gcc
CFLAGS = -Wall -Wextra -O2
OBJ = main.o server.o daemon.o
webserver: $(OBJ)
$(CC) $(CFLAGS) -o $@ $(OBJ)
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f *.o webserver

49
lab4/daemon.c Normal file
View File

@@ -0,0 +1,49 @@
/* Вспомогательные функции для демонизации вебсервера */
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
pid_t pid, sid;
void daemonize() {
syslog(LOG_DEBUG, " --- STARTING DAEMON --- ");
/* Закрываем стандартные файловые дескприторы */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
pid = fork();
if (pid < 0) {
syslog(LOG_CRIT, "Unable to fork process!");
exit(EXIT_FAILURE);
}
/* Если fork получился, то родительский процесс можно завершить */
if (pid > 0) {
syslog(LOG_DEBUG, "Killed parent process");
exit(EXIT_SUCCESS);
}
umask(0);
/* Sid для дочернего процесса */
sid = setsid();
if (sid < 0) {
syslog(LOG_CRIT, "Unable to set session id");
exit(EXIT_FAILURE);
}
/* Изменяем текущий рабочий каталог */
if ((chdir("/")) < 0) {
syslog(LOG_CRIT, "Unable to change working directory");
exit(EXIT_FAILURE);
}
}

6
lab4/daemon.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef DAEMON_H
#define DAEMON_H
void daemonize();
#endif

56
lab4/main.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* Простой многопроцессный HTTP сервер с использованием fork()
*
* Компиляция: make
*
* Запуск: ./webserver <путь_к_папке> [порт]
* Примеры:
* ./webserver ./test-site
* ./webserver ./test-site 8080
*
* Остановка: pkill webserver
*
* Просмотр логов:
* sudo journalctl -f -t MY-WEB-SERVER
* sudo journalctl -f -t MY-WEB-SERVER --since "now" -p info
*
* Просмотр процессов:
* pstree -p $(pgrep -o webserver)
* ps aux | grep webserver
*/
#include "server.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <www_root> [port]\n", argv[0]);
fprintf(stderr, "Example: %s ./test-site 13131\n", argv[0]);
return 1;
}
/* Преобразуем путь в абсолютный */
char abs_path[PATH_MAX];
if (realpath(argv[1], abs_path) == NULL) {
perror("Invalid path");
return 1;
}
int port = 13131;
if (argc > 2) {
port = atoi(argv[2]);
if (port <= 0 || port > 65535) {
fprintf(stderr, "Invalid port: %s\n", argv[2]);
return 1;
}
}
printf("Starting web server on port %d, serving %s...\n", port, abs_path);
server(abs_path, port);
return 0;
}

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;
}

7
lab4/server.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef SERVER_H
#define SERVER_H
int server(const char *www_root, int port);
#endif

17
lab4/test-site/data.txt Normal file
View File

@@ -0,0 +1,17 @@
Это тестовый текстовый файл.
Сервер должен корректно отдавать как текстовые, так и бинарные файлы.
Особенности реализации:
- Многопроцессная архитектура через fork()
- Демонизация процесса
- Поддержка HTTP/1.0 GET запросов
- Журналирование через syslog
- Обработка ошибок (404, 501)
Строки текста для проверки корректности передачи:
1234567890
ABCDEFGHIJKLMNOPQRSTUVWXYZ
абвгдежзийклмнопрстуфхцчшщъыьэюя
Special chars: !@#$%^&*()_+-=[]{}|;':",.<>?/

38
lab4/test-site/index.html Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Тестовый веб-сервер</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="content">
<h1>🚀 Веб-сервер работает!</h1>
<p>Это тестовая страница простого HTTP сервера на C с использованием fork().</p>
<h2>Особенности:</h2>
<ul>
<li>Многопроцессная обработка клиентов</li>
<li>Работа в режиме демона</li>
<li>Поддержка HTTP GET</li>
<li>Статические файлы (HTML, текст, изображения)</li>
<li>Журналирование через syslog</li>
</ul>
<h2>Тестовые ссылки:</h2>
<ul>
<li><a href="/test.html">Тестовая страница</a></li>
<li><a href="/logo.png">Изображение (PNG)</a></li>
<li><a href="/data.txt">Текстовый файл</a></li>
<li><a href="/style.css">CSS файл</a></li>
</ul>
<h2>Изображение:</h2>
<img src="/logo.png" alt="Test Image">
</div>
</body>
</html>

BIN
lab4/test-site/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

35
lab4/test-site/style.css Normal file
View File

@@ -0,0 +1,35 @@
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
h1 {
color: #333;
}
.content {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
a {
color: #1a73e8;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
ul {
line-height: 1.8;
}
img {
margin: 20px 0;
}

39
lab4/test-site/test.html Normal file
View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Тестовая страница</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.box {
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
a {
color: #ffd700;
}
</style>
</head>
<body>
<div class="box">
<h1>✨ Тестовая страница</h1>
<p>Если вы видите эту страницу, значит сервер корректно обрабатывает HTTP запросы!</p>
<p>Сервер работает на порту <strong>13131</strong>.</p>
<p><a href="/">← Вернуться на главную</a></p>
</div>
</body>
</html>