Files
unix/lab4/server.c
2025-11-02 13:37:19 +01:00

210 lines
5.9 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Реализовать 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;
}