KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Марк Митчелл - Программирование для Linux. Профессиональный подход

Марк Митчелл - Программирование для Linux. Профессиональный подход

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Марк Митчелл, "Программирование для 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"

 " 

The requested URL %s was not found on this server.

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"

 " 

The method %s is not implemented by this server.

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

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*