long_polling_controller.rb 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. class LongPollingController < ApplicationController
  3. skip_before_action :session_update # prevent race conditions
  4. prepend_before_action :authentication_check_only
  5. # GET /api/v1/message_send
  6. def message_send
  7. new_connection = false
  8. # check client id
  9. client_id = client_id_verify
  10. if !client_id
  11. new_connection = true
  12. client_id = client_id_gen
  13. log 'new client connection', client_id
  14. end
  15. data = params['data'].permit!.to_h
  16. session_data = {}
  17. if current_user&.id
  18. session_data = { 'id' => current_user.id }
  19. end
  20. # spool messages for new connects
  21. if data['spool']
  22. Sessions.spool_create(data)
  23. end
  24. if data['event'] == 'login'
  25. Sessions.create(client_id, session_data, { type: 'ajax' })
  26. elsif data['event']
  27. message = Sessions::Event.run(
  28. event: data['event'],
  29. payload: data,
  30. session: session_data,
  31. client_id: client_id,
  32. clients: {},
  33. options: {},
  34. )
  35. if message
  36. Sessions.send(client_id, message)
  37. end
  38. else
  39. log "unknown message '#{data.inspect}'", client_id
  40. end
  41. if new_connection
  42. result = { client_id: client_id }
  43. render json: result
  44. return
  45. end
  46. render json: {}
  47. end
  48. # GET /api/v1/message_receive
  49. def message_receive
  50. # check client id
  51. client_id = client_id_verify
  52. raise Exceptions::UnprocessableEntity, __('Invalid client_id received!') if !client_id
  53. # check queue to send
  54. begin
  55. # update last ping
  56. 4.times do
  57. sleep 0.25
  58. end
  59. # sleep 1
  60. Sessions.touch(client_id) # rubocop:disable Rails/SkipsModelValidations
  61. # set max loop time to 24 sec. because of 30 sec. timeout of mod_proxy
  62. count = 3
  63. if Rails.env.production?
  64. count = 12
  65. end
  66. loop do
  67. count -= 1
  68. queue = Sessions.queue(client_id)
  69. if queue && queue[0]
  70. logger.debug { "send #{queue.inspect} to #{client_id}" }
  71. render json: queue
  72. return
  73. end
  74. 8.times do
  75. sleep 0.25
  76. end
  77. # sleep 2
  78. if count.zero?
  79. render json: { event: 'pong' }
  80. return
  81. end
  82. end
  83. rescue
  84. raise Exceptions::UnprocessableEntity, __('Invalid client_id in receive loop!')
  85. end
  86. end
  87. private
  88. def client_id_gen
  89. SecureRandom.uuid
  90. end
  91. def client_id_verify
  92. return if !params[:client_id]
  93. sessions = Sessions.sessions
  94. return if sessions.exclude?(params[:client_id].to_s)
  95. params[:client_id].to_s
  96. end
  97. def log(data, client_id = '-')
  98. logger.info "client(#{client_id}) #{data}"
  99. end
  100. end