Class: Rollbar::Middleware::Js

Inherits:
Object
  • Object
show all
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__))

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, config) ⇒ Js

Returns a new instance of Js.



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

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

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



8
9
10
# File 'lib/rollbar/middleware/js.rb', line 8

def app
  @app
end

#configObject (readonly)

Returns the value of attribute config.



9
10
11
# File 'lib/rollbar/middleware/js.rb', line 9

def config
  @config
end

Instance Method Details

#add_js(env, response) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rollbar/middleware/js.rb', line 56

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

  return nil unless body

  head_open_end = find_end_of_head_open(body)
  return nil unless head_open_end

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

#add_js?(env, status, headers) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#append_nonce?Boolean

Returns:

  • (Boolean)


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

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

#attachment?(headers) ⇒ Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/rollbar/middleware/js.rb', line 46

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

#build_body_with_js(env, body, head_open_end) ⇒ Object



81
82
83
84
85
86
# File 'lib/rollbar/middleware/js.rb', line 81

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



71
72
73
74
75
76
77
78
79
# File 'lib/rollbar/middleware/js.rb', line 71

def build_response(env, app_result, response_string)
  return 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



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/rollbar/middleware/js.rb', line 19

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

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

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

#close_old_response(response) ⇒ Object



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

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

#config_js_tag(env) ⇒ Object



104
105
106
# File 'lib/rollbar/middleware/js.rb', line 104

def config_js_tag(env)
  script_tag("var _rollbarConfig = #{config[:options].to_json};", env)
end

#enabled?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/rollbar/middleware/js.rb', line 33

def enabled?
  !!config[:enabled]
end

#find_end_of_head_open(body) ⇒ Object



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

def find_end_of_head_open(body)
  head_open = body.index(/<head\W/)
  body.index('>', head_open) if head_open
end

#html?(headers) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#html_safe_if_needed(string) ⇒ Object



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

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

#join_body(response) ⇒ Object



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

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

#js_snippetObject



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

def js_snippet
  SNIPPET
end

#script_tag(content, env) ⇒ Object



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

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



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

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

#streaming?(env) ⇒ Boolean

Returns:

  • (Boolean)


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

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

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