# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ class LongPollingController < ApplicationController skip_action_callback :session_update # prevent race conditions # GET /api/v1/message_send def message_send new_connection = false # check client id client_id = client_id_verify if !client_id new_connection = true client_id = client_id_gen log 'new client connection', client_id end if !params['data'] params['data'] = {} end # spool messages for new connects if params['data']['spool'] msg = JSON.generate( params['data'] ) Sessions.spool_create(msg) end # get spool messages and send them to new client connection if params['data']['action'] == 'spool' # error handling if params['data']['timestamp'] log "request spool data > '#{Time.zone.at( params['data']['timestamp'] )}'", client_id else log 'request spool init data', client_id end if current_user spool = Sessions.spool_list( params['data']['timestamp'], current_user.id ) spool.each { |item| if item[:type] == 'direct' log "send spool to (user_id=#{current_user.id})", client_id Sessions.send( client_id, item[:message] ) else log 'send spool', client_id Sessions.send( client_id, item[:message] ) end } end # send spool:sent event to client sleep 0.2 log 'send spool:sent event', client_id Sessions.send( client_id, { event: 'spool:sent', data: { timestamp: Time.zone.now.utc.to_i } } ) end # receive message if params['data']['action'] == 'login' user_id = session[:user_id] user = {} if user_id user = User.find( user_id ).attributes end log "send auth login (user_id #{user_id})", client_id Sessions.create( client_id, user, { type: 'ajax' } ) # broadcast elsif params['data']['action'] == 'broadcast' # list all current clients client_list = Sessions.list client_list.each {|local_client_id, local_client| if local_client_id != client_id # broadcast to recipient list if params['data']['recipient'] && params['data']['recipient']['user_id'] params['data']['recipient']['user_id'].each { |loop_user_id| if local_client[:user]['id'].to_s == loop_user_id.to_s log "send broadcast from (#{client_id}) to (user_id #{loop_user_id})", local_client_id Sessions.send( local_client_id, params['data'] ) end } # broadcast every client else log "send broadcast from (#{client_id})", local_client_id Sessions.send( local_client_id, params['data'] ) end else log 'do not send broadcast to it self', client_id end } end if new_connection result = { client_id: client_id } render json: result else render json: {} end end # GET /api/v1/message_receive def message_receive # check client id client_id = client_id_verify if !client_id render json: { error: 'Invalid client_id receive!' }, status: :unprocessable_entity return end # check queue to send begin # update last ping 4.times { sleep 0.25 } #sleep 1 Sessions.touch( client_id ) # set max loop time to 24 sec. because of 30 sec. timeout of mod_proxy count = 12 loop do count = count - 1 queue = Sessions.queue( client_id ) if queue && queue[0] logger.debug "send #{queue.inspect} to #{client_id}" render json: queue return end 8.times { sleep 0.25 } #sleep 2 if count == 0 render json: { action: 'pong' } return end end rescue => e logger.error e.inspect logger.error e.backtrace render json: { error: 'Invalid client_id in receive loop!' }, status: :unprocessable_entity return end end private def client_id_gen rand(9_999_999_999).to_s end def client_id_verify return if !params[:client_id] sessions = Sessions.sessions return if !sessions.include?( params[:client_id].to_s ) params[:client_id].to_s end def log( data, client_id = '-' ) logger.info "client(#{client_id}) #{data}" end end