lab4 webserver
This commit is contained in:
209
lab4/server.c
Normal file
209
lab4/server.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user