Class: Rollbar::Middleware::Js

Inherits:
Object
  • Object
show all
Includes:
RequestDataExtractor
Defined in:
lib/rollbar/middleware/js.rb

Overview

Middleware to inject the rollbar.js snippet into a 200 html response

Constant Summary collapse

JS_IS_INJECTED_KEY =
'rollbar.js_is_injected'
SNIPPET =
File.read(File.expand_path('../../../../data/rollbar.snippet.js', __FILE__))

Constants included from RequestDataExtractor

RequestDataExtractor::ALLOWED_BODY_PARSEABLE_METHODS, RequestDataExtractor::ALLOWED_HEADERS_REGEX

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RequestDataExtractor

#extract_person_data_from_controller, #extract_request_data_from_rack, #scrub_params, #scrub_url

Constructor Details

#initialize(app, config) ⇒ Js

Returns a new instance of Js.



19
20
21
22
# File 'lib/rollbar/middleware/js.rb', line 19

def initialize(app, config)
  @app = app
  @config = config
end

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



13
14
15
# File 'lib/rollbar/middleware/js.rb', line 13

def app
  @app
end

#configObject (readonly)

Returns the value of attribute config.



14
15
16
# File 'lib/rollbar/middleware/js.rb', line 14

def config
  @config
end

Instance Method Details

#add_js(env, response) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/rollbar/middleware/js.rb', line 62

def add_js(env, response)
  body = join_body(response)
  close_old_response(response)

  return nil unless body

  insert_after_idx = find_insertion_point(body)
  return nil unless insert_after_idx

  build_body_with_js(env, body, insert_after_idx)
rescue => e
  Rollbar.log_error("[Rollbar] Rollbar.js could not be added because #{e} exception")
  nil
end

#add_js?(env, headers) ⇒ Boolean

Returns:

  • (Boolean)


43
44
45
46
# File 'lib/rollbar/middleware/js.rb', line 43

def add_js?(env, headers)
  enabled? && !env[JS_IS_INJECTED_KEY] &&
    html?(headers) && !attachment?(headers) && !streaming?(env)
end

#add_person_data(js_config, env) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/rollbar/middleware/js.rb', line 124

def add_person_data(js_config, env)
  person_data = extract_person_data_from_controller(env)

  return if person_data && person_data.empty?

  js_config[:payload] ||= {}
  js_config[:payload][:person] = person_data if person_data
end

#append_nonce?Boolean

Returns:

  • (Boolean)


157
158
159
160
161
162
# File 'lib/rollbar/middleware/js.rb', line 157

def append_nonce?
  defined?(::SecureHeaders) && ::SecureHeaders.respond_to?(:content_security_policy_script_nonce) &&
    defined?(::SecureHeaders::Configuration) &&
    !::SecureHeaders::Configuration.get.csp.opt_out? &&
    !::SecureHeaders::Configuration.get.current_csp[:script_src].to_a.include?("'unsafe-inline'")
end

#attachment?(headers) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/rollbar/middleware/js.rb', line 52

def attachment?(headers)
  headers['Content-Disposition'].to_s.include?('attachment')
end

#build_body_with_js(env, body, head_open_end) ⇒ Object



87
88
89
90
91
92
# File 'lib/rollbar/middleware/js.rb', line 87

def build_body_with_js(env, body, head_open_end)
  return body unless head_open_end

  body[0..head_open_end] << config_js_tag(env) << snippet_js_tag(env) <<
    body[head_open_end + 1..-1]
end

#build_response(env, app_result, response_string) ⇒ Object



77
78
79
80
81
82
83
84
85
# File 'lib/rollbar/middleware/js.rb', line 77

def build_response(env, app_result, response_string)
  return app_result unless response_string

  env[JS_IS_INJECTED_KEY] = true
  response = ::Rack::Response.new(response_string, app_result[0],
                                  app_result[1])

  response.finish
end

#call(env) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rollbar/middleware/js.rb', line 24

def call(env)
  app_result = app.call(env)

  begin
    return app_result unless add_js?(env, app_result[1])

    response_string = add_js(env, app_result[2])
    build_response(env, app_result, response_string)
  rescue => e
    Rollbar.log_error("[Rollbar] Rollbar.js could not be added because #{e} exception")

    app_result
  end
end

#close_old_response(response) ⇒ Object



112
113
114
# File 'lib/rollbar/middleware/js.rb', line 112

def close_old_response(response)
  response.close if response.respond_to?(:close)
end

#config_js_tag(env) ⇒ Object



116
117
118
119
120
121
122
# File 'lib/rollbar/middleware/js.rb', line 116

def config_js_tag(env)
  js_config = Rollbar::Util.deep_copy(config[:options])

  add_person_data(js_config, env)

  script_tag("var _rollbarConfig = #{js_config.to_json};", env)
end

#enabled?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/rollbar/middleware/js.rb', line 39

def enabled?
  !!config[:enabled]
end

#find_end_after_regex(body, regex) ⇒ Object



100
101
102
103
# File 'lib/rollbar/middleware/js.rb', line 100

def find_end_after_regex(body, regex)
  open_idx = body.index(regex)
  body.index('>', open_idx) if open_idx
end

#find_insertion_point(body) ⇒ Object



94
95
96
97
98
# File 'lib/rollbar/middleware/js.rb', line 94

def find_insertion_point(body)
  find_end_after_regex(body, /<meta\s*charset=/i) ||
    find_end_after_regex(body, /<meta\s*http-equiv="Content-Type"/i) ||
    find_end_after_regex(body, /<head\W/i)
end

#html?(headers) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/rollbar/middleware/js.rb', line 48

def html?(headers)
  headers['Content-Type'] && headers['Content-Type'].include?('text/html')
end

#html_safe_if_needed(string) ⇒ Object



152
153
154
155
# File 'lib/rollbar/middleware/js.rb', line 152

def html_safe_if_needed(string)
  string = string.html_safe if string.respond_to?(:html_safe)
  string
end

#join_body(response) ⇒ Object



105
106
107
108
109
110
# File 'lib/rollbar/middleware/js.rb', line 105

def join_body(response)
  response.to_enum.reduce('') do |acc, fragment|
    acc << fragment.to_s
    acc
  end
end

#js_snippetObject



137
138
139
# File 'lib/rollbar/middleware/js.rb', line 137

def js_snippet
  SNIPPET
end

#script_tag(content, env) ⇒ Object



141
142
143
144
145
146
147
148
149
150
# File 'lib/rollbar/middleware/js.rb', line 141

def script_tag(content, env)
  if append_nonce?
    nonce = ::SecureHeaders.content_security_policy_script_nonce(::Rack::Request.new(env))
    script_tag_content = "\n<script type=\"text/javascript\" nonce=\"#{nonce}\">#{content}</script>"
  else
    script_tag_content = "\n<script type=\"text/javascript\">#{content}</script>"
  end

  html_safe_if_needed(script_tag_content)
end

#snippet_js_tag(env) ⇒ Object



133
134
135
# File 'lib/rollbar/middleware/js.rb', line 133

def snippet_js_tag(env)
  script_tag(js_snippet, env)
end

#streaming?(env) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
60
# File 'lib/rollbar/middleware/js.rb', line 56

def streaming?(env)
  return false unless defined?(ActionController::Live)

  env['action_controller.instance'].class.included_modules.include?(ActionController::Live)
end