Программирование для Linux. Профессиональный подход - Марк Митчелл
Шрифт:
Интервал:
Закладка:
" </body>n"
"</html>n";
/* HTTP-ответ, заголовок и шаблон страницы на случай,
когда запрашиваемый документ не найден. */
static char* not_found_response_template =
"HTTP/1.0 404 Not Foundn"
"Content-type: text/htmln"
"n"
"<html>n"
" <body>n"
" <h1>Not Found</h1>n"
" <p>The requested URL %s was not found on this server.</p>n"
" </body>n"
"</html>n";
/* HTTP-ответ, заголовок к шаблон страницы на случай,
когда запрашивается непонятный метод */
static char* bad_method_response_template =
"HTTP/1.0 501 Method Not Implementedn"
"Content-type: text/htmln"
"n"
"<html>n"
" <body>n"
" <h1>Method Not Implemented</h1>n"
" <p>The method %s is not implemented by this server.</p>n"
" </body>n"
"</html>n";
/* Обработчик сигнала SIGCHLD, удаляющий завершившиеся
дочерние процессы. */
static void clean_up_child_process(int signal_number) {
int status;
wait(&status);
}
/* Обработка HTTP-запроса "GET" к странице PAGE и
запись результата в файл с дескриптором CONNECTION_FD. */
static void handle_get(int connection_fd, const char* page) {
struct server_module* module = NULL;
/* Убеждаемся, что имя страницы начинается с косой черты и
не содержит других символов косой черты, так как
подкаталоги не поддерживаются. */
if (*page == '/' && strchr(page + 1, '/') == NULL) {
char module_file_name[64];
/* Имя страницы правильно. Формируем имя модуля, добавляя
расширение ".so" к имени страницы. */
snprintf(module_file_name, sizeof(module_file_name),
"%s.so", page + 1);
/* Попытка открытия модуля. */
module = module_open(module_file_name);
}
if (module == NULL) {
/* Имя страницы неправильно сформировано или не удалось
открыть модуль с указанным именем. В любом случае
возвращается HTTP-ответ "404. Not Found". */
char response[1024];
/* Формирование ответного сообщения. */
snprintf(response, sizeof(response),
not_found_response_template, page);
/* Отправка его клиенту. */
write(connection_fd, response, strlen(response));
} else {
/* Запрашиваемый модуль успешно загружен. */
/* Выдача HTTP-ответа, обозначающего успешную обработку
запроса, и HTTP-заголовка для HTML-страницы. */
write(connection_fd, ok_response, strlen(ok_response));
/* Вызов модуля, генерирующего HTML-код страницы и
записывающего этот код в указанный файл. */
(*module->generate_function)(connection_fd);
/* Работа с модулем окончена. */
module_close(module);
}
}
/* Обработка клиентского запроса на подключение. */
static void handle_connection(int connection_fd) {
char buffer[256];
ssize_t bytes_read;
/* Получение данных от клиента. */
bytes_read =
read(connection_fd, buffer, sizeof(buffer) — 1);
if (bytes_read > 0) {
char method[sizeof(buffer)];
char url[sizeof(buffer)];
char protocol[sizeof(buffer)];
/* Часть данных успешно прочитана. Завершаем буфер
нулевым символом, чтобы его можно было использовать
в строковых операциях. */
buffer[bytes_read] = ' ';
/* Первая строка, посылаемая клиентом, -- это HTTP-запрос.
В запросе указаны метод, запрашиваемая страница и
версия протокола. */
sscanf(buffer, "%s %s %s", method, url, protocol);
/* В заголовке, стоящем после запроса, может находиться
любая информация. В данной реализации HTTP-сервера
эта информация не учитывается. Тем не менее необходимо
прочитать все данные, посылаемые клиентом. Данные читаются
до тех пор, пока не встретится конец заголовка,
обозначаемый пустой строкой. В HTTP пустой строке
соответствуют символы CR/LF. */
while (strstr(buffer, " rnrn") == NULL)
bytes_read = read(connection_fd, buffer, sizeof(buffer));
/* Проверка правильности последней операции чтения.
Если она не завершилась успешно, произошел разрыв
соединения, поэтому завершаем работу. */
if (bytes_read == -1) {
close(connection_fd);
return;
}
/* Проверка поля версии. Сервер понимает протокол HTTP
версий 1.0 и 1.1. */
if (strcmp(protocol, "HTTP/1.0") &&
strcmp(protocol, "HTTP/1.1")) {
/* Протокол не поддерживается. */
write(connection_fd, bad_request_response,
sizeof(bad_request_response));
} else if (strcmp (method, "GET")) {
/* Сервер реализует только метод GET, а клиент указал
другой метод. */
char response[1024];
snprintf(response, sizeof(response),
bad_method_response_template, method);
write(connection_fd, response, strlen(response));
} else
/* Корректный запрос. Обрабатываем его. */
handle_get(connection_fd, url);
} else if (bytes_read == 0)
/* Клиент разорвал соединение, не успев отправить данные.
Ничего не предпринимаем */
;
else
/* Операция чтения завершилась ошибкой. */
system_error("read");
}
void server_run(struct in_addr local_address, uint16_t port) {
struct sockaddr_in socket_address;
int rval;
struct sigaction sigchld_action;
int server_socket;
/* Устанавливаем обработчик сигнала SIGCHLD, который будет
удалять завершившееся дочерние процессы. */
memset(&sigchld_action, 0, sizeof(sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchld_action, NULL);
/* Создание TCP-сокета */
server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket == -1) system_error("socket");
/* Создание адресной структуры, определяющей адрес
для приема запросов. */
memset(&socket_address, 0, sizeof(socket_address));
socket_address.sin_family = AF_INET;
socket_address.sin_port = port;
socket_address.sin_addr = local_address;
/* Привязка сокета к этому адресу. */
rval =
bind(server_socket, &socket_address,
sizeof(socket_address));
if (rval != 0)
system_error("bind");
/* Перевод сокета в режим приема запросов. */
rval = listen(server_socket, 10);
if (rval != 0)
system_error("listen");
if (verbose) {
/* В режиме развернутых сообщений отображаем адрес и порт,
с которыми работает сервер. */
socklen_t address_length;
/* Нахождение адреса сокета. */
address_length = sizeof(socket_address);
rval =
getsockname(server_socket, &socket_address, &address_length);
assert(rval == 0);
/* Вывод сообщения. Номер порта должен быть преобразован
из сетевого (обратного) порядка следования байтов
в серверный (прямой). */
printf("server listening on %s:%dn",
inet_ntoa(socket_address.sin_addr),
(int)ntohs(socket_address.sin_port));
}
/* Бесконечный цикл обработки запросов. */
while (1) {
struct sockaddr_in remote_address;
socklen_t address_length;
int connection;
pid_t child_pid;
/* Прием запроса. Эта функция блокируется до тех пор, пока
не поступит запрос. */
address_length = sizeof(remote_address);
connection = accept(server_socket, &remote_address,
&address_length);
if (connection == -1) {
/* Функция завершилась неудачно. */
if (errno == EINTR)
/* Функция была прервана сигналом. Повторная попытка. */
continue;
else
/* Что-то случилось. */
system_error("accept");
}
/* Соединение установлено. Вывод сообщения, если сервер
работает в режиме развернутых сообщений. */
if (verbose) {
socklen_t address_length;
/* Получение адреса клиента. */
address_length = sizeof(socket_address);
rval =
getpeername(connection, &socket_address, &address_length);