All Files (100.0% covered at 4.38 hits/line)
16 files in total.
358 relevant lines.
358 lines covered and
0 lines missed
-
# frozen_string_literal: true
-
-
1
require 'active_support'
-
1
require 'active_support/inflector'
-
1
require 'rack'
-
1
require 'securerandom'
-
1
require 'request_store'
-
-
1
require 'context_request_middleware/railtie' if defined?(Rails)
-
1
require 'context_request_middleware/sampling_handler'
-
1
require 'context_request_middleware/middleware'
-
1
require 'context_request_middleware/cookie'
-
1
require 'context_request_middleware/request'
-
1
require 'context_request_middleware/context'
-
1
require 'context_request_middleware/push_handler'
-
1
require 'context_request_middleware/sampling_handler/accept_all'
-
1
require 'context_request_middleware/error_logger'
-
1
require 'context_request_middleware/parameter_filter'
-
-
# :nodoc:
-
1
module ContextRequestMiddleware
-
1
include ActiveSupport::Configurable
-
-
# For older Rack Versions there is no method 'get_header' this
-
# Request class will provide that logic.
-
skipped
# :nocov:
-
skipped
config_accessor(:request_class, instance_accessor: false) do
-
skipped
if Rack.release[0].to_i < 2
-
skipped
# :nodoc:
-
skipped
class RackRequest < Rack::Request
-
skipped
def get_header(name)
-
skipped
@env[name]
-
skipped
end
-
skipped
end
-
skipped
RackRequest
-
skipped
else
-
skipped
Rack::Request
-
skipped
end
-
skipped
end
-
skipped
# :nocov:
-
-
# For older ActiveSupport versions there is no class 'ParameterFilter'
-
# ContextRequestMiddleware::ParameterFilter class will provide the logic.
-
skipped
# :nocov:
-
skipped
config_accessor(:parameter_filter_class, instance_accessor: false) do
-
skipped
if defined?(ActiveSupport::ParameterFilter)
-
skipped
ActiveSupport::ParameterFilter
-
skipped
else
-
skipped
ContextRequestMiddleware::ParameterFilter
-
skipped
end
-
skipped
end
-
skipped
# :nocov:
-
-
# Array to specify the parameters
-
# that need to be masked
-
# @default []
-
1
config_accessor(:parameter_filter_list, instance_accessor: false) do
-
1
[]
-
end
-
-
# Mask to use when masking the parameters
-
# @default '[FILTERED]'
-
1
config_accessor(:parameter_filter_mask, instance_accessor: false) do
-
1
'[FILTERED]'
-
end
-
-
# Array to specify the headers supported to hold the request_id.
-
# Defaults to the X_REQUEST_ID header.
-
# @default ['HTTP_X_REQUEST_ID']
-
1
config_accessor(:request_id_headers, instance_accessor: false) do
-
1
['HTTP_X_REQUEST_ID', 'action_dispatch.request_id']
-
end
-
-
# Array to specify the headers supported to hold the start time of the
-
# request.
-
# @default ['HTTP_X_REQUEST_START', 'HTTP_X_QUEUE_START']
-
1
config_accessor(:request_start_time_headers, instance_accessor: false) do
-
1
%w[HTTP_X_REQUEST_START HTTP_X_QUEUE_START]
-
end
-
-
# If remote IP is carried in an unsupported headers it can be specified here.
-
# Expects an Array. For one item this means one item Array.
-
# @default nil which means the X_REQUEST_HOST and rails specifics are
-
# supported
-
1
config_accessor(:remote_ip_headers, instance_accessor: false)
-
-
# Application id given to the application using the middleware.
-
# @default 'anonymous'
-
1
config_accessor(:app_id, instance_accessor: false) do
-
1
'anonymous'
-
end
-
-
# small case '_' or '.' delimited classname to point to the session id
-
# retriever.
-
# To be found under the namespace ContextRequestMiddleware::Request
-
# @default 'cookie_session_id_retriever' which resolves to
-
# ContextRequestMiddleware::Request::SessionIdRetriever.
-
1
config_accessor(:request_context_retriever, instance_accessor: false) do
-
1
'cookie_session_id_retriever'
-
end
-
# version for request_retriever.
-
# @default nil as no version yet set
-
1
config_accessor(:request_context_retriever_version, instance_accessor: false)
-
-
# Classname (small case) on how to extract the context if a new one is
-
# created. Basically this means detect if a new context was created or
-
# not. For example is the session still valid or not.
-
# To be found under the namespace ContextRequestMiddleware::Context
-
# @default 'cookie_session_id_retriever'
-
1
config_accessor(:context_retriever, instance_accessor: false) do
-
1
'cookie_session_retriever'
-
end
-
1
config_accessor(:context_retriever_version, instance_accessor: false)
-
-
# Extract the user id from Main application
-
# Set in Main App ENV['cookie_session.user_id'] = current_user.id
-
# usually done in application_controller
-
# so it can be applied to the context
-
# @default cookie_session.user_id
-
1
config_accessor(:session_owner_id, instance_accessor: false) do
-
1
'cookie_session.user_id'
-
end
-
-
# Extract the context status from Main application
-
# Set in Main App ENV['cookie_session.context_status']
-
# usually done in application_controller
-
# so it can be applied to the context
-
# @default cookie_session.context_status
-
1
config_accessor(:context_status, instance_accessor: false) do
-
1
'cookie_session.context_status'
-
end
-
-
# Classname (small case) on how to push the data stored from the current
-
# request.
-
# @default rabbitmq_push_handler which means it pushes to RabbitMQ
-
1
config_accessor(:push_handler, instance_accessor: false) do
-
1
'rabbitmq_push_handler'
-
end
-
1
config_accessor(:push_handler_version, instance_accessor: false)
-
-
# Configuration to configure the push_handler if required.
-
# @default: {}
-
2
config_accessor(:push_handler_config, instance_accessor: false) { {} }
-
-
# Classname (small case) on how to detect if this request should be
-
# sampled.
-
# @default accept_all which means all requests will be sampled.
-
1
config_accessor(:sampling_handler, instance_accessor: false) do
-
1
'accept_all'
-
end
-
1
config_accessor(:sampling_handler_version, instance_accessor: false)
-
-
# Array to specify the logger tags
-
# @default ['CONTEXT_REQUEST_MIDDLEWARE']
-
1
config_accessor(:logger_tags, instance_accessor: false) do
-
1
['CONTEXT_REQUEST_MIDDLEWARE']
-
end
-
-
# retrieves a class that is loaded from the root_pathname and
-
# suffixed with both name and version.
-
# @root_pathname: the root path to be prefixed.
-
# For example ContextRequestMiddleware::Request as string.
-
# @name: the name of the class in small case seperated with . or _.
-
# For example session_id_retriever will yield to SessionIdretriever.
-
# @version: if version is given it will be suffixed to the path.
-
# For example version=1 will yield {root_path}::V1.
-
# @return a class if found otherwise nil.
-
1
def self.load_class_from_name(name, root_path_name, version = nil)
-
22
version = "V#{version}" if version
-
[root_path_name, version, name.tr('.', '_').camelize].compact.join('::')
-
22
.constantize
-
rescue NameError
-
3
nil
-
end
-
-
#
-
# Returns the first header found from the given array of headers in the
-
# given request.
-
# @headres an array of headers to be looking for. Headers are specified full
-
# as in the environment with HTTP_ prefixed and capital letters ...
-
# @request the Rack::Request holding the request (headers) as Hash.
-
1
def self.select_request_headers(headers, request)
-
16
value = nil
-
16
headers.each do |header|
-
25
value = request.get_header(header)
-
25
break if value
-
end
-
16
value
-
end
-
end
-
-
1
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__)))
-
# frozen_string_literal: true
-
-
1
require 'context_request_middleware/context/cookie_session_retriever'
-
-
1
module ContextRequestMiddleware
-
# Base module to consolidate the different context extraction logics.
-
# Like extracting sessions that have been newly created, apitokens, ..
-
1
module Context
-
1
extend self
-
-
1
def retriever_for_response(request)
-
ContextRequestMiddleware
-
.load_class_from_name(
-
ContextRequestMiddleware.context_retriever,
-
ContextRequestMiddleware::Context.to_s,
-
ContextRequestMiddleware.context_retriever_version
-
6
)&.new(request)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
1
module Context
-
# Class for retrieving the session if set via rack cookie.
-
# This requires the session and more data to be stored in
-
# '_session_id' cookie key.
-
1
class CookieSessionRetriever
-
1
include ActiveSupport::Configurable
-
1
include ContextRequestMiddleware::Cookie
-
-
1
HTTP_HEADER = 'Set-Cookie'
-
-
1
attr_accessor :data
-
-
1
def initialize(request)
-
11
@request = request
-
11
@data = {}
-
end
-
-
1
def call(status, header, body)
-
11
@response = Rack::Response.new(body, status, header)
-
11
if new_context?
-
6
data[:context_id] = session_id
-
6
data[:owner_id] = owner_id
-
6
data[:context_status] = context_status
-
6
data[:context_type] = context_type
-
6
data[:app_id] = ContextRequestMiddleware.app_id
-
end
-
11
data
-
end
-
-
1
def new_context?
-
17
new_session_id && new_session_id != request_cookie_session_id
-
end
-
-
1
private
-
-
1
def owner_id
-
6
from_thread_var(ContextRequestMiddleware.session_owner_id, 'unknown')
-
end
-
-
1
def context_status
-
6
from_thread_var(ContextRequestMiddleware.context_status, 'unknown')
-
end
-
-
1
def context_type
-
6
'session_cookie'
-
end
-
-
1
def session_id
-
6
new_session_id || request_cookie_session_id
-
end
-
-
1
def new_session_id
-
33
new_session = nil
-
session_id_header = set_cookie_header.match(/_session_id=([^\;]+)/) \
-
33
if set_cookie_header
-
33
new_session = session_id_header[1] if session_id_header
-
33
new_session
-
end
-
-
1
def request_cookie_session_id
-
10
cookie_session_id(@request)
-
end
-
-
1
def set_cookie_header
-
59
@response.headers.fetch(HTTP_HEADER, nil)
-
end
-
-
1
def from_thread_var(key, default = nil)
-
12
RequestStore.store[key] || default
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
# Cookie module to consist the compatibility with
-
# rack version 1.x & 2.0
-
1
module Cookie
-
1
HTTP_COOKIE = 'HTTP_COOKIE' if Rack.release < '2.0.0'
-
-
skipped
# :nocov:
-
skipped
def cookie_session_id(request)
-
skipped
if Rack.release < '2.0.0'
-
skipped
parse_cookies(request.env)['_session_id'] ||
-
skipped
(request.env['action_dispatch.cookies'] || {})['_session_id']
-
skipped
else
-
skipped
Rack::Utils.parse_cookies(request.env)['_session_id'] ||
-
skipped
(request.env['action_dispatch.cookies'] || {})['_session_id']
-
skipped
end
-
skipped
end
-
skipped
-
skipped
# :nocov:
-
1
if Rack.release < '2.0.0'
-
skipped
# :nocov:
-
skipped
def parse_cookies(env)
-
skipped
parse_cookies_header env[HTTP_COOKIE]
-
skipped
end
-
skipped
-
skipped
def parse_cookies_header(header)
-
skipped
# rubocop:disable Metrics/LineLength, Style/RescueModifier, Style/CaseEquality
-
skipped
cookies = Rack::Utils.parse_query(header, ';,') { |s| unescape(s) rescue s }
-
skipped
cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
-
skipped
# rubocop:enable Metrics/LineLength, Style/RescueModifier, Style/CaseEquality
-
skipped
end
-
skipped
# :nocov:
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
# Duplicable module used by ParameterFilter module
-
1
module Duplicable
-
1
def self.check?(object)
-
12
return false if object.is_a?(Method) || object.is_a?(UnboundMethod)
-
-
12
true
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
# Logger module to provide logging of errors
-
1
module ErrorLogger
-
1
extend self
-
-
# runs block of code and logs an error if any occurs
-
1
def error_handler
-
6
yield
-
rescue StandardError => e
-
1
logger.tagged(ContextRequestMiddleware.logger_tags) do
-
1
logger.error e.message + e.backtrace.join('\n')
-
end
-
end
-
-
# Returns logger from these options:
-
# option 1: Rails.logger, as defined in the host application
-
# option 2: new instance of ActiveSupport::TaggedLogging class
-
1
def logger(logger = Logger)
-
2
@logger ||= if defined?(Rails.logger.tagged)
-
skipped
# :nocov:
-
skipped
Rails.logger
-
skipped
# :nocov:
-
else
-
1
ActiveSupport::TaggedLogging.new(logger)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
# :nodoc:
-
# rubocop:disable Metrics/ClassLength
-
1
class Middleware
-
1
def initialize(app)
-
7
@app = app
-
end
-
-
1
def call(env)
-
7
@push_handler ||= PushHandler.from_middleware
-
7
dup._call(env)
-
end
-
-
# rubocop:disable Metrics/MethodLength
-
1
def _call(env)
-
6
@data = {}
-
6
request = ContextRequestMiddleware.request_class.new(env)
-
6
request(request)
-
6
status, header, body = @app.call(env)
-
6
ContextRequestMiddleware::ErrorLogger.error_handler do
-
6
response(status, header, body)
-
6
@context = context(status, header, body, request)
-
6
push_context
-
5
push if valid_sample?(request)
-
end
-
6
[status, header, body]
-
end
-
# rubocop:enable Metrics/MethodLength
-
-
1
private
-
-
1
def request(request)
-
6
request_data(request)
-
6
others_data(request)
-
6
@data
-
end
-
-
1
def request_data(request)
-
6
@data[:request_id] = request_id(request)
-
6
@data[:request_context] = request_context(request)
-
6
@data[:request_start_time] = request_start_time(request)
-
6
@data[:request_method] = request.request_method
-
6
@data[:request_params] = filter_params(request.params)
-
6
@data[:request_path] = request.path
-
end
-
-
1
def filter_params(params)
-
6
return params if ContextRequestMiddleware.parameter_filter_list.empty?
-
-
6
filter = ContextRequestMiddleware.parameter_filter_class.new(
-
ContextRequestMiddleware.parameter_filter_list,
-
mask: ContextRequestMiddleware.parameter_filter_mask
-
)
-
6
filter.filter(params)
-
end
-
-
1
def others_data(request)
-
6
@data[:source] = source(request)
-
6
@data[:host] = request.host
-
end
-
-
1
def response(status, _headers, _response)
-
6
@data[:request_status] = status
-
end
-
-
# checks if this request changed the context
-
1
def context(status, header, body, request)
-
6
@context = context_retriever(request)&.call(status, header, body)
-
@data[:request_context] = @context[:context_id] \
-
6
if @context && @context[:context_id]
-
6
@context
-
end
-
-
# retrieves the context of the current request
-
1
def request_context(request)
-
6
@request_context ||= Request.retriever_for_request(request)&.call
-
end
-
-
1
def context_retriever(request)
-
6
@context_retriever ||=
-
Context.retriever_for_response(request)
-
end
-
-
1
def push
-
4
return unless @data
-
4
return unless @data.any?
-
4
return unless @push_handler
-
-
4
@push_handler.push(@data, push_options(@data, 'request'))
-
-
nil
-
end
-
-
1
def push_context
-
6
return unless @context_retriever.new_context?
-
3
return unless @context
-
3
return unless @context.any?
-
3
return unless @push_handler
-
-
3
@push_handler.push(@context, push_options(@data, 'context'))
-
end
-
-
1
def push_options(_data, type)
-
7
{
-
type: type,
-
message_id: SecureRandom.uuid
-
}
-
end
-
-
1
def valid_sample?(request)
-
5
@sample_handler ||= SamplingHandler.from_request
-
5
if @sample_handler
-
4
@sample_handler.valid?(request)
-
else
-
1
false
-
end
-
end
-
-
1
def request_start_time(request)
-
ContextRequestMiddleware.select_request_headers(
-
ContextRequestMiddleware.request_start_time_headers,
-
request
-
6
) || (defined?(Time.current) ? Time.current : Time.now).to_f
-
end
-
-
1
def source(request)
-
6
(ContextRequestMiddleware.remote_ip_headers &&
-
ContextRequestMiddleware
-
.select_request_headers(ContextRequestMiddleware.remote_ip_headers,
-
6
request)) ||
-
request.get_header('action_dispatch.remote_ip').to_s ||
-
request.get_header('HTTP_X_FORWARDED_HOST').to_s
-
end
-
-
1
def request_id(request)
-
6
@request_id ||= ContextRequestMiddleware.select_request_headers(
-
ContextRequestMiddleware.request_id_headers, request
-
)
-
end
-
end
-
# rubocop:enable Metrics/ClassLength
-
end
-
# frozen_string_literal: true
-
# rubocop:disable all
-
-
1
require 'context_request_middleware/duplicable'
-
-
1
module ContextRequestMiddleware
-
# ParameterFilter module to filter the contents of provided parameters.
-
# Used when the version of ActiveSupport doesn't have this class
-
1
class ParameterFilter
-
1
FILTERED = "[FILTERED]" # :nodoc:
-
-
# Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
-
# Other types of filters are treated as +String+ using +to_s+.
-
# For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
-
#
-
# ==== Options
-
#
-
# * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
-
1
def initialize(filters = [], mask: FILTERED)
-
6
@filters = filters
-
6
@mask = mask
-
end
-
-
# Mask value of +params+ if key matches one of filters.
-
1
def filter(params)
-
6
compiled_filter.call(params)
-
end
-
-
# Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
-
skipped
# :nocov:
-
skipped
def filter_param(key, value)
-
skipped
@filters.empty? ? value : compiled_filter.value_for_key(key, value)
-
skipped
end
-
skipped
# :nocov:
-
-
1
private
-
-
1
def compiled_filter
-
6
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
-
end
-
-
1
class CompiledFilter # :nodoc:
-
1
def self.compile(filters, mask:)
-
6
return lambda { |params| params.dup } if filters.empty?
-
-
6
strings, regexps, blocks = [], [], []
-
-
6
filters.each do |item|
-
30
case item
-
when Proc
-
6
blocks << item
-
when Regexp
-
6
regexps << item
-
else
-
18
strings << Regexp.escape(item.to_s)
-
end
-
end
-
-
6
deep_regexps = regexps.dup
-
12
deep_regexps.keep_if { |r| r.to_s.include?("\\.") }
-
12
regexps.delete_if { |r| r.to_s.include?("\\.") }
-
-
6
deep_strings = strings.dup
-
24
deep_strings.keep_if { |s| s.include?("\\.") }
-
24
strings.delete_if { |s| s.include?("\\.") }
-
-
6
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
-
6
deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
-
-
6
new regexps, deep_regexps, blocks, mask: mask
-
end
-
-
1
attr_reader :regexps, :deep_regexps, :blocks
-
-
1
def initialize(regexps, deep_regexps, blocks, mask:)
-
6
@regexps = regexps
-
6
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
-
6
@blocks = blocks
-
6
@mask = mask
-
end
-
-
1
def call(params, parents = [], original_params = params)
-
8
filtered_params = params.class.new
-
-
8
params.each do |key, value|
-
12
filtered_params[key] = value_for_key(key, value, parents, original_params)
-
end
-
-
8
filtered_params
-
end
-
-
1
def value_for_key(key, value, parents = [], original_params = nil)
-
12
parents.push(key) if deep_regexps
-
34
if regexps.any? { |r| r.match?(key) }
-
2
value = @mask
-
20
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
-
2
value = @mask
-
8
elsif value.is_a?(Hash)
-
2
value = call(value, parents, original_params)
-
6
elsif value.is_a?(Array)
-
skipped
# :nocov:
-
skipped
# If we don't pop the current parent it will be duplicated as we
-
skipped
# process each array value.
-
skipped
parents.pop if deep_regexps
-
skipped
value = value.map { |v| value_for_key(key, v, parents, original_params) }
-
skipped
# Restore the parent stack after processing the array.
-
skipped
parents.push(key) if deep_regexps
-
skipped
# :nocov:
-
6
elsif blocks.any?
-
6
key = key.dup if Duplicable.check?(key)
-
6
value = value.dup if Duplicable.check?(value)
-
12
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
-
end
-
12
parents.pop if deep_regexps
-
12
value
-
end
-
end
-
end
-
end
-
# rubocop:enable all
-
# frozen_string_literal: true
-
-
1
require 'context_request_middleware/push_handler/rabbitmq_push_handler'
-
1
require 'context_request_middleware/push_handler/rabbitmq_push_handler_async'
-
-
1
module ContextRequestMiddleware
-
# :nodoc:
-
1
module PushHandler
-
1
extend self
-
-
1
def initialize(**_config); end
-
-
1
def from_middleware
-
ContextRequestMiddleware
-
.load_class_from_name(ContextRequestMiddleware.push_handler,
-
ContextRequestMiddleware::PushHandler.to_s,
-
ContextRequestMiddleware.push_handler_version)
-
1
&.new(**ContextRequestMiddleware.push_handler_config)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
1
module PushHandler
-
# :nodoc:
-
1
class Base
-
1
def push(_data, _options); end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'bunny'
-
1
require 'connection_pool'
-
1
require 'context_request_middleware/push_handler/base'
-
-
1
module ContextRequestMiddleware
-
1
module PushHandler
-
# PushHandler that pusblishes the data given to a RabbitMQ exchange.
-
# If the exchange is not existant it will be created. The session is
-
# taken from the session_pool.
-
1
class RabbitmqPushHandler < Base
-
# :nodoc:
-
1
class ConfirmationFailed < StandardError
-
1
def initialize(channel, nacked, unconfirmed)
-
super("Message confirmation on the exchange #{channel} has failed\
-
1
(#{nacked}/#{unconfirmed}).")
-
end
-
end
-
# Setup the rublisher with configuring via the config options. The
-
# following config options are supported:
-
# @rabbit_mq_url url to connect to RabbitMQ
-
# @pool_size size of the connection pool to be used. Defaults to 1
-
# @session_params a hash definiting the params passed to the session.
-
# @heartbeat heartbeat interval used to communicate with
-
# RabbitMQ.
-
# @exchange_name name of the exchange defaults to 'fos.context_request'
-
# @exchange_type type of the exchange defaults to ''
-
# @exchange_options options passed to the exchange if it has to be
-
# created.
-
# rubocop:disable Metrics/MethodLength
-
1
def initialize(**config)
-
3
@config = config.dup
-
3
@session_params = config.fetch(:session_params, {})
-
.merge(threaded: false,
-
automatically_recover: false,
-
heartbeat: config[:heartbeat])
-
3
pool_size = @session_params.delete(:session_pool) || 1
-
3
@session_params.freeze
-
3
@session_pool = ConnectionPool.new(size: pool_size) do
-
3
Bunny.new(config[:rabbit_mq_url], @session_params)
-
end
-
3
config_clean
-
end
-
# rubocop:enable Metrics/MethodLength
-
-
# Publishes the given data on the exchange. The exchange is created if
-
# it does not exist.
-
# @data a hash representing the data to be published as json.
-
# @options options to be passed to the publish to the exchange.
-
1
def push(data, options)
-
3
@session_pool.with do |session|
-
3
session.start
-
3
channel = session.create_channel
-
3
channel.confirm_select
-
-
3
exchange = fetch_exchange(session, channel)
-
3
exchange.publish(data.to_json, **options)
-
-
3
wait_for_confirms(channel)
-
2
channel.close
-
end
-
end
-
-
1
private
-
-
1
def wait_for_confirms(channel)
-
3
return if channel.wait_for_confirms
-
-
1
raise ConfirmationFailed.new(exchange_name, channel.nacked_set,
-
channel.unconfirmed_set)
-
end
-
-
1
def config_clean
-
3
@config.delete(:rabbit_mq_url)
-
3
@config.delete(:session_params)
-
3
@config.delete(:heartbeat)
-
end
-
-
# return the channel if a channel is already there otherwise create a new
-
# exchange with the predefined settings.
-
# Can be overwriten by ContextRequestMiddleware.fetch_exchange_callback
-
1
def fetch_exchange(_session, channel)
-
3
channel.exchanges[exchange_name] || bunny_exchange(channel)
-
end
-
-
1
def bunny_exchange(channel)
-
1
Bunny::Exchange.new(channel, exchange_type, exchange_name,
-
exchange_options)
-
end
-
-
1
def exchange_name
-
5
@exchange_name ||= @config.fetch(:exchange_name, 'fos.context_request')
-
end
-
-
1
def exchange_type
-
1
@config.fetch(:exchange_type, 'topic')
-
end
-
-
1
def exchange_options
-
1
@config.fetch(:exchange_options, {})
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'rabbitmq_client'
-
1
require 'context_request_middleware/push_handler/base'
-
-
1
module ContextRequestMiddleware
-
1
module PushHandler
-
# PushHandler that publishes the data given to a RabbitMQ exchange.
-
# If the exchange is not existent it will be created. The session is
-
# taken from the session_pool.
-
1
class RabbitmqPushHandlerAsync < Base
-
# Setup the publisher with configuring via the config options. The
-
# following config options are supported:
-
# @rabbitmq_url url to connect to RabbitMQ
-
# @pool_size size of the connection pool to be used. Defaults to 1
-
# @session_params a hash defining the params passed to the session.
-
# @heartbeat_publisher heartbeat interval used to communicate with
-
# RabbitMQ.
-
# @exchange_name name of the exchange defaults to 'fos.context_request'
-
# @exchange_type type of the exchange defaults to ''
-
# @exchange_options options passed to the exchange if it has to be
-
# created.
-
1
def initialize(**config)
-
1
@config = config.dup
-
1
exchange = RabbitmqClient::ExchangeRegistry.new
-
1
exchange.add(exchange_name, exchange_type, exchange_options)
-
1
@config[:exchange_registry] = exchange
-
1
@publisher = RabbitmqClient::Publisher.new(@config)
-
1
config_clean
-
end
-
-
# Publishes the given data on the exchange.
-
# @data a hash representing the data to be published.
-
# @options options to be passed to the publish to the exchange.
-
1
def push(data, options)
-
1
@publisher.publish(data,
-
options.merge(exchange_name: exchange_name))
-
end
-
-
1
private
-
-
1
def config_clean
-
1
@config.delete(:rabbitmq_url)
-
1
@config.delete(:session_params)
-
1
@config.delete(:heartbeat)
-
end
-
-
1
def exchange_name
-
2
@exchange_name ||= @config.fetch(:exchange_name, 'fos.context_request')
-
end
-
-
1
def exchange_type
-
1
@config.fetch(:exchange_type, 'topic')
-
end
-
-
1
def exchange_options
-
1
@config.fetch(:exchange_options, {})
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# Module to provide helper functions on request headers, methods, cookies
-
1
require 'context_request_middleware/request/cookie_session_id_retriever'
-
-
1
module ContextRequestMiddleware
-
# :nodoc:
-
1
module Request
-
1
extend self
-
-
1
def retriever_for_request(request)
-
ContextRequestMiddleware
-
.load_class_from_name(
-
ContextRequestMiddleware.request_context_retriever,
-
ContextRequestMiddleware::Request.to_s,
-
ContextRequestMiddleware.request_context_retriever_version
-
6
)&.new(request)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
1
module Request
-
# Class for retrieving the session if set via rack cookie.
-
# This requires the session id to be stored in '_session_id'
-
# cookie key.
-
1
class CookieSessionIdRetriever
-
1
include ActiveSupport::Configurable
-
1
include ContextRequestMiddleware::Cookie
-
-
1
def initialize(request)
-
8
@request = request
-
end
-
-
1
def call
-
8
cookie_session_id(@request)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
# :nodoc:
-
1
module SamplingHandler
-
1
extend self
-
-
1
def from_request
-
ContextRequestMiddleware
-
.load_class_from_name(ContextRequestMiddleware.sampling_handler,
-
ContextRequestMiddleware::SamplingHandler.to_s,
-
ContextRequestMiddleware.sampling_handler_version)
-
5
&.new
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module ContextRequestMiddleware
-
1
module SamplingHandler
-
# Simple sampling handler that samples every request.
-
1
class AcceptAll
-
1
def valid?(_request)
-
4
true
-
end
-
end
-
end
-
end