class GripMiddleware

Public Class Methods

new(app) click to toggle source
# File lib/gripmiddleware.rb, line 14
def initialize(app)  
  @app = app  
end

Public Instance Methods

call(env) click to toggle source
# File lib/gripmiddleware.rb, line 18
def call(env)
  env['grip_proxied'] = false
  env['grip_wscontext'] = nil
  grip_signed = false
  grip_proxies = RailsSettings.get_grip_proxies
  if env.key?('HTTP_GRIP_SIG') and !grip_proxies.nil?
    grip_proxies.each do |entry|
      if GripControl.validate_sig(env['HTTP_GRIP_SIG'], entry['key'])         
        grip_signed = true
        break
      end
    end
  end
  content_type = nil
  if env.key?('CONTENT_TYPE')
    content_type = env['CONTENT_TYPE']
    at = content_type.index(';')
    if !at.nil?
      content_type = content_type[0..at-1]
    end
  end
  accept_types = nil
  if env.key?('HTTP_ACCEPT')
    accept_types = env['HTTP_ACCEPT']
    tmp = accept_types.split(',')
    accept_types = []
    tmp.each do |s|
      accept_types.push(s.strip)
    end
  end
  wscontext = nil
  if env['REQUEST_METHOD'] == 'POST' and ((content_type == 
      'application/websocket-events') or (!accept_types.nil? and
      accept_types.include?('application/websocket-events')))
    cid = nil
    if env.key?('HTTP_CONNECTION_ID')
      cid = env['HTTP_CONNECTION_ID']
    end
    meta = {}
    env.each do |k, v|
      if k.start_with?('HTTP_META_')
        meta[convert_header_name(k[10..-1])] = v
      end
    end
    events = nil
    begin
      events = GripControl.decode_websocket_events(env["rack.input"].read)
    rescue
      return [ 400, {}, ["Error parsing WebSocket events.\n"]]
    end
    wscontext = WebSocketContext.new(cid, meta, events)
  end
  env['grip_proxied'] = grip_signed
  env['grip_wscontext'] = wscontext
  begin
    status, headers, response = @app.call(env)
  rescue NonWebSocketRequestError => e 
    return [400, {}, [e.message + "\n"]]
  end
  if !env['grip_wscontext'].nil? and status == 200
    wscontext = env['grip_wscontext']
    meta_remove = Set.new
    wscontext.orig_meta.each do |k, v|
      found = false
      wscontext.meta.each do |nk, nv|
        if nk.downcase == k
          found = true
          break
        end
      end
      if !found
        meta_remove.add(k)
      end
    end
    meta_set = {}
    wscontext.meta.each do |k, v|
      lname = k.downcase
      need_set = true        
      wscontext.orig_meta.each do |ok, ov|
        if lname == ok and v == ov
          need_set = false
          break
        end
      end
      if need_set
        meta_set[lname] = v
      end
    end
    events = []
    if wscontext.accepted
      events.push(WebSocketEvent.new('OPEN'))
    end
    events.push(*wscontext.out_events)
    if wscontext.closed
      events.push(WebSocketEvent.new('CLOSE',
          [wscontext.out_close_code].pack('S>')))
    end
    if response.respond_to?(:content_type)
      response.body = GripControl.encode_websocket_events(events)
      response.content_type = 'application/websocket-events'
    else
      response = [GripControl.encode_websocket_events(events)]
    end
    headers['Content-Type'] = 'application/websocket-events'
    if wscontext.accepted
                              headers['Sec-WebSocket-Extensions'] = 'grip'
    end
    meta_remove.each do |k, v|
                  headers['Set-Meta-' + k] = ''
    end
    meta_set.each do |k, v|
                  headers['Set-Meta-' + k] = v
    end
  elsif !env['grip_hold'].nil?
    if !env['grip_proxied'] and RailsSettings.get_grip_proxy_required
      return [ 501, {}, ["Not implemented.\n"]]
    end
    channels = env['grip_channels']
    prefix = RailsSettings.get_prefix
    if prefix != ''
      channels.each do |channel|
        channel.name = prefix + channel.name
      end
    end
    if status == 304
      iheaders = headers.clone
      if !iheaders.key?('Location') and response.respond_to?(:location) and
          !response.location.nil?
        iheaders['Location'] = response.location
      end
      if response.respond_to?(:body)
        orig_body = response.body
      else
        orig_body = response.to_s
      end
      iresponse = Response.new(status, nil, iheaders, orig_body)
      timeout = nil
      if !env['grip_timeout'].nil?
        timeout = env['grip_timeout']
      end
      if response.respond_to?(:content_type)
        response.body = GripControl.create_hold(env['grip_hold'],
            channels, iresponse, timeout)
        response.content_type = 'application/grip-instruct'
      else
        response = [GripControl.create_hold(env['grip_hold'],
            channels, iresponse, timeout)]
      end
      headers = {'Content-Type' => 'application/grip-instruct'}
    else
      headers['Grip-Hold'] = env['grip_hold']
      headers['Grip-Channel'] = GripControl.create_grip_channel_header(
          channels)
      if !env['grip_timeout'].nil?
        headers['Grip-Timeout'] = env['grip_timeout'].to_s
      end
    end
  end
  return [status, headers, response]
end

Private Instance Methods

convert_header_name(name) click to toggle source
# File lib/gripmiddleware.rb, line 181
def convert_header_name(name)
  out = ''
  name.each_char do |c|
    if c == '_'
      out += '-'
    else
      out += c.downcase
    end
  end
  return out
end