📜 ⬆️ ⬇️

Manage Nginx Push Module Channels Using Ruby

Greetings to all! Many developers know about the great Nginx Push Module for the Nginx web server. Many have tested it, felt it.

The task of the module is to allow the Nginx web server to act as a Comet server .

There is enough material to use this module: the official project page is good, the Basic HTTP Push Relay Protocol is described as well as many articles, such as Nginx & Comet: Low Latency Server Push . However, in many manuals only the basic configuration of the module is considered using a single public channel by all clients. Despite its enormous utility, the module does not provide developers with flexible channel management and their protection.
')
In this article I will write a small example showing a possible way to manage channels.


Task


What do we need?


As a result, each user will be allocated a unique channel (after passing authorization, for example).

Nginx Push Module - Secure


Nginx Push Module provides us with some directives in the nginx configuration configuration security. Consider only those that I applied:


Implementation


So, let's name our module - Channel. We will develop it in Ruby (there will also be small inserts on Rails).
To control the channels (see Basic HTTP Push Relay Protocol ), we need an HTTP client. I like Patron .
The array of open channels will be stored in the array opened_channels. The channel id will be generated using the generate_channel_id method.

Creating a channel (the open method) is done by sending a PUT request to the publish point (we just have it / publish). If the new channel is created successfully (status 200), then the generated id is added to the opened_channels array and returned.
Closing a channel (the close method) is done by sending a DELETE request to publish.
Checking the existence of a channel (the exist? Method) is performed by sending a GET request to publish. If the server returns 200, the channel is open, otherwise, we delete the channel from the array.
Sending data to the channel (push method) is carried out by sending a POST request to publish with the indication of data and content-type. Data is sent only to open channels.

All HTTP requests must contain a channel parameter (we have this channel). Naturally, the publish point should be protected.


Module code:


module Channel @http_client = Patron::Session.new @http_client.base_url = "http://localhost/publish" @@opened_channels = [] mattr_accessor :opened_channels class << self def open id = generate_channel_id resp = @http_client.put(build_request_for_channel(id), "") if resp.status == 200 opened_channels << id id else false end end def close(id) resp = @http_client.delete(build_request_for_channel(id)) resp.status end def exist?(id) resp = @http_client.get(build_request_for_channel id) if resp.status == 200 true else opened_channels.delete id false end end def push(id, data, content_type) if exist? id puts "pushing to channel with id=#{id}..." resp = @http_client.post(build_request_for_channel(id), data, {"Content-Type" => content_type}) resp.status end end def push_to_all_channels(data, content_type="application/json") opened_channels.each { |c| push(c, data, content_type) } end private def generate_channel_id UUIDTools::UUID.timestamp_create.to_s end def build_request_for_channel(id) "/?channel=#{id}" end end end 


Channel opening on request:


 def subscribe if channel_id = Channel::open render text: channel_id else render nothing: true, status: 500 end end 


Example of sending data:


 user = current_user channel_id = user.channel_id msg = user.messages.last data = msg.to_json(only: [:created_at, :text]) status = Channel::push(channel_id, msg, "application/json") 

Source: https://habr.com/ru/post/145874/


All Articles