resurfaceio-logger 1.8.3 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/resurfaceio/all.rb +13 -1
- data/lib/resurfaceio/base_logger.rb +33 -7
- data/lib/resurfaceio/http_logger.rb +93 -76
- data/lib/resurfaceio/http_logger_for_rack.rb +3 -3
- data/lib/resurfaceio/http_logger_for_rails.rb +9 -7
- data/lib/resurfaceio/http_message.rb +89 -0
- data/lib/resurfaceio/http_request_impl.rb +6 -1
- data/lib/resurfaceio/http_response_impl.rb +1 -1
- data/lib/resurfaceio/http_rule.rb +15 -0
- data/lib/resurfaceio/http_rules.rb +130 -0
- data/lib/resurfaceio/usage_loggers.rb +4 -4
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '081b638ef0c63585d851de54a9b30ef9b54de58cf68f880785c2e1a8d214cb54'
|
4
|
+
data.tar.gz: a71c49ec92a27637f7d77b52177df39d0b197757a056154e5247f5e40aa350d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2f2c6be12c8977756ab185cc34c64cc29b0956940139315a1d9c023f4df0756cbea1c3fdb8eaab9207070ddda63ee1dac34688d8272d1782d5664b531de94a3
|
7
|
+
data.tar.gz: 8eed5ac226184687025edf8d3f213585b73d1577c8c6a5420f71b4bc5205add88470672e350746edef7e2a48bf3441276517fcb7ec176bdb16f88b21f9059c7a
|
data/lib/resurfaceio/all.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
require 'resurfaceio/base_logger'
|
5
5
|
require 'resurfaceio/http_logger'
|
6
6
|
require 'resurfaceio/http_logger_for_rack'
|
7
7
|
require 'resurfaceio/http_logger_for_rails'
|
8
|
+
require 'resurfaceio/http_message'
|
8
9
|
require 'resurfaceio/http_request_impl'
|
9
10
|
require 'resurfaceio/http_response_impl'
|
11
|
+
require 'resurfaceio/http_rule'
|
12
|
+
require 'resurfaceio/http_rules'
|
10
13
|
require 'resurfaceio/usage_loggers'
|
11
14
|
|
12
15
|
module Resurfaceio
|
@@ -23,12 +26,21 @@ module Resurfaceio
|
|
23
26
|
class HttpLoggerForRails < HttpLoggerForRails
|
24
27
|
end
|
25
28
|
|
29
|
+
class HttpMessage < HttpMessage
|
30
|
+
end
|
31
|
+
|
26
32
|
class HttpRequestImpl < HttpRequestImpl
|
27
33
|
end
|
28
34
|
|
29
35
|
class HttpResponseImpl < HttpResponseImpl
|
30
36
|
end
|
31
37
|
|
38
|
+
class HttpRule < HttpRule
|
39
|
+
end
|
40
|
+
|
41
|
+
class HttpRules < HttpRules
|
42
|
+
end
|
43
|
+
|
32
44
|
class UsageLoggers < UsageLoggers
|
33
45
|
end
|
34
46
|
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
require 'uri'
|
5
5
|
require 'net/http'
|
6
6
|
require 'net/https'
|
7
|
+
require 'socket'
|
7
8
|
require 'zlib'
|
8
9
|
require 'resurfaceio/usage_loggers'
|
9
10
|
|
@@ -11,6 +12,7 @@ class BaseLogger
|
|
11
12
|
|
12
13
|
def initialize(agent, options = {})
|
13
14
|
@agent = agent
|
15
|
+
@host = BaseLogger.host_lookup
|
14
16
|
@skip_compression = false
|
15
17
|
@skip_submission = false
|
16
18
|
@version = BaseLogger.version_lookup
|
@@ -48,6 +50,8 @@ class BaseLogger
|
|
48
50
|
@enabled = false
|
49
51
|
end
|
50
52
|
end
|
53
|
+
|
54
|
+
@enableable = !@queue.nil? || !@url.nil?
|
51
55
|
end
|
52
56
|
|
53
57
|
def agent
|
@@ -60,14 +64,26 @@ class BaseLogger
|
|
60
64
|
end
|
61
65
|
|
62
66
|
def enable
|
63
|
-
@enabled = true
|
67
|
+
@enabled = true if @enableable
|
64
68
|
self
|
65
69
|
end
|
66
70
|
|
71
|
+
def enableable?
|
72
|
+
@enableable
|
73
|
+
end
|
74
|
+
|
67
75
|
def enabled?
|
68
76
|
@enabled && UsageLoggers.enabled?
|
69
77
|
end
|
70
78
|
|
79
|
+
def host
|
80
|
+
@host
|
81
|
+
end
|
82
|
+
|
83
|
+
def queue
|
84
|
+
@queue
|
85
|
+
end
|
86
|
+
|
71
87
|
def skip_compression?
|
72
88
|
@skip_compression
|
73
89
|
end
|
@@ -84,11 +100,11 @@ class BaseLogger
|
|
84
100
|
@skip_submission = value
|
85
101
|
end
|
86
102
|
|
87
|
-
def submit(
|
88
|
-
if @skip_submission || !enabled?
|
103
|
+
def submit(msg)
|
104
|
+
if msg.nil? || @skip_submission || !enabled?
|
89
105
|
true
|
90
106
|
elsif @queue
|
91
|
-
@queue <<
|
107
|
+
@queue << msg
|
92
108
|
true
|
93
109
|
else
|
94
110
|
begin
|
@@ -97,10 +113,10 @@ class BaseLogger
|
|
97
113
|
@url_connection.use_ssl = @url.include?('https')
|
98
114
|
request = Net::HTTP::Post.new(@url_parsed.path)
|
99
115
|
if @skip_compression
|
100
|
-
request.body =
|
116
|
+
request.body = msg
|
101
117
|
else
|
102
118
|
request.add_field('Content-Encoding', 'deflated')
|
103
|
-
request.body = Zlib::Deflate.deflate(
|
119
|
+
request.body = Zlib::Deflate.deflate(msg)
|
104
120
|
end
|
105
121
|
response = @url_connection.request(request)
|
106
122
|
response.code.to_i == 204
|
@@ -119,6 +135,16 @@ class BaseLogger
|
|
119
135
|
@version
|
120
136
|
end
|
121
137
|
|
138
|
+
def self.host_lookup
|
139
|
+
dyno = ENV['DYNO']
|
140
|
+
return dyno unless dyno.nil?
|
141
|
+
begin
|
142
|
+
Socket.gethostname
|
143
|
+
rescue
|
144
|
+
'unknown'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
122
148
|
def self.version_lookup
|
123
149
|
Gem.loaded_specs['resurfaceio-logger'].version.to_s
|
124
150
|
end
|
@@ -1,107 +1,124 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
require 'json'
|
5
5
|
require 'resurfaceio/base_logger'
|
6
|
+
require 'resurfaceio/http_message'
|
7
|
+
require 'resurfaceio/http_rule'
|
8
|
+
require 'resurfaceio/http_rules'
|
6
9
|
|
7
10
|
class HttpLogger < BaseLogger
|
8
11
|
|
9
12
|
AGENT = 'http_logger.rb'.freeze
|
10
13
|
|
11
|
-
|
12
|
-
super(AGENT, options)
|
13
|
-
end
|
14
|
+
@@default_rules = HttpRules.strict_rules
|
14
15
|
|
15
|
-
def
|
16
|
-
|
17
|
-
append_value message, 'request_method', request.request_method
|
18
|
-
append_value message, 'request_url', request.url
|
19
|
-
append_value message, 'response_code', response.status
|
20
|
-
append_request_headers message, request
|
21
|
-
append_request_params message, request
|
22
|
-
append_response_headers message, response
|
23
|
-
append_value message, 'request_body', request_body unless request_body == ''
|
24
|
-
final_response_body = response_body.nil? ? response.body : response_body
|
25
|
-
append_value message, 'response_body', final_response_body unless final_response_body == ''
|
26
|
-
message << ['agent', @agent]
|
27
|
-
message << ['version', @version]
|
28
|
-
message << ['now', now.nil? ? (Time.now.to_f * 1000).floor.to_s : now]
|
29
|
-
JSON.generate message
|
16
|
+
def self.default_rules
|
17
|
+
@@default_rules
|
30
18
|
end
|
31
19
|
|
32
|
-
def
|
33
|
-
|
20
|
+
def self.default_rules=(val)
|
21
|
+
@@default_rules = val.gsub(/^\s*include default\s*$/, '')
|
34
22
|
end
|
35
23
|
|
36
24
|
def self.string_content_type?(s)
|
37
25
|
!s.nil? && !(s =~ /^(text\/(html|plain|xml))|(application\/(json|soap|xml|x-www-form-urlencoded))/i).nil?
|
38
26
|
end
|
39
27
|
|
40
|
-
|
41
|
-
|
42
|
-
def append_request_headers(message, request)
|
43
|
-
respond_to_env = request.respond_to?(:env)
|
44
|
-
if respond_to_env || request.respond_to?(:headers)
|
45
|
-
headers = respond_to_env ? request.env : request.headers
|
46
|
-
headers.each do |name, value|
|
47
|
-
unless value.nil?
|
48
|
-
if name =~ /^CONTENT_TYPE/
|
49
|
-
message << ['request_header:content-type', value]
|
50
|
-
end
|
51
|
-
if name =~ /^HTTP_/
|
52
|
-
message << ["request_header:#{name[5..-1].downcase.tr('_', '-')}", value]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end unless headers.nil?
|
56
|
-
end
|
57
|
-
end
|
28
|
+
def initialize(options = {})
|
29
|
+
super(AGENT, options)
|
58
30
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end unless hash.nil?
|
31
|
+
# read rules from param or defaults
|
32
|
+
if options.respond_to?(:has_key?) && options.has_key?(:rules)
|
33
|
+
@rules = options[:rules].gsub(/^\s*include default\s*$/, @@default_rules)
|
34
|
+
@rules = @@default_rules unless @rules.strip.length > 0
|
35
|
+
else
|
36
|
+
@rules = @@default_rules
|
66
37
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
38
|
+
|
39
|
+
# parse and break rules out by verb
|
40
|
+
prs = HttpRules.parse(@rules)
|
41
|
+
@rules_allow_http_url = prs.select {|r| 'allow_http_url' == r.verb}.length > 0
|
42
|
+
@rules_copy_session_field = prs.select {|r| 'copy_session_field' == r.verb}
|
43
|
+
@rules_remove = prs.select {|r| 'remove' == r.verb}
|
44
|
+
@rules_remove_if = prs.select {|r| 'remove_if' == r.verb}
|
45
|
+
@rules_remove_if_found = prs.select {|r| 'remove_if_found' == r.verb}
|
46
|
+
@rules_remove_unless = prs.select {|r| 'remove_unless' == r.verb}
|
47
|
+
@rules_remove_unless_found = prs.select {|r| 'remove_unless_found' == r.verb}
|
48
|
+
@rules_replace = prs.select {|r| 'replace' == r.verb}
|
49
|
+
@rules_sample = prs.select {|r| 'sample' == r.verb}
|
50
|
+
@rules_stop = prs.select {|r| 'stop' == r.verb}
|
51
|
+
@rules_stop_if = prs.select {|r| 'stop_if' == r.verb}
|
52
|
+
@rules_stop_if_found = prs.select {|r| 'stop_if_found' == r.verb}
|
53
|
+
@rules_stop_unless = prs.select {|r| 'stop_unless' == r.verb}
|
54
|
+
@rules_stop_unless_found = prs.select {|r| 'stop_unless_found' == r.verb}
|
55
|
+
@skip_compression = prs.select {|r| 'skip_compression' == r.verb}.length > 0
|
56
|
+
@skip_submission = prs.select {|r| 'skip_submission' == r.verb}.length > 0
|
57
|
+
|
58
|
+
# finish validating rules
|
59
|
+
raise RuntimeError.new('Multiple sample rules') if @rules_sample.length > 1
|
60
|
+
unless @url.nil? || @url.start_with?('https') || @rules_allow_http_url
|
61
|
+
@enableable = false
|
62
|
+
@enabled = false
|
72
63
|
end
|
73
64
|
end
|
74
65
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
found_content_type = true if name =~ /^content-type/
|
82
|
-
message << ["response_header:#{name}", value]
|
83
|
-
end
|
84
|
-
end unless response.headers.nil?
|
85
|
-
end
|
86
|
-
unless found_content_type || response.content_type.nil?
|
87
|
-
message << ['response_header:content-type', response.content_type]
|
88
|
-
end
|
66
|
+
def rules
|
67
|
+
@rules
|
68
|
+
end
|
69
|
+
|
70
|
+
def log(request, response, response_body = nil, request_body = nil)
|
71
|
+
!enabled? || submit(format(request, response, response_body, request_body))
|
89
72
|
end
|
90
73
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
message << [key, value.to_s]
|
74
|
+
def format(request, response, response_body = nil, request_body = nil, now = nil)
|
75
|
+
details = HttpMessage.build(request, response, response_body, request_body)
|
76
|
+
|
77
|
+
# copy data from session if configured
|
78
|
+
unless @rules_copy_session_field.empty?
|
79
|
+
ssn = request.session
|
80
|
+
if !ssn.nil? && ssn.respond_to?(:keys)
|
81
|
+
@rules_copy_session_field.each do |r|
|
82
|
+
ssn.keys.each {|d| (details << ["session_field:#{d}", ssn[d].to_s]) if r.param1.match(d)}
|
101
83
|
end
|
102
84
|
end
|
103
85
|
end
|
104
|
-
|
86
|
+
|
87
|
+
# quit early based on stop rules if configured
|
88
|
+
@rules_stop.each {|r| details.each {|d| return nil if r.scope.match(d[0])}}
|
89
|
+
@rules_stop_if_found.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
|
90
|
+
@rules_stop_if.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
|
91
|
+
passed = 0
|
92
|
+
@rules_stop_unless_found.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
|
93
|
+
return nil if passed != @rules_stop_unless_found.length
|
94
|
+
passed = 0
|
95
|
+
@rules_stop_unless.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
|
96
|
+
return nil if passed != @rules_stop_unless.length
|
97
|
+
|
98
|
+
# do sampling if configured
|
99
|
+
return nil if !@rules_sample[0].nil? && (rand * 100 >= @rules_sample[0].param1)
|
100
|
+
|
101
|
+
# winnow sensitive details based on remove rules if configured
|
102
|
+
@rules_remove.each {|r| details.delete_if {|d| r.scope.match(d[0])}}
|
103
|
+
@rules_remove_unless_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
|
104
|
+
@rules_remove_if_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
|
105
|
+
@rules_remove_unless.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
|
106
|
+
@rules_remove_if.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
|
107
|
+
return nil if details.empty?
|
108
|
+
|
109
|
+
# mask sensitive details based on replace rules if configured
|
110
|
+
@rules_replace.each {|r| details.each {|d| d[1] = d[1].gsub(r.param1, r.param2) if r.scope.match(d[0])}}
|
111
|
+
|
112
|
+
# remove any details with empty values
|
113
|
+
details.delete_if {|d| '' == d[1]}
|
114
|
+
return nil if details.empty?
|
115
|
+
|
116
|
+
# finish message
|
117
|
+
details << ['now', now.nil? ? (Time.now.to_f * 1000).floor.to_s : now]
|
118
|
+
details << ['agent', @agent]
|
119
|
+
details << ['host', @host]
|
120
|
+
details << ['version', @version]
|
121
|
+
JSON.generate details
|
105
122
|
end
|
106
123
|
|
107
124
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
require 'rack'
|
5
5
|
require 'resurfaceio/http_logger'
|
6
6
|
|
7
7
|
class HttpLoggerForRack # http://rack.rubyforge.org/doc/SPEC.html
|
8
8
|
|
9
|
-
def initialize(app, options={})
|
9
|
+
def initialize(app, options = {})
|
10
10
|
@app = app
|
11
11
|
@logger = HttpLogger.new(options)
|
12
12
|
end
|
@@ -21,7 +21,7 @@ class HttpLoggerForRack # http://rack.rubyforge.org/doc/SPEC.html
|
|
21
21
|
response = Rack::Response.new(body, status, headers)
|
22
22
|
if HttpLogger::string_content_type?(response.content_type)
|
23
23
|
request = Rack::Request.new(env)
|
24
|
-
@logger.
|
24
|
+
@logger.submit(@logger.format(request, response))
|
25
25
|
end
|
26
26
|
end
|
27
27
|
[status, headers, body]
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
require 'resurfaceio/http_logger'
|
5
5
|
|
6
6
|
class HttpLoggerForRails
|
7
7
|
|
8
|
-
def initialize(options={})
|
8
|
+
def initialize(options = {})
|
9
9
|
@logger = HttpLogger.new(options)
|
10
10
|
end
|
11
11
|
|
@@ -15,11 +15,13 @@ class HttpLoggerForRails
|
|
15
15
|
|
16
16
|
def around(controller)
|
17
17
|
yield
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
if @logger.enabled?
|
19
|
+
request = controller.request
|
20
|
+
response = controller.response
|
21
|
+
status = response.status
|
22
|
+
if (status < 300 || status == 302) && HttpLogger::string_content_type?(response.content_type)
|
23
|
+
@logger.submit(@logger.format(request, response))
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class HttpMessage
|
7
|
+
|
8
|
+
def self.build(request, response, response_body = nil, request_body = nil)
|
9
|
+
message = []
|
10
|
+
append_value message, 'request_method', request.request_method unless request.request_method.nil?
|
11
|
+
append_value message, 'request_url', request.url unless request.url.nil?
|
12
|
+
append_value message, 'response_code', response.status unless response.status.nil?
|
13
|
+
append_request_headers message, request
|
14
|
+
append_request_params message, request
|
15
|
+
append_response_headers message, response
|
16
|
+
append_value message, 'request_body', request_body unless request_body == ''
|
17
|
+
final_response_body = response_body.nil? ? response.body : response_body
|
18
|
+
append_value message, 'response_body', final_response_body unless final_response_body == ''
|
19
|
+
return message
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def self.append_request_headers(message, request)
|
25
|
+
respond_to_env = request.respond_to?(:env)
|
26
|
+
if respond_to_env || request.respond_to?(:headers)
|
27
|
+
headers = respond_to_env ? request.env : request.headers
|
28
|
+
headers.each do |name, value|
|
29
|
+
unless value.nil?
|
30
|
+
if name =~ /^CONTENT_TYPE/
|
31
|
+
message << ['request_header:content-type', value]
|
32
|
+
end
|
33
|
+
if name =~ /^HTTP_/
|
34
|
+
message << ["request_header:#{name[5..-1].downcase.tr('_', '-')}", value]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end unless headers.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.append_request_params(message, request)
|
42
|
+
respond_to_env = request.respond_to?(:env)
|
43
|
+
if respond_to_env || request.respond_to?(:form_hash)
|
44
|
+
hash = respond_to_env ? request.env['rack.request.form_hash'] : request.form_hash
|
45
|
+
hash.each do |name, value|
|
46
|
+
append_value message, "request_param:#{name.downcase}", value
|
47
|
+
end unless hash.nil?
|
48
|
+
end
|
49
|
+
if respond_to_env || request.respond_to?(:query_hash)
|
50
|
+
hash = respond_to_env ? request.env['rack.request.query_hash'] : request.query_hash
|
51
|
+
hash.each do |name, value|
|
52
|
+
append_value message, "request_param:#{name.downcase}", value
|
53
|
+
end unless hash.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.append_response_headers(message, response)
|
58
|
+
found_content_type = false
|
59
|
+
if response.respond_to?(:headers)
|
60
|
+
response.headers.each do |name, value|
|
61
|
+
unless value.nil?
|
62
|
+
name = name.downcase
|
63
|
+
found_content_type = true if name =~ /^content-type/
|
64
|
+
message << ["response_header:#{name}", value]
|
65
|
+
end
|
66
|
+
end unless response.headers.nil?
|
67
|
+
end
|
68
|
+
unless found_content_type || response.content_type.nil?
|
69
|
+
message << ['response_header:content-type', response.content_type]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.append_value(message, key, value = nil)
|
74
|
+
unless key.nil?
|
75
|
+
unless value.nil?
|
76
|
+
case value
|
77
|
+
when Array
|
78
|
+
message << [key, value.join]
|
79
|
+
when String
|
80
|
+
message << [key, value]
|
81
|
+
else
|
82
|
+
message << [key, value.to_s]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
message
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
class HttpRequestImpl
|
5
5
|
|
@@ -7,6 +7,7 @@ class HttpRequestImpl
|
|
7
7
|
@form_hash = Hash.new
|
8
8
|
@headers = Hash.new
|
9
9
|
@query_hash = Hash.new
|
10
|
+
@session = Hash.new
|
10
11
|
end
|
11
12
|
|
12
13
|
def add_header(key, value)
|
@@ -40,6 +41,10 @@ class HttpRequestImpl
|
|
40
41
|
@query_hash
|
41
42
|
end
|
42
43
|
|
44
|
+
def session
|
45
|
+
@session
|
46
|
+
end
|
47
|
+
|
43
48
|
attr_accessor :request_method
|
44
49
|
attr_accessor :url
|
45
50
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
|
+
|
4
|
+
class HttpRule
|
5
|
+
|
6
|
+
def initialize(verb, scope = nil, param1 = nil, param2 = nil)
|
7
|
+
@verb = verb
|
8
|
+
@scope = scope
|
9
|
+
@param1 = param1
|
10
|
+
@param2 = param2
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :verb, :scope, :param1, :param2
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
|
+
|
4
|
+
class HttpRules
|
5
|
+
|
6
|
+
def self.debug_rules
|
7
|
+
"allow_http_url\ncopy_session_field /.*/\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.standard_rules
|
11
|
+
%q(/request_header:cookie|response_header:set-cookie/ remove
|
12
|
+
/(request|response)_body|request_param/ replace /[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)/, /x@y.com/
|
13
|
+
/request_body|request_param|response_body/ replace /[0-9\.\-\/]{9,}/, /xyxy/
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.strict_rules
|
18
|
+
%q(/request_url/ replace /([^\?;]+).*/, !\\\\1!
|
19
|
+
/request_body|response_body|request_param:.*|request_header:(?!user-agent).*|response_header:(?!(content-length)|(content-type)).*/ remove
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse(rules)
|
24
|
+
result = []
|
25
|
+
unless rules.nil?
|
26
|
+
rules = rules.gsub(/^\s*include debug\s*$/, debug_rules)
|
27
|
+
rules = rules.gsub(/^\s*include standard\s*$/, standard_rules)
|
28
|
+
rules = rules.gsub(/^\s*include strict\s*$/, strict_rules)
|
29
|
+
rules.each_line do |rule|
|
30
|
+
parsed = parse_rule(rule)
|
31
|
+
result << parsed unless parsed.nil?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_rule(r)
|
38
|
+
if r.nil? || r.match(REGEX_BLANK_OR_COMMENT)
|
39
|
+
nil
|
40
|
+
elsif r.match(REGEX_ALLOW_HTTP_URL)
|
41
|
+
HttpRule.new('allow_http_url')
|
42
|
+
elsif (m = r.match(REGEX_COPY_SESSION_FIELD))
|
43
|
+
HttpRule.new('copy_session_field', nil, parse_regex(r, m[1]))
|
44
|
+
elsif (m = r.match(REGEX_REMOVE))
|
45
|
+
HttpRule.new('remove', parse_regex(r, m[1]))
|
46
|
+
elsif (m = r.match(REGEX_REMOVE_IF))
|
47
|
+
HttpRule.new('remove_if', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
48
|
+
elsif (m = r.match(REGEX_REMOVE_IF_FOUND))
|
49
|
+
HttpRule.new('remove_if_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
50
|
+
elsif (m = r.match(REGEX_REMOVE_UNLESS))
|
51
|
+
HttpRule.new('remove_unless', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
52
|
+
elsif (m = r.match(REGEX_REMOVE_UNLESS_FOUND))
|
53
|
+
HttpRule.new('remove_unless_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
54
|
+
elsif (m = r.match(REGEX_REPLACE))
|
55
|
+
HttpRule.new('replace', parse_regex(r, m[1]), parse_regex_find(r, m[2]), parse_string(r, m[3]))
|
56
|
+
elsif (m = r.match(REGEX_SAMPLE))
|
57
|
+
m1 = m[1].to_i
|
58
|
+
raise RuntimeError.new("Invalid sample percent: #{m1}") if m1 < 1 || m1 > 99
|
59
|
+
HttpRule.new('sample', nil, m1)
|
60
|
+
elsif r.match(REGEX_SKIP_COMPRESSION)
|
61
|
+
HttpRule.new('skip_compression')
|
62
|
+
elsif r.match(REGEX_SKIP_SUBMISSION)
|
63
|
+
HttpRule.new('skip_submission')
|
64
|
+
elsif (m = r.match(REGEX_STOP))
|
65
|
+
HttpRule.new('stop', parse_regex(r, m[1]))
|
66
|
+
elsif (m = r.match(REGEX_STOP_IF))
|
67
|
+
HttpRule.new('stop_if', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
68
|
+
elsif (m = r.match(REGEX_STOP_IF_FOUND))
|
69
|
+
HttpRule.new('stop_if_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
70
|
+
elsif (m = r.match(REGEX_STOP_UNLESS))
|
71
|
+
HttpRule.new('stop_unless', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
72
|
+
elsif (m = r.match(REGEX_STOP_UNLESS_FOUND))
|
73
|
+
HttpRule.new('stop_unless_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
74
|
+
else
|
75
|
+
raise RuntimeError.new("Invalid rule: #{r}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def self.parse_regex(r, regex)
|
82
|
+
s = parse_string(r, regex)
|
83
|
+
raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}") if '*' == s || '+' == s || '?' == s
|
84
|
+
s = "^#{s}" unless s.start_with?('^')
|
85
|
+
s = "#{s}$" unless s.end_with?('$')
|
86
|
+
begin
|
87
|
+
return Regexp.compile(s)
|
88
|
+
rescue RegexpError
|
89
|
+
raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.parse_regex_find(r, regex)
|
94
|
+
begin
|
95
|
+
return Regexp.compile(parse_string(r, regex))
|
96
|
+
rescue RegexpError
|
97
|
+
raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.parse_string(r, expr)
|
102
|
+
%w(~ ! % | /).each do |sep|
|
103
|
+
if (m = expr.match(/^[#{sep}](.*)[#{sep}]$/))
|
104
|
+
m1 = m[1]
|
105
|
+
raise RuntimeError.new("Unescaped separator (#{sep}) in rule: #{r}") if m1.match(/^[#{sep}].*|.*[^\\][#{sep}].*/)
|
106
|
+
return m1.gsub("\\#{sep}", sep)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
raise RuntimeError.new("Invalid expression (#{expr}) in rule: #{r}")
|
110
|
+
end
|
111
|
+
|
112
|
+
REGEX_ALLOW_HTTP_URL = /^\s*allow_http_url\s*(#.*)?$/.freeze
|
113
|
+
REGEX_BLANK_OR_COMMENT = /^\s*([#].*)*$/.freeze
|
114
|
+
REGEX_COPY_SESSION_FIELD = /^\s*copy_session_field\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
115
|
+
REGEX_REMOVE = /^\s*([~!%|\/].+[~!%|\/])\s*remove\s*(#.*)?$/.freeze
|
116
|
+
REGEX_REMOVE_IF = /^\s*([~!%|\/].+[~!%|\/])\s*remove_if\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
117
|
+
REGEX_REMOVE_IF_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*remove_if_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
118
|
+
REGEX_REMOVE_UNLESS = /^\s*([~!%|\/].+[~!%|\/])\s*remove_unless\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
119
|
+
REGEX_REMOVE_UNLESS_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*remove_unless_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
120
|
+
REGEX_REPLACE = /^\s*([~!%|\/].+[~!%|\/])\s*replace[\s]+([~!%|\/].+[~!%|\/]),[\s]+([~!%|\/].*[~!%|\/])\s*(#.*)?$/.freeze
|
121
|
+
REGEX_SAMPLE = /^\s*sample\s+(\d+)\s*(#.*)?$/.freeze
|
122
|
+
REGEX_SKIP_COMPRESSION = /^\s*skip_compression\s*(#.*)?$/.freeze
|
123
|
+
REGEX_SKIP_SUBMISSION = /^\s*skip_submission\s*(#.*)?$/.freeze
|
124
|
+
REGEX_STOP = /^\s*([~!%|\/].+[~!%|\/])\s*stop\s*(#.*)?$/.freeze
|
125
|
+
REGEX_STOP_IF = /^\s*([~!%|\/].+[~!%|\/])\s*stop_if\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
126
|
+
REGEX_STOP_IF_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*stop_if_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
127
|
+
REGEX_STOP_UNLESS = /^\s*([~!%|\/].+[~!%|\/])\s*stop_unless\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
128
|
+
REGEX_STOP_UNLESS_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*stop_unless_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
129
|
+
|
130
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# © 2016-
|
2
|
+
# © 2016-2019 Resurface Labs Inc.
|
3
3
|
|
4
4
|
class UsageLoggers
|
5
5
|
|
6
|
-
@@
|
6
|
+
@@BRICKED = 'true'.eql?(ENV['USAGE_LOGGERS_DISABLE'])
|
7
7
|
|
8
|
-
@@disabled = @@
|
8
|
+
@@disabled = @@BRICKED
|
9
9
|
|
10
10
|
def self.disable
|
11
11
|
@@disabled = true
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.enable
|
15
|
-
@@disabled = false unless @@
|
15
|
+
@@disabled = false unless @@BRICKED
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.enabled?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resurfaceio-logger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RobDickinson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,8 +77,11 @@ files:
|
|
77
77
|
- lib/resurfaceio/http_logger.rb
|
78
78
|
- lib/resurfaceio/http_logger_for_rack.rb
|
79
79
|
- lib/resurfaceio/http_logger_for_rails.rb
|
80
|
+
- lib/resurfaceio/http_message.rb
|
80
81
|
- lib/resurfaceio/http_request_impl.rb
|
81
82
|
- lib/resurfaceio/http_response_impl.rb
|
83
|
+
- lib/resurfaceio/http_rule.rb
|
84
|
+
- lib/resurfaceio/http_rules.rb
|
82
85
|
- lib/resurfaceio/usage_loggers.rb
|
83
86
|
homepage: https://github.com/resurfaceio/logger-ruby
|
84
87
|
licenses:
|
@@ -100,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
103
|
version: '0'
|
101
104
|
requirements: []
|
102
105
|
rubyforge_project:
|
103
|
-
rubygems_version: 2.
|
106
|
+
rubygems_version: 2.7.7
|
104
107
|
signing_key:
|
105
108
|
specification_version: 4
|
106
109
|
summary: Library for usage logging
|