# frozen_string_literal: true
require "rack"
require "request_store"
require "securerandom"
module Marlowe
# Marlowe correlation id middleware. Including this into your middleware
# stack will capture or add a correlation id header on an incoming request,
# and save that id in a request session variable.
class Middleware
# The name of the default header to look for and put the correlation id in.
CORRELATION_HEADER = Marlowe::Config::CORRELATION_HEADER # :nodoc:
# Configure the Marlowe middleware to call +app+ with options +opts+.
#
# === Options
#
# :header:: The name of the header to inspect. Defaults to
# 'X-Request-Id'. Also available as
# :correlation_header.
# :handler:: The handler for request correlation IDs. Defaults to
# sanitizing provided request IDs or generating a UUID.
# If :simple is provided, provided request IDs
# will not be sanitized. A callable (expecting a single
# input of any possible existing request ID) may be
# provided to introduce more complex request ID
# handling.
# :return:: If +true+ (the default), the request correlation ID
# will be returned as part of the response headers.
# :action_dispatch:: If +true+, Marlowe will add code to behave
# like ActionDispatch::RequestId.
# Depends on ActionDispatch::Request.
def initialize(app, opts = nil)
@app = app
@config = Marlowe::Config.override(opts)
end
# Stores the incoming correlation id from the +env+ hash. If the correlation
# id has not been sent, a new UUID is generated and the +env+ is modified.
def call(env)
req_id = Marlowe.make_request_id(env[config.http_header], config)
RequestStore.store[:correlation_id] = env[config.http_header] = req_id
if config.action_dispatch
req = ActionDispatch::Request.new(env)
req.request_id = req_id
end
app.call(env).tap { |_status, headers, _body|
if config.return
headers[config.header] =
if config.action_dispatch
req.request_id
else
RequestStore.store[:correlation_id]
end
end
}
end
private
attr_reader :app, :config
end
end