Class: Appsignal::Rack::BodyWrapper Private

Inherits:
Object
  • Object
show all
Defined in:
lib/appsignal/rack/body_wrapper.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Direct Known Subclasses

CallableBodyWrapper, EnumerableBodyWrapper

Constant Summary collapse

IGNORED_ERRORS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

[Errno::EPIPE].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(body, appsignal_transaction) ⇒ BodyWrapper

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of BodyWrapper.



39
40
41
42
43
# File 'lib/appsignal/rack/body_wrapper.rb', line 39

def initialize(body, appsignal_transaction)
  @body_already_closed = false
  @body = body
  @transaction = appsignal_transaction
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Delegate missing methods to the wrapped body. Based on: github.com/rack/rack/blob/0ed580bbe3858ffe5d530adf1bdad9ef9c03407c/lib/rack/body_proxy.rb#L44-L61



71
72
73
# File 'lib/appsignal/rack/body_wrapper.rb', line 71

def method_missing(method_name, *args, &block)
  @body.__send__(method_name, *args, &block)
end

Class Method Details

.wrap(original_body, appsignal_transaction) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/appsignal/rack/body_wrapper.rb', line 9

def self.wrap(original_body, appsignal_transaction)
  # The logic of how Rack treats a response body differs based on which methods
  # the body responds to. This means that to support the Rack 3.x spec in full
  # we need to return a wrapper which matches the API of the wrapped body as closely
  # as possible. Pick the wrapper from the most specific to the least specific.
  # See https://github.com/rack/rack/blob/main/SPEC.rdoc#the-body-
  #
  # What is important is that our Body wrapper responds to the same methods Rack
  # (or a webserver) would be checking and calling, and passes through that functionality
  # to the original body.
  #
  # This comment https://github.com/rails/rails/pull/49627#issuecomment-1769802573
  # is of particular interest to understand why this has to be somewhat complicated.
  if original_body.respond_to?(:to_path)
    PathableBodyWrapper.new(original_body, appsignal_transaction)
  elsif original_body.respond_to?(:to_ary)
    ArrayableBodyWrapper.new(original_body, appsignal_transaction)
  elsif !original_body.respond_to?(:each) && original_body.respond_to?(:call)
    # This body only supports #call, so we must be running a Rack 3 application
    # It is possible that a body exposes both `each` and `call` in the hopes of
    # being backwards-compatible with both Rack 3.x and Rack 2.x, however
    # this is not going to work since the SPEC says that if both are available,
    # `each` should be used and `call` should be ignored.
    # So for that case we can drop to our default EnumerableBodyWrapper
    CallableBodyWrapper.new(original_body, appsignal_transaction)
  else
    EnumerableBodyWrapper.new(original_body, appsignal_transaction)
  end
end

Instance Method Details

#closeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This must be present in all Rack bodies and will be called by the serving adapter



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/appsignal/rack/body_wrapper.rb', line 46

def close
  # The @body_already_closed check is needed so that if `to_ary`
  # of the body has already closed itself (as prescribed) we do not
  # attempt to close it twice
  if !@body_already_closed && @body.respond_to?(:close)
    Appsignal.instrument("close_response_body.rack") { @body.close }
  end
  @body_already_closed = true
rescue *IGNORED_ERRORS # Do not report
  raise
rescue Exception => error # rubocop:disable Lint/RescueException
  appsignal_report_error(error)
  raise error
end

#respond_to_missing?(method_name, include_all = false) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return whether the wrapped body responds to the method if this class does not. Based on: github.com/rack/rack/blob/0ed580bbe3858ffe5d530adf1bdad9ef9c03407c/lib/rack/body_proxy.rb#L16-L24

Returns:

  • (Boolean)


64
65
66
# File 'lib/appsignal/rack/body_wrapper.rb', line 64

def respond_to_missing?(method_name, include_all = false)
  super || @body.respond_to?(method_name, include_all)
end