Сервер обрабатывает только запросы протокола HTTP версий 1.0 и 1.1. Столкнувшись с иными протоколом или версией сервер возвращает HTTP-код 400 и сообщение bad_request_response
. Сервер понимает только HTTP-метод GET. Если клиент запрашивает какой-то другой метод, сервер возвращает HTTP-код 501 и сообщение bad_method_response_template
.
■ Если клиент послал правильно сформированный запрос GET, функция handle_connection()
вызывает функцию handle_get()
, которая обрабатывает запрос. Эта функция пытается загрузить серверный модуль, имя которого генерируется на основании имени запрашиваемой страницы. Например, когда клиент запрашивает страницу с именем "information", делается попытка загрузить модуль information.so
. Если модуль не может быть загружен, функция handle_get()
возвращает HTTP-код 404 и сообщение not_found_response_template
.
В случае обращения к верной странице функция handle_get()
возвращает клиенту HTTP-код 200, указывающий на успешную обработку запроса, и вызывает функцию module_generate()
, содержащуюся в модуле. Последняя генерирует HTML-код Web-страницы и посылает его клиенту.
■ Функция server_run()
регистрирует функцию clean_up_child_process()
в качестве обработчика сигнала SIGCHLD
. Обработчик просто очищает ресурсы завершившегося дочернего процесса (см. раздел 3.4.4. "Асинхронное удаление дочерних процессов").
11.2.4. Основная программа
В файле main.c
(листинг 11.5) содержится функция main()
сервера. Она отвечает за анализ аргументов командной строки и обнаружение ошибок в них, а также за конфигурирование и запуск сервера.
Листинг 11.5. ( main.c ) Главная серверная функция, выполняющая анализ аргументов командной строки
#include
#include
#include
#include
#include
#include
#include
#include
#include "server.h"
/* Описание длинных опций для функции getopt_long(). */
static const struct option long_options[] = {
{ "address", 1, NULL, 'a' },
{ "help", 0, NULL, 'h' },
{ "module-dir", 1, NULL, 'm' },
{ "port", 1, NULL, 'p' },
{ "verbose", 0, NULL, 'v' },
};
/* Описание коротких опций для функции getopt_long(). */
static const char* const short_options = "a:hm:p:v";
/* Сообщение о том, как правильно использовать программу. */
static const char* const usage_template =
"Usage: %s { options }\n"
" -a, --address ADDR Bind to local address (by default, bind\n"
" to all local addresses).\n"
" -h, --help Print this information.\n"
" -m, --module-dir DIR Load modules from specified directory\n"
" (by default, use executable directory).\n"
" -p, --port PORT Bind to specified port.\n"
" -v, --verbose Print verbose messages.\n";
/* Вывод сообщения о правильном использовании программы
и завершение работы. Если аргумент IS_ERROR не равен нулю,
сообщение записывается в поток stderr и возвращается
признак ошибки, в противном случае сообщение выводится в
поток stdout и возвращается обычный нулевой код. */
static void print_usage(int is_error) {
fprintf(is_error ? stderr : stdout, usage_template,
program_name);
exit(is_error ? 1 : 0);
}
int main(int argc, char* const argv[]) {
struct in_addr local_address;
uint16_t port;
int next_option;
/* Сохранение имени программы для отображения в сообщениях
об ошибке. */
program_name = argv[0];
/* Назначение стандартных установок. По умолчанию сервер
связан со всеми локальными адресами, и ему автоматически
назначается неиспользуемый порт. */
local_address.s_addr = INADDR_ANY;
port = 0;
/* He отображать развернутые сообщения. */
verbose = 0;
/* Загружать модули из каталога, в котором содержится
исполняемый файл. */
module_dir = get_self_executable_directory();
assert(module_dir != NULL);
/* Анализ опций. */
do {
next_option =
getopt_long(argc, argv, short_options,
long_options, NULL);
switch (next_option) {
case 'a':
/* Пользователь ввел -a или --address. */
{
struct hostent* local_host_name;
/* Поиск заданного адреса. */
local_host_name = gethostbyname(optarg);
if (local_host_name == NULL ||
local_host_name->h_length == 0)
/* He удалось распознать имя. */
error(optarg, "invalid host name");
else
/* Введено правильное имя */
local_address.s_addr =
*((int*)(local_host_name->h_addr_list[0]));
}
break;
case 'h':
/* Пользователь ввёл -h или --help. */
print_usage(0);
case 'm':
/* Пользователь ввел -m или --module-dir. */
{
struct stat dir_info;
/* Проверка существования каталога */
Читать дальше