class Marlowe::Middleware

Marlowe correlation id middleware. Including this into your middleware stack will add a correlation id header as an incoming request, and save that id in a request session variable.

Public Class Methods

new(app, opts = {}) click to toggle source

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.

# File lib/marlowe/middleware.rb, line 34
def initialize(app, opts = {})
  @app = app
  @header, @http_header = format_header_name(
    opts[:header] || opts[:correlation_header] || CORRELATION_HEADER
  )
  @handler = opts.fetch(:handler, :clean)
  @return = opts.fetch(:return, true)
  @action_dispatch = opts.fetch(:action_dispatch, false)
end

Public Instance Methods

call(env) click to toggle source

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.

# File lib/marlowe/middleware.rb, line 46
def call(env)
  req_id = make_request_id(env[@http_header])
  RequestStore.store[:correlation_id] = env[@http_header] = req_id

  if @action_dispatch
    req = ActionDispatch::Request.new(env)
    req.request_id = req_id
  end

  @app.call(env).tap { |_status, headers, _body|
    if @return
      headers[@header] = if @action_dispatch
        req.request_id
      else
        RequestStore.store[:correlation_id]
      end
    end
  }
end

Private Instance Methods

clean(request_id) click to toggle source
# File lib/marlowe/middleware.rb, line 85
def clean(request_id)
  simple(request_id).gsub(/[^\w\-]/, "")[0, 255]
end
format_header_name(header) click to toggle source
# File lib/marlowe/middleware.rb, line 68
def format_header_name(header)
  [
    header.to_s.tr("_", "-").freeze,
    "HTTP_#{header.to_s.tr("-", "_").upcase}"
  ]
end
make_request_id(request_id) click to toggle source
# File lib/marlowe/middleware.rb, line 75
def make_request_id(request_id)
  if @handler == :simple
    simple(request_id)
  elsif @handler.is_a?(Proc)
    simple(@handler.call(request_id))
  else
    clean(request_id)
  end
end
simple(request_id) click to toggle source
# File lib/marlowe/middleware.rb, line 89
def simple(request_id)
  if request_id && !request_id.empty? && request_id !~ /\A[[:space]]*\z/
    request_id
  else
    SecureRandom.uuid
  end
end