Я написал веб-сервер, который ничего не умеет

И, спрашивается, зачем?

А затем, чтобы рассказать на примере, как обрабатываются GET-запросы. Пока что мой сервер ничего не умеет, кроме обработки GET-запросов и вывода информации о клиенте, он даже не читает никакие файлы с диска. Его задача другая, показать, как происходит обработка запросов. Я это сделал потому, что как оказалось, далеко не все веб-разработчики понимают, как это происходит. Мне вот повезло, давным давно приходилось разбирать аж multipart-запросы и обрабатывать заголовки, приходящие на сервер - в бытность, когда писал свою библиотеку на Perl.

Но сейчас я сделал пример на Ruby и не поймет его только баран (или обезьяна). Я постарался максимально документировать его, чтобы было все понятно.

require 'socket' # Из стандартной библиотеки Ruby

# Открываем TCP сокет (Это можно было сделать и по-другому, но я решил
# не создавать сокет вручную, а воспользоваться более высоким уровнем).
# Как видно, сокет будет ожидать соединения (т.к. он является сервером) на
# localhost на 8000-м порту.
server = TCPServer.open('localhost', 8000)

# Бесконечный цикл.
# После того, как соединение от клиента будет принято - клиенту
# будет возвращен ответ и соединение закроется - программа перейдет
# в начало цикла, чтобы ожидать новый запрос
loop do
  # Принимаем входящее соединение клиента. Пока соединение не будет принято,
  # сокет будет ожидать его, и код ниже этой строки
  # не будет выполняться.
  client = server.accept
  client_headers = []

  # Получаем данные клиента построчно
  while line = client.gets
    client_headers << line

    # Протокол HTTP предписывает \r\n как
    # перевод строки — всегда, независимо от операционной системы.
    # Поэтому, тело запроса нам не нужно (т.к. мы обрабатываем
    # только GET-запросы. Получить тело нам понадобилось бы
    # только в POST-запросе, а в GET-запросе тела просто-напросто нет).
    break if line == "\r\n"
  end

  # Поспим 10 секунд. Я сделал это для того, чтобы проиллюстрировать кое что.
  # Попробуйте одновременно обратиться в 2-х закладках браузера к веб-серверу и
  # вы увидите, что второй запрос будет происходить в два раза дольше
  # первого, т.к. он будет дожидаться, пока обработается первый запрос. Чтобы запросы
  # обрабатывались параллельно (т.е. не стояли в очереди) - можно сделать
  # несколько тредов (воркеров).
  sleep 10

  # Возвращаем ответ клиенту и закрываем соединение. В виде тела ответа
  # показываем клиенту его же заголовки.
  client.print "HTTP/1.1 200/OK\n"
  client.print "Content-type: text/html\n\n"
  client.print client_headers.join "<br/>"
  client.close
end

Я создал TCP-сервер при помощи объекта TCPServer, но можно было сделать и на чистом объекте Socket. Просто TCPServer, который наследуется от TCPSocket, а тот, в свою очередь, от Socket - уже настроен для того, чтобы быть сервером, то есть он умеет висеть на определенном хосте и порту и ожидать входящие соединения.

Из кода видно, что:

  • Веб-сервер обрабатывает только GET-запрос
  • Он не читает никаких файлов с диска, а просто отображает информацию о клиенте, который к нему присоединился
  • Всегда отвечает со статусом 200

Можно сохранить код в файл server.rb, а затем запустить его:

ruby server.rb

Also interesting

Tags: ,

3 Responses to “Я написал веб-сервер, который ничего не умеет”

  1. Андрей says:

    Прикольно конечно, для начала. Теперь вам стоит показать обрaботку POST запорсов. Кстати, многие не понимают как передаются куки, хотя разобраться в этом довольно просто.

    • Ouch! says:

      Да, думаю, нужно добавить. Но сначала, надо добавить треды =)
      Поддержку кук добавить довольно просто. А вот мультипарт запросы что-то ооочень ломает разбирать вручную. Тут цель не написать веб-сервер, а показать некоторые основополагающие вещи. Думаю, о чем написать дальше.

  2. Ouch! says:

    Кстати, пытался протестировать быстродействие :) с помощью ab:

    $ ab -n 5 -c 5 -k http://localhost:3000/

    Получаю это: Benchmarking localhost (be patient)…apr_socket_recv: Connection refused (111).
    Кто знает где собака зарыта? :) Я что-то не могу ничего придумать даже.

Leave a Reply