speakeasy_ruby_sdk 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +184 -0
- data/lib/speakeasy_ruby_sdk/config.rb +60 -0
- data/lib/speakeasy_ruby_sdk/har_builder.rb +242 -0
- data/lib/speakeasy_ruby_sdk/http_transaction.rb +122 -0
- data/lib/speakeasy_ruby_sdk/masker.rb +166 -0
- data/lib/speakeasy_ruby_sdk/time.rb +42 -0
- data/lib/speakeasy_ruby_sdk/url_utils.rb +24 -0
- data/lib/speakeasy_ruby_sdk/version.rb +3 -0
- data/lib/speakeasy_ruby_sdk.rb +112 -0
- data/test/bulk_test.rb +176 -0
- data/test/http_test.rb +32 -0
- data/test/masker_test.rb +124 -0
- data/test/testdata/captures_basic_request_and_no_response_body_input.json +15 -0
- data/test/testdata/captures_basic_request_and_no_response_body_output.json +61 -0
- data/test/testdata/captures_basic_request_and_response_input.json +21 -0
- data/test/testdata/captures_basic_request_and_response_output.json +70 -0
- data/test/testdata/captures_basic_request_and_response_with_different_content_types_input.json +22 -0
- data/test/testdata/captures_basic_request_and_response_with_different_content_types_output.json +74 -0
- data/test/testdata/captures_basic_request_and_response_with_no_response_header_set_input.json +21 -0
- data/test/testdata/captures_basic_request_and_response_with_no_response_header_set_output.json +70 -0
- data/test/testdata/captures_basic_request_with_nano_precision_input.json +23 -0
- data/test/testdata/captures_basic_request_with_nano_precision_output.json +70 -0
- data/test/testdata/captures_cookies_input.json +35 -0
- data/test/testdata/captures_cookies_output.json +165 -0
- data/test/testdata/captures_masked_request_response_input.json +73 -0
- data/test/testdata/captures_masked_request_response_output.json +156 -0
- data/test/testdata/captures_no_response_body_when_not_modified_input.json +17 -0
- data/test/testdata/captures_no_response_body_when_not_modified_output.json +60 -0
- data/test/testdata/captures_post_request_with_body_input.json +24 -0
- data/test/testdata/captures_post_request_with_body_output.json +81 -0
- data/test/testdata/captures_query_params_input.json +21 -0
- data/test/testdata/captures_query_params_output.json +75 -0
- data/test/testdata/captures_redirect_input.json +22 -0
- data/test/testdata/captures_redirect_output.json +74 -0
- data/test/testdata/drops_request_and_response_bodies_when_request_body_too_large_input.json +24 -0
- data/test/testdata/drops_request_and_response_bodies_when_request_body_too_large_output.json +81 -0
- data/test/testdata/drops_response_body_when_too_large_input.json +24 -0
- data/test/testdata/drops_response_body_when_too_large_output.json +81 -0
- metadata +240 -0
@@ -0,0 +1,242 @@
|
|
1
|
+
module SpeakeasyRubySdk
|
2
|
+
class HarBuilder
|
3
|
+
|
4
|
+
def initialize api_config
|
5
|
+
@api_config = api_config
|
6
|
+
end
|
7
|
+
|
8
|
+
def construct_creator name, version
|
9
|
+
return {
|
10
|
+
"name": name,
|
11
|
+
"version": version
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def construct_log version, creator, entries, comment
|
16
|
+
return {
|
17
|
+
'log': {
|
18
|
+
"version": version,
|
19
|
+
"creator": creator,
|
20
|
+
"entries": entries,
|
21
|
+
"comment": comment
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def construct_empty_cache
|
28
|
+
return {
|
29
|
+
}
|
30
|
+
end
|
31
|
+
def construct_empty_params
|
32
|
+
return []
|
33
|
+
end
|
34
|
+
|
35
|
+
def construct_query_records query_params
|
36
|
+
query_params.map {|k, v| {
|
37
|
+
"name": k,
|
38
|
+
"value": v
|
39
|
+
} }.sort_by(&lambda{ |h| h[:name] })
|
40
|
+
end
|
41
|
+
|
42
|
+
def construct_response_cookies cookies
|
43
|
+
final_cookies = []
|
44
|
+
if ! cookies.nil?
|
45
|
+
for cookie in cookies
|
46
|
+
new_cookie = {
|
47
|
+
'name': cookie.name,
|
48
|
+
'value': cookie.value,
|
49
|
+
}
|
50
|
+
if cookie.path && cookie.path != '/'
|
51
|
+
new_cookie['path'] = cookie.path
|
52
|
+
end
|
53
|
+
if cookie.domain
|
54
|
+
new_cookie['domain'] = cookie.domain
|
55
|
+
end
|
56
|
+
if cookie.expires
|
57
|
+
new_cookie['expires'] = cookie.expires.strftime("%Y-%m-%dT%H:%M:%S.%NZ")
|
58
|
+
end
|
59
|
+
if cookie.httponly
|
60
|
+
new_cookie['httpOnly'] = cookie.httponly
|
61
|
+
end
|
62
|
+
if cookie.secure
|
63
|
+
new_cookie['secure'] = cookie.secure
|
64
|
+
end
|
65
|
+
final_cookies << new_cookie
|
66
|
+
end
|
67
|
+
end
|
68
|
+
return final_cookies.sort_by(&lambda{ |c| c[:name] })
|
69
|
+
end
|
70
|
+
|
71
|
+
def construct_request_cookies cookies
|
72
|
+
if cookies.nil?
|
73
|
+
return []
|
74
|
+
else
|
75
|
+
return cookies.map{ |cookie|
|
76
|
+
{
|
77
|
+
'name': cookie[0],
|
78
|
+
'value': cookie[1]
|
79
|
+
}
|
80
|
+
}.sort_by(&lambda{ |c| c[:name] })
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def construct_header_records headers
|
85
|
+
final_headers = []
|
86
|
+
for k, v in headers
|
87
|
+
if v.is_a? Array
|
88
|
+
for value in v
|
89
|
+
final_headers << {
|
90
|
+
"name": k,
|
91
|
+
"value": value
|
92
|
+
}
|
93
|
+
end
|
94
|
+
else
|
95
|
+
final_headers << {
|
96
|
+
"name": k,
|
97
|
+
"value": v
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
final_headers.sort_by(&lambda{ |h| h[:name] })
|
102
|
+
end
|
103
|
+
|
104
|
+
def construct_post_data request
|
105
|
+
content_type = request.headers['content-type']
|
106
|
+
if content_type.nil? || content_type.length == 0
|
107
|
+
content_type = "application/octet-stream"
|
108
|
+
end
|
109
|
+
if request.body.empty?
|
110
|
+
return nil
|
111
|
+
elsif (request.headers.include?('content-length')) && (!request.headers['content-length'].nil?) && (request.headers['content-length'].to_i > @api_config.max_capture_size)
|
112
|
+
return {
|
113
|
+
"mimeType": content_type,
|
114
|
+
"text": "--dropped--"
|
115
|
+
}
|
116
|
+
else
|
117
|
+
return {
|
118
|
+
"mimeType": content_type,
|
119
|
+
"text": request.body
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def construct_response_content status, body, headers
|
125
|
+
content_type = headers['content-type']
|
126
|
+
if content_type.nil? || content_type.length == 0
|
127
|
+
content_type = "application/octet-stream"
|
128
|
+
end
|
129
|
+
if status == 304
|
130
|
+
return {
|
131
|
+
"mimeType": content_type,
|
132
|
+
"size": -1
|
133
|
+
}
|
134
|
+
elsif (headers.include?('content-length')) && (!headers['content-length'].nil?) && (headers['content-length'].to_i > @api_config.max_capture_size)
|
135
|
+
return {
|
136
|
+
"mimeType": content_type,
|
137
|
+
"text": "--dropped--",
|
138
|
+
"size": -1
|
139
|
+
}
|
140
|
+
elsif ! headers.include?('content-length')
|
141
|
+
return {
|
142
|
+
"mimeType": content_type,
|
143
|
+
"size": -1
|
144
|
+
}
|
145
|
+
else
|
146
|
+
return {
|
147
|
+
"mimeType": content_type,
|
148
|
+
"text": body,
|
149
|
+
"size": headers['content-length'].to_i
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def calculate_header_size headers
|
155
|
+
raw_headers = ''
|
156
|
+
for k, v in headers
|
157
|
+
if v.is_a? Array
|
158
|
+
for value in v
|
159
|
+
raw_headers += "#{k}: #{value}\r\n"
|
160
|
+
end
|
161
|
+
else
|
162
|
+
raw_headers += "#{k}: #{v}\r\n"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
raw_headers.bytesize
|
166
|
+
end
|
167
|
+
|
168
|
+
def construct_request request
|
169
|
+
req = {
|
170
|
+
"method": request.method,
|
171
|
+
"url": request.url,
|
172
|
+
"httpVersion": request.transaction.protocol,
|
173
|
+
"cookies": self.construct_request_cookies(request.cookies),
|
174
|
+
"headers": self.construct_header_records(request.headers),
|
175
|
+
"queryString": self.construct_query_records(request.query_params),
|
176
|
+
"headersSize": self.calculate_header_size(request.headers),
|
177
|
+
"bodySize": request.content_length.to_i,
|
178
|
+
}
|
179
|
+
if ! self.construct_post_data(request).nil?
|
180
|
+
req["postData"] = self.construct_post_data(request)
|
181
|
+
end
|
182
|
+
req
|
183
|
+
end
|
184
|
+
|
185
|
+
def construct_response response
|
186
|
+
res = {
|
187
|
+
"status": response.status,
|
188
|
+
"statusText": Rack::Utils::HTTP_STATUS_CODES[response.status],
|
189
|
+
"httpVersion": response.transaction.protocol,
|
190
|
+
"cookies": self.construct_response_cookies(response.cookies),
|
191
|
+
"headers": self.construct_header_records(response.headers),
|
192
|
+
"content": self.construct_response_content(response.status, response.body, response.headers),
|
193
|
+
"redirectURL": response.headers.fetch('location', ''),
|
194
|
+
"headersSize": self.calculate_header_size(response.headers)
|
195
|
+
}
|
196
|
+
|
197
|
+
if response.status == 304
|
198
|
+
res["bodySize"] = 0
|
199
|
+
elsif !response.headers.include?('content-length')
|
200
|
+
res["bodySize"] = -1
|
201
|
+
else
|
202
|
+
res["bodySize"] = response.body.bytesize
|
203
|
+
end
|
204
|
+
res
|
205
|
+
end
|
206
|
+
|
207
|
+
def construct_timings
|
208
|
+
return {
|
209
|
+
"send": -1,
|
210
|
+
"wait": -1,
|
211
|
+
"receive": -1,
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
def construct_entries http_transaction
|
216
|
+
entry = {
|
217
|
+
"startedDateTime": http_transaction.time_utils.start_time.strftime("%Y-%m-%dT%H:%M:%S.%NZ"),
|
218
|
+
"time": http_transaction.time_utils.elapsed_time,
|
219
|
+
"request": self.construct_request(http_transaction.request),
|
220
|
+
"response": self.construct_response(http_transaction.response),
|
221
|
+
"serverIPAddress": http_transaction.request.url.hostname,
|
222
|
+
"cache": self.construct_empty_cache,
|
223
|
+
"timings": self.construct_timings
|
224
|
+
}
|
225
|
+
if !http_transaction.port.nil? && http_transaction.port != "80"
|
226
|
+
entry["connection"] = http_transaction.port
|
227
|
+
end
|
228
|
+
return [entry]
|
229
|
+
end
|
230
|
+
|
231
|
+
def construct_har http_transaction
|
232
|
+
creator = construct_creator SpeakeasyRubySdk.to_s, SpeakeasyRubySdk::VERSION
|
233
|
+
version = "1.2"
|
234
|
+
comment = "request capture for #{http_transaction.request.url.to_s}"
|
235
|
+
|
236
|
+
entries = construct_entries http_transaction
|
237
|
+
|
238
|
+
log = construct_log version, creator, entries, comment
|
239
|
+
log.to_json
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'http-cookie'
|
2
|
+
|
3
|
+
module SpeakeasyRubySdk
|
4
|
+
class HttpTransaction
|
5
|
+
|
6
|
+
attr_reader :time_utils, :status, :env, :request, :response, :protocol, :port
|
7
|
+
|
8
|
+
def handle_forward_headers request_headers
|
9
|
+
if request_headers.include? 'x-forwarded-proto' && request_headers['x-forwarded-proto']
|
10
|
+
scheme = request_headers['x-forwarded-proto'].downcase
|
11
|
+
elsif request_headers.include? 'x-forwarded-scheme' && request_headers['x-forwarded-scheme']
|
12
|
+
scheme = request_headers['x-forwarded-scheme'].downcase
|
13
|
+
elsif request_headers.include? 'forwarded' && request_headers['forwarded']
|
14
|
+
forwarded = request_headers['forwarded']
|
15
|
+
protoRegex = Regexp.new(/(?i)(?:proto=)(https|http)/)
|
16
|
+
matches = forwarded.match(protoRegexp)
|
17
|
+
if matches.length > 1
|
18
|
+
scheme = matches[1].downcase
|
19
|
+
end
|
20
|
+
end
|
21
|
+
scheme
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize time_utils, env, status, response_headers, response_body, masker
|
25
|
+
## Setup Data
|
26
|
+
@time_utils = time_utils
|
27
|
+
@status = status
|
28
|
+
@env = env
|
29
|
+
@protocol = env['SERVER_PROTOCOL']
|
30
|
+
@port = env['SERVER_PORT']
|
31
|
+
|
32
|
+
if ! response_body.nil? && response_body.respond_to?(:body)
|
33
|
+
response_body = response_body.body
|
34
|
+
elsif !response_body.nil? && response_body.respond_to?(:join)
|
35
|
+
response_body = response_body.join
|
36
|
+
elsif response_body.nil?
|
37
|
+
response_body = ''
|
38
|
+
end
|
39
|
+
# normalize request headers
|
40
|
+
request_headers = Hash[*env.select {|k,v| k.start_with? 'HTTP_'}
|
41
|
+
.collect {|k,v| [k.sub(/^HTTP_/, ''), v]}
|
42
|
+
.collect {|k,v| [k.split('_').collect(&:downcase).join('-'), v]}
|
43
|
+
.sort
|
44
|
+
.flatten]
|
45
|
+
request_body = env['rack.input'].read
|
46
|
+
request_method = env['REQUEST_METHOD']
|
47
|
+
rack_request = Rack::Request.new(env)
|
48
|
+
response_headers = Hash[response_headers.collect {|k,v| [k.downcase, v] }]
|
49
|
+
scheme = self.handle_forward_headers request_headers
|
50
|
+
if scheme
|
51
|
+
@protocol = scheme
|
52
|
+
end
|
53
|
+
query_params = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
54
|
+
|
55
|
+
## Temporary url var for populating cookiejar.
|
56
|
+
unmasked_url = UrlUtils.resolve_url env
|
57
|
+
|
58
|
+
request_cookies = CGI::Cookie.parse(request_headers['cookie'] || '').map { |cookie| [cookie[0], cookie[1][0]] }
|
59
|
+
response_cookies = HTTP::CookieJar.new()
|
60
|
+
if response_headers.include? 'set-cookie'
|
61
|
+
cookies = response_headers['set-cookie']
|
62
|
+
cookies.map { |cookie| response_cookies.parse(cookie, unmasked_url) }
|
63
|
+
end
|
64
|
+
|
65
|
+
## Begin Masking
|
66
|
+
masked_query_params = masker.mask_query_params unmasked_url.path, query_params
|
67
|
+
|
68
|
+
masked_request_headers = masker.mask_request_headers unmasked_url.path, request_headers
|
69
|
+
masked_response_headers = masker.mask_response_headers unmasked_url.path, response_headers
|
70
|
+
|
71
|
+
request_url = UrlUtils.resolve_url env, masked_query_params
|
72
|
+
|
73
|
+
masked_request_cookies = masker.mask_request_cookies unmasked_url.path, request_cookies
|
74
|
+
masked_response_cookies = masker.mask_response_cookies unmasked_url.path, response_cookies
|
75
|
+
|
76
|
+
masked_request_body = masker.mask_request_body unmasked_url.path, request_body
|
77
|
+
masked_response_body = masker.mask_response_body unmasked_url.path, response_body
|
78
|
+
|
79
|
+
|
80
|
+
## Construct Request Response
|
81
|
+
@request = HttpRequest.new self, rack_request, request_url, request_method, masked_request_headers, masked_query_params, masked_request_body, masked_request_cookies
|
82
|
+
@response = HttpResponse.new self, status, masked_response_headers, masked_response_body, masked_response_cookies
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class HttpRequest
|
87
|
+
attr_reader :transaction, :method, :url, :query_params, :headers, :body, :cookies
|
88
|
+
def initialize transaction, rack_request, request_url, request_method, request_headers, query_params, request_body, request_cookies
|
89
|
+
@transaction = transaction
|
90
|
+
@rack_request = rack_request
|
91
|
+
@url = request_url
|
92
|
+
@query_params = query_params
|
93
|
+
@method = request_method
|
94
|
+
@headers = request_headers
|
95
|
+
@body = request_body
|
96
|
+
@cookies = request_cookies
|
97
|
+
end
|
98
|
+
|
99
|
+
def content_type
|
100
|
+
@rack_request.content_type
|
101
|
+
end
|
102
|
+
|
103
|
+
def content_length
|
104
|
+
if @rack_request.content_length == "0"
|
105
|
+
-1
|
106
|
+
else
|
107
|
+
@rack_request.content_length
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class HttpResponse
|
113
|
+
attr_reader :transaction, :status, :headers, :body, :cookies
|
114
|
+
def initialize transaction, status, response_headers, response_body, response_cookies
|
115
|
+
@transaction = transaction
|
116
|
+
@status = (status.nil? || status == -1) ? 200 : status
|
117
|
+
@headers = response_headers
|
118
|
+
@body = response_body
|
119
|
+
@cookies = response_cookies
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module SpeakeasyRubySdk
|
2
|
+
|
3
|
+
class MaskConfig
|
4
|
+
attr_reader :type, :attributes, :masks, :controller
|
5
|
+
def initialize type, attributes, masks=nil, controller=nil
|
6
|
+
@type = type
|
7
|
+
@contoller = controller
|
8
|
+
@attributes = attributes
|
9
|
+
@masks = masks
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_mask_for_attribute attribute
|
13
|
+
if @masks.nil? || @masks.empty?
|
14
|
+
SpeakeasyRubySdk::Masker::SIMPLE_MASK
|
15
|
+
elsif @masks.length == 1
|
16
|
+
@masks[0]
|
17
|
+
else
|
18
|
+
i = @attributes.find_index{|att| att == attribute}
|
19
|
+
if i > @masks.length
|
20
|
+
SpeakeasyRubySdk::Masker::SIMPLE_MASK
|
21
|
+
else
|
22
|
+
@masks[i]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Masker
|
29
|
+
|
30
|
+
SIMPLE_MASK = '__masked__'
|
31
|
+
SIMPLE_NUMBER_MASK = -12321
|
32
|
+
|
33
|
+
def initialize config
|
34
|
+
@masks = {
|
35
|
+
:query_params => [],
|
36
|
+
:request_headers => [],
|
37
|
+
:response_headers => [],
|
38
|
+
:request_cookies => [],
|
39
|
+
:response_cookies => [],
|
40
|
+
:request_body_string => [],
|
41
|
+
:request_body_number => [],
|
42
|
+
:response_body_string => [],
|
43
|
+
:response_body_number => []
|
44
|
+
}
|
45
|
+
# todo remove this dependency for other mask control
|
46
|
+
@routes = config.routes
|
47
|
+
|
48
|
+
if config.masking
|
49
|
+
config.masking.map {|mask| @masks[mask.type] << mask}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def mask_value mask, attribute, path
|
54
|
+
if !mask.controller.nil?
|
55
|
+
route = @routes.recognize_path path
|
56
|
+
if mask.controller === route[:prefix]
|
57
|
+
return mask.get_mask_for_attribute attribute
|
58
|
+
else
|
59
|
+
return value;
|
60
|
+
end
|
61
|
+
else
|
62
|
+
return mask.get_mask_for_attribute attribute
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def mask_pair masking_key, path, attribute, value
|
67
|
+
masked_value = value
|
68
|
+
if @masks.include? masking_key
|
69
|
+
masks = @masks[masking_key]
|
70
|
+
for mask in masks
|
71
|
+
if mask.attributes.include? attribute.to_s.downcase
|
72
|
+
masked_value = mask_value mask, attribute, path
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return masked_value
|
77
|
+
end
|
78
|
+
|
79
|
+
def mask_dict masking_key, path, hash_map
|
80
|
+
masked_dict = {}
|
81
|
+
for key, value in hash_map
|
82
|
+
masked_dict[key] = mask_pair masking_key, path, key, value
|
83
|
+
if value.is_a? Array
|
84
|
+
masked_dict[key] = value.map { |v| mask_pair masking_key, path, key, v }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
masked_dict
|
88
|
+
end
|
89
|
+
|
90
|
+
def mask_query_params path, query_params
|
91
|
+
mask_dict :query_params, path, query_params
|
92
|
+
end
|
93
|
+
|
94
|
+
def mask_request_headers path, headers
|
95
|
+
mask_dict :request_headers, path, headers
|
96
|
+
end
|
97
|
+
|
98
|
+
def mask_response_headers path, headers
|
99
|
+
mask_dict :response_headers, path, headers
|
100
|
+
end
|
101
|
+
|
102
|
+
def mask_request_cookies path, cookies
|
103
|
+
mask_dict :request_cookies, path, cookies
|
104
|
+
end
|
105
|
+
|
106
|
+
def mask_response_cookies path, cookies
|
107
|
+
for cookie in cookies
|
108
|
+
key = cookie.name
|
109
|
+
value = cookie.value
|
110
|
+
masked_value = self.mask_pair :response_cookies, path, key, value
|
111
|
+
cookie.value = masked_value
|
112
|
+
end
|
113
|
+
cookies
|
114
|
+
end
|
115
|
+
|
116
|
+
def mask_body_string masking_key_prefix, path, body
|
117
|
+
masking_key = "#{masking_key_prefix}_string".to_sym
|
118
|
+
|
119
|
+
masked_body = body
|
120
|
+
if @masks.include? masking_key
|
121
|
+
for mask in @masks[masking_key]
|
122
|
+
for attribute in mask.attributes
|
123
|
+
|
124
|
+
regex_string = Regexp.new "(\"#{attribute}\": *)(\".*?[^\\\\]\")( *[, \\n\\r}]?)"
|
125
|
+
|
126
|
+
matches = body.match(regex_string)
|
127
|
+
if matches
|
128
|
+
masked_body = masked_body.gsub(regex_string, "#{matches[1]}\"#{mask.get_mask_for_attribute(attribute)}\"#{matches[3]}")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
masked_body
|
134
|
+
end
|
135
|
+
def mask_body_number masking_key_prefix, path, body
|
136
|
+
masking_key = "#{masking_key_prefix}_number".to_sym
|
137
|
+
|
138
|
+
masked_body = body
|
139
|
+
if @masks.include? masking_key
|
140
|
+
for mask in @masks[masking_key]
|
141
|
+
for attribute in mask.attributes
|
142
|
+
regex_string = Regexp.new "(\"#{attribute}\": *)(-?[0-9]+\\.?[0-9]*)( *[, \\n\\r}]?)"
|
143
|
+
matches = body.match(regex_string)
|
144
|
+
if matches
|
145
|
+
masked_body = masked_body.gsub(regex_string, "#{matches[1]}#{mask.get_mask_for_attribute(attribute)}#{matches[3]}")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
masked_body
|
151
|
+
end
|
152
|
+
|
153
|
+
def mask_request_body path, body
|
154
|
+
masked_body = mask_body_string 'request_body', path, body
|
155
|
+
mask_body_number 'request_body', path, masked_body
|
156
|
+
end
|
157
|
+
|
158
|
+
def mask_response_body path, body
|
159
|
+
masked_body = mask_body_string 'response_body', path, body
|
160
|
+
mask_body_number 'response_body', path, masked_body
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SpeakeasyRubySdk
|
2
|
+
class TimeUtils
|
3
|
+
## Convenience class to optionally override times
|
4
|
+
## In test environment
|
5
|
+
def initialize(start_time=nil, elapsed_time=nil)
|
6
|
+
@start_time = start_time
|
7
|
+
@elapsed_time = elapsed_time
|
8
|
+
@end_time = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def start_time
|
12
|
+
now
|
13
|
+
end
|
14
|
+
|
15
|
+
def now
|
16
|
+
if @start_time.nil?
|
17
|
+
@start_time
|
18
|
+
@start_time = Time.now
|
19
|
+
return @start_time
|
20
|
+
else
|
21
|
+
return @start_time
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_end_time
|
26
|
+
@end_time = Time.now
|
27
|
+
end
|
28
|
+
|
29
|
+
def elapsed_time
|
30
|
+
if !@elapsed_time.nil?
|
31
|
+
return @elapsed_time
|
32
|
+
else
|
33
|
+
@elapsed_time = time_difference(@end_time, @start_time)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def time_difference end_time, start_time
|
38
|
+
((end_time - start_time)).to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'uri'
|
2
|
+
module SpeakeasyRubySdk
|
3
|
+
module UrlUtils
|
4
|
+
def self.resolve_url env, query_params=nil
|
5
|
+
if env.include?('SERVER_PORT') && env['SERVER_PORT'] != "80"
|
6
|
+
request_uri = "#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['PATH_INFO']}"
|
7
|
+
else
|
8
|
+
request_uri = "#{env['SERVER_NAME']}#{env['PATH_INFO']}"
|
9
|
+
end
|
10
|
+
|
11
|
+
if request_uri.include? '?'
|
12
|
+
request_uri = request_uri.split('?')[0]
|
13
|
+
end
|
14
|
+
scheme = env['rack.url_scheme']
|
15
|
+
host = env['HTTP_HOST']
|
16
|
+
if !query_params.nil? && !query_params.empty?
|
17
|
+
updated_query_string = URI.encode_www_form query_params
|
18
|
+
URI("#{scheme}://#{host}#{request_uri}?#{updated_query_string}")
|
19
|
+
else
|
20
|
+
URI("#{scheme}://#{host}#{request_uri}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|