speakeasy_ruby_sdk 0.0.2
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 +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
|