Всем привет, думаю читатель, нажавший на данный заголовок, уже, возможно, догадывается, о чем примерно пойдет речь. Данная статья рассчитана на тех, у кого уже есть какое-либо понимания языков программирования (лучше если именно Ruby), а также хотя бы общие сведения о работе ОС и сети.

Данный цикл статей будет посвящен написанию с нуля без использования сторонних библиотек аналога известного фреймворка Ruby on Rails который назовем SimpleRails. Конечно, мы не будем его повторять полностью, а лишь попытаемся повторить основные функциональные возможности.

Для тех, кто не в курсе в мире веб разработки существует фреймворк Ruby on Rails. Фреймворк горячо любим автором и сообществом и используется для создания полнофункционального сайта или по-другому веб приложения. Но все ли знают и понимают, как оно там вообще работает?

Как говорил классик – позвольте мне 30 секунд исторической справки. Все примерно слышали, что сайты работают через протокол HTTP/ HTTPS. Для упрощения возьмем протокол HTTP. Данный протокол текстовый (и работает поверх протокола TCP описание работы которого мы опустим), но что нам это говорит? По сути, это просто строка, в которой определенным способом хранится информация. То есть, по сути, вся работа сайта — это отправка и получение строк в определенном формате. Вот и все. Возможно, для кого-то это покажется слишком простым, но так оно и есть. Когда вы открыли сайт, на самом деле вам пришла текстовая строка, которую браузер обработал и вам отобразил.

Простейшая схема будет выглядеть так. Здесь HTTP request и HTTP response это просто строки. Верхняя отправляется на сервер, где находится сайт (пока опустим сетевые моменты). Когда вы у себя в браузере вводите название сайта или нажимаете на его ссылку - вы отправляете строку на сервер о желании получить страничку сайта, сервер отправляет вам строку с этой страничкой.

Рис 1 – Простейшая работа сайта
Рис 1 – Простейшая работа сайта

Что же за строки такие отправляются и что из себя по итогу представляет этот HTTP протокол?

Простейший запрос GET
GET /customers/42 HTTP/1.1

Простейший запрос POST

POST /customers HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

email=test@acme.com&password=pass123

P.S. пример запросов любезно взят с сайта

Запрос может содержать разные части - Разберем сначала GET запрос (интерпретация будет вольная, за точной можно отправиться за соответствующим RFC). В первой строке сначала указывается метод запроса (GET), затем путь до ресурса (/customers/42), который мы хотим получить, и затем версия самого протокола (HTTP/1.1). Запрос может быть и сложнее, как например предоставленный далее POST запрос. Аналогичным образом у него формируется первая строка, но затем идут еще несколько строк. Сначала идут строки заголовков (7 и 8 строки), а затем тело запроса (10 строка). Строки разделяются между собой символами \r\n.

С самими запросами разобрались, но кто их шлет и какими средствам получает?

Поскольку мы работаем на компьютере, на нем установлена та или иная ОС. У основных широко используемых ОС, на которых есть возможность сетевого взаимодействия есть понятие сокетов. Согласно определению википедии сокет — это программный интерфейс для обеспечения обмена данными между процессами. Возможно все равно не до конца понятно. Для большего упрощения будем считать, что сокет — это буфер, в который можно записать или считать строку.

Возьмем аналогию с кассой в банке. Как там происходит взаимодействие? Вы идете в банк, просите вас обслужить в кассе, подходите к окну с деньгами и документами, кладете их в специальный ящик и ждете пока оператор за окошком их обработает и вернет вам обратно тоже деньги или документы. С сокетами также, вы «идете» к операционной системе, просите дать вам сокет, ОС предоставляет вам сокет, вы в него пишете свою строку, ждете и получаете другую строку.

Рис 2 – Простейшая работа кассы в банке
Рис 2 – Простейшая работа кассы в банке

Вернемся к реализации. Взглянем на вырезку из документации Ruby.

# A simple server might look like this:
require 'socket'

server = TCPServer.new 2000 # Server bound to port 2000

loop do
  client = server.accept    # Wait for a client to connect
  client.puts "Hello !"
  client.puts "Time is #{Time.now}"
  client.close
end

# A simple client may look like this:
require 'socket'

s = TCPSocket.new 'localhost', 2000

while line = s.gets # Read lines from socket
  puts line         # and print them
end

s.close             # close socket when done

Немного разберем данный код. В коде сервера мы сначала подключаем гем с сокетами, затем просим ОС открыть новый сокет на определенном порту, затем в цикле начинаем ожидать получения данных. После получения данных мы пишем обратно пару строк кода с текущем временем и отправляем их обратно. В коде клиента все работает схожим образом. Мы также подключаем гем с сокетами, затем просим ОС подключиться к существующему сокету на определенном порту, затем запрашиваем данные у сервера, печатаем их в консоль и закрываем подключение к сокету. Можно провести соответствующие аналогии с примером с банком выше.

Сверху уже вполне работающий код сервера. К сожалению, он не удовлетворяет требованию формата HTTP, но мы можем его проверить запустив клиентский код. Немного дополним его для удовлетворения требованиям формата HTTP.

require 'socket'
server = TCPServer.new 2000

puts "server started"

while session = server.accept
  puts 'get request'
  session.print "HTTP/1.1 200\r\n"
  session.print "Content-Type: text/html\r\n"
  session.print "\r\n"
  session.print "<p>Hello from SimpleRails!</p>"

  session.close
end

Этот код уже можно запустить и получить ответ в браузере. В нем мы отправляем ответ клиенту/браузеру тоже в формате HTTP. Сначала указываем версию протокола и код возврата (200), затем пишем заголовок о том, что формат данных будет текст или html и затем сами данные в формате html.

Рис 3 – Ответ от сервера
Рис 3 – Ответ от сервера

Но почему мы не пишем наши приложения таким образом? Почему никогда не залезаем в такие дебри? Можно, конечно, в этом одном файлике вручную обрабатывать каждый приходящий пакет, но это выйдет слишком сложно, получится непонятное месиво не поддерживаемого кода. В результате были придуманы некоторые абстракции, которым будут посвящены следующие статьи.

Комментарии (6)


  1. Nurked
    16.11.2024 17:29

    Жесть. Ребят, я 14 лет назад писал статью про Рельсы. Даже тогда получилось больше и полезнее.

    Просто так генерировать не получится. Тут писать надо.


    1. Martin_mad Автор
      16.11.2024 17:29

      В статье написано что это только первая часть. Изначальный смысл был не сделать очередное описание работы существующих рельс, которых в интернете полно, а попробовать написать их самим.


      1. Nurked
        16.11.2024 17:29

        Ну окей. Хорошо, это я поддерживаю. Но у вас статья на 800 слов. Это даже на серьёзный коммент не дотягивает. Интересно-же. Но это не статья, которую интересно читать. Это - просто коммент о том, как работают рельсы на 800 символов, который уже и сам по себе понятен из мануала на сайте рельсов.

        А как насчёт того, что не понятно из контекста? Или подводных камней? Или, может быть пару десятков абзацев на тему того "Нафига эти рельсы нужны?" Они же нужны. Люди пишут на руби.

        Если хотите - давайте я помогу написать. Хотя бы план статьи. Но это больше похоже на пост, а не на статью


        1. k4ir05
          16.11.2024 17:29

          Это - просто коммент о том, как работают рельсы на 800 символов, который уже и сам по себе понятен из мануала на сайте рельсов.

          Если бы. Тут затронули только зачаток веб-сервера (что не является частью Рельсов).


    1. Sheti
      16.11.2024 17:29

      Рельсы ещё живы? Даже удивлен немного.


      1. k4ir05
        16.11.2024 17:29

        Живее всех живых. Совсем недавно 8-я версия вышла с кучей новинок.