Class: StackifyRubyAPM::ResponseManipulator

Inherits:
Object
  • Object
show all
Defined in:
lib/stackify_apm/response_manipulator.rb

Overview

an abstraction for manipulating the HTML we capture in the middleware

Constant Summary collapse

SCAN_LIMIT =

examine in order to look for a RUM insertion point.

50_000
HEAD_START =
'<head'.freeze
HEAD_END =
'</head'.freeze
RUM_SCRIPT_VARIABLE =
'[RUM_SCRIPT_VARIABLE'.freeze
BRKT =
']'.freeze
GT =
'>'.freeze
CHARSET_RE =
/<\s*meta[^>]+charset\s*=[^>]*>/
X_UA_COMPATIBLE_RE =
/<\s*meta[^>]+http-equiv\s*=\s*['"]x-ua-compatible['"][^>]*>/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env, rack_response, config) ⇒ ResponseManipulator

Returns a new instance of ResponseManipulator.



24
25
26
27
28
29
30
31
32
33
# File 'lib/stackify_apm/response_manipulator.rb', line 24

def initialize(env, rack_response, config)
  @env = env
  @rack_response = rack_response

  @rack_status = rack_response[0]
  @rack_headers = rack_response[1]
  @rack_body = rack_response[2]
  @rack_flagger = nil
  @config = config
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



12
13
14
# File 'lib/stackify_apm/response_manipulator.rb', line 12

def env
  @env
end

#jsfile_to_injectObject (readonly)

Returns the value of attribute jsfile_to_inject.



10
11
12
# File 'lib/stackify_apm/response_manipulator.rb', line 10

def jsfile_to_inject
  @jsfile_to_inject
end

#rack_bodyObject (readonly)

Returns the value of attribute rack_body.



11
12
13
# File 'lib/stackify_apm/response_manipulator.rb', line 11

def rack_body
  @rack_body
end

#Rack_flaggerObject (readonly)

Returns the value of attribute Rack_flagger.



11
12
13
# File 'lib/stackify_apm/response_manipulator.rb', line 11

def Rack_flagger
  @Rack_flagger
end

#rack_headersObject (readonly)

Returns the value of attribute rack_headers.



11
12
13
# File 'lib/stackify_apm/response_manipulator.rb', line 11

def rack_headers
  @rack_headers
end

#rack_responseObject (readonly)

Returns the value of attribute rack_response.



10
11
12
# File 'lib/stackify_apm/response_manipulator.rb', line 10

def rack_response
  @rack_response
end

#rack_statusObject (readonly)

Returns the value of attribute rack_status.



11
12
13
# File 'lib/stackify_apm/response_manipulator.rb', line 11

def rack_status
  @rack_status
end

Instance Method Details

#adjust_pagehtml_response(rum_variable_script = false) ⇒ Object

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/stackify_apm/response_manipulator.rb', line 59

def adjust_pagehtml_response(rum_variable_script = false)
  response = @rack_body
  source = gather_source(response)
  close_old_response(response)
  return nil unless source

  client_id = @config.client_id
  device_id = @config.device_id
  client_rundomain = @config.client_run_domain
  inject_flag = false

  if client_rundomain != '' && StackifyRubyAPM.check_isdomain(client_rundomain) && client_id && device_id
    inject_flag = true
  else
    warn 'ClientRumDomain might be empty or Invalid URL stackify.properties.'
  end

  return unless inject_flag
  jsfile_to_inject = StackifyRubyAPM.inject_rum_script

  # Only scan the first 50k (roughly) then give up.
  beginning_of_source = source[0..SCAN_LIMIT]

  meta_tag_positions = [
    find_x_ua_compatible_position(beginning_of_source),
    find_charset_position(beginning_of_source)
  ].compact

  insertion_index = if !meta_tag_positions.empty?
                      meta_tag_positions.max
                    else
                      find_end_of_head_open(beginning_of_source)
                    end

  if insertion_index && inject_flag
    source = source[0...insertion_index] <<
             jsfile_to_inject <<
             source[insertion_index..-1]
    source = source.gsub('[RUM_SCRIPT_VARIABLE]', '') if rum_variable_script
  end
  source
end

#call_manipulateObject



35
36
37
38
39
40
41
# File 'lib/stackify_apm/response_manipulator.rb', line 35

def call_manipulate
  if check_rumscript_variable
    adjust_pagehtml_response(true)
  else
    adjust_pagehtml_response
  end
end

#check_rumscript_variableObject



43
44
45
46
47
48
49
50
51
# File 'lib/stackify_apm/response_manipulator.rb', line 43

def check_rumscript_variable
  response = @rack_body
  source = gather_source(response)
  close_old_response(response)
  return false unless source

  return true if source.include?(RUM_SCRIPT_VARIABLE)
  false
end

#close_old_response(response) ⇒ Object



130
131
132
# File 'lib/stackify_apm/response_manipulator.rb', line 130

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

#find_charset_position(beginning_of_source) ⇒ Object



119
120
121
122
# File 'lib/stackify_apm/response_manipulator.rb', line 119

def find_charset_position(beginning_of_source)
  match = CHARSET_RE.match(beginning_of_source)
  match.end(0) if match
end

#find_end_of_head_open(beginning_of_source) ⇒ Object

rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/PerceivedComplexity



104
105
106
107
# File 'lib/stackify_apm/response_manipulator.rb', line 104

def find_end_of_head_open(beginning_of_source)
  head_open = beginning_of_source.index(HEAD_END)
  beginning_of_source.index(GT, head_open) - 6 if head_open
end

#find_rum_script_variable_index(beginning_of_source) ⇒ Object



109
110
111
112
# File 'lib/stackify_apm/response_manipulator.rb', line 109

def find_rum_script_variable_index(beginning_of_source)
  head_open = beginning_of_source.index(RUM_SCRIPT_VARIABLE)
  beginning_of_source.index(BRKT, head_open) - 20 if head_open
end

#find_x_ua_compatible_position(beginning_of_source) ⇒ Object



114
115
116
117
# File 'lib/stackify_apm/response_manipulator.rb', line 114

def find_x_ua_compatible_position(beginning_of_source)
  match = X_UA_COMPATIBLE_RE.match(beginning_of_source)
  match.end(0) if match
end

#gather_source(response) ⇒ Object



124
125
126
127
128
# File 'lib/stackify_apm/response_manipulator.rb', line 124

def gather_source(response)
  source = nil
  response.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s) }
  source
end

#rebuild_rack_responseObject



53
54
55
# File 'lib/stackify_apm/response_manipulator.rb', line 53

def rebuild_rack_response
  [rack_status, rack_headers, rack_body]
end