Skip to content
This repository has been archived by the owner on Jul 23, 2023. It is now read-only.

Commit

Permalink
merged master
Browse files Browse the repository at this point in the history
  • Loading branch information
markburns committed May 14, 2015
2 parents 8d99e97 + 4e39043 commit 6b1fbeb
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
66 changes: 66 additions & 0 deletions lib/slanger/api_server.rb
@@ -0,0 +1,66 @@
# encoding: utf-8
require 'sinatra/base'
require 'signature'
require 'json'
require 'active_support/core_ext/hash'
require 'eventmachine'
require 'em-hiredis'
require 'rack'
require 'fiber'
require 'rack/fiber_pool'

module Slanger
class ApiServer < Sinatra::Base
use Rack::FiberPool
set :raise_errors, lambda { false }
set :show_exceptions, false

# Respond with HTTP 401 Unauthorized if request cannot be authenticated.
error(Signature::AuthenticationError) { |e| halt 401, "401 UNAUTHORIZED\n#{e}" }

post '/apps/:app_id/events' do
authenticate
# Event and channel data are now serialized in the JSON data
# So, extract and use it
rv = RequestValidation.new(request.body.read)
socket_id = rv.socket_id
data = rv.data

# Send event to each channel
data["channels"].each { |channel| publish(channel, data['name'], data['data'], socket_id) }

status 202
return {}.to_json
end


post '/apps/:app_id/channels/:channel_id/events' do
authenticate

publish(params[:channel_id], params['name'], request.body.read.tap{ |s| s.force_encoding('utf-8') })

status 202
return {}.to_json
end

def payload(channel, event, data, socket_id)
{
event: event,
data: data,
channel: channel,
socket_id: socket_id
}.select { |_,v| v }.to_json
end

def authenticate
# authenticate request. exclude 'channel_id' and 'app_id' included by sinatra but not sent by Pusher.
# Raises Signature::AuthenticationError if request does not authenticate.
Signature::Request.new('POST', env['PATH_INFO'], params.except('captures', 'splat' , 'channel_id', 'app_id')).
authenticate { |key| Signature::Token.new key, Slanger::Config.secret }
end

def publish(channel, event, data, socket_id)
Slanger::Redis.publish(channel, payload(channel, event, data, socket_id))
end
end
end
25 changes: 25 additions & 0 deletions lib/slanger/request_validation.rb
@@ -0,0 +1,25 @@
module Slanger
class RequestValidation < Struct.new :body
def socket_id
validate_socket_id!(data["socket_id"])
end

def data
@data ||= JSON.parse(body.tap{ |s| s.force_encoding('utf-8')})
end

private

def validate_socket_id!(socket_id)
unless valid_socket_id?(socket_id)
raise Signature::AuthenticationError.new("Invalid socket_id: #{socket_id}")
end

socket_id
end

def valid_socket_id?(socket_id)
socket_id =~ /\A[\da-fA-F]{8}\-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}\z/
end
end
end
1 change: 0 additions & 1 deletion spec/unit/request_validation_spec.rb
Expand Up @@ -26,6 +26,5 @@ def validate(socket_id)
def body(socket_id)
{socket_id: socket_id}.to_json
end

end

0 comments on commit 6b1fbeb

Please sign in to comment.