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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +184 -0
  4. data/lib/speakeasy_ruby_sdk/config.rb +60 -0
  5. data/lib/speakeasy_ruby_sdk/har_builder.rb +242 -0
  6. data/lib/speakeasy_ruby_sdk/http_transaction.rb +122 -0
  7. data/lib/speakeasy_ruby_sdk/masker.rb +166 -0
  8. data/lib/speakeasy_ruby_sdk/time.rb +42 -0
  9. data/lib/speakeasy_ruby_sdk/url_utils.rb +24 -0
  10. data/lib/speakeasy_ruby_sdk/version.rb +3 -0
  11. data/lib/speakeasy_ruby_sdk.rb +112 -0
  12. data/test/bulk_test.rb +176 -0
  13. data/test/http_test.rb +32 -0
  14. data/test/masker_test.rb +124 -0
  15. data/test/testdata/captures_basic_request_and_no_response_body_input.json +15 -0
  16. data/test/testdata/captures_basic_request_and_no_response_body_output.json +61 -0
  17. data/test/testdata/captures_basic_request_and_response_input.json +21 -0
  18. data/test/testdata/captures_basic_request_and_response_output.json +70 -0
  19. data/test/testdata/captures_basic_request_and_response_with_different_content_types_input.json +22 -0
  20. data/test/testdata/captures_basic_request_and_response_with_different_content_types_output.json +74 -0
  21. data/test/testdata/captures_basic_request_and_response_with_no_response_header_set_input.json +21 -0
  22. data/test/testdata/captures_basic_request_and_response_with_no_response_header_set_output.json +70 -0
  23. data/test/testdata/captures_basic_request_with_nano_precision_input.json +23 -0
  24. data/test/testdata/captures_basic_request_with_nano_precision_output.json +70 -0
  25. data/test/testdata/captures_cookies_input.json +35 -0
  26. data/test/testdata/captures_cookies_output.json +165 -0
  27. data/test/testdata/captures_masked_request_response_input.json +73 -0
  28. data/test/testdata/captures_masked_request_response_output.json +156 -0
  29. data/test/testdata/captures_no_response_body_when_not_modified_input.json +17 -0
  30. data/test/testdata/captures_no_response_body_when_not_modified_output.json +60 -0
  31. data/test/testdata/captures_post_request_with_body_input.json +24 -0
  32. data/test/testdata/captures_post_request_with_body_output.json +81 -0
  33. data/test/testdata/captures_query_params_input.json +21 -0
  34. data/test/testdata/captures_query_params_output.json +75 -0
  35. data/test/testdata/captures_redirect_input.json +22 -0
  36. data/test/testdata/captures_redirect_output.json +74 -0
  37. data/test/testdata/drops_request_and_response_bodies_when_request_body_too_large_input.json +24 -0
  38. data/test/testdata/drops_request_and_response_bodies_when_request_body_too_large_output.json +81 -0
  39. data/test/testdata/drops_response_body_when_too_large_input.json +24 -0
  40. data/test/testdata/drops_response_body_when_too_large_output.json +81 -0
  41. metadata +240 -0
@@ -0,0 +1,112 @@
1
+ require_relative 'speakeasy_ruby_sdk/version'
2
+ require_relative 'speakeasy_ruby_sdk/config'
3
+ require_relative 'speakeasy_ruby_sdk/url_utils'
4
+ require_relative 'speakeasy_ruby_sdk/masker'
5
+ require_relative 'speakeasy_ruby_sdk/http_transaction'
6
+ require_relative 'speakeasy_ruby_sdk/har_builder'
7
+ require_relative 'speakeasy_ruby_sdk/time'
8
+
9
+ require "delegate"
10
+
11
+ require 'speakeasy_pb'
12
+ include Ingest
13
+ include Embedaccesstoken
14
+
15
+ module SpeakeasyRubySdk
16
+ class RouteWrapper < SimpleDelegator
17
+ def path
18
+ super.spec.to_s
19
+ end
20
+ end
21
+
22
+ class Middleware
23
+ attr_accessor :ingest_client
24
+ attr_reader :config
25
+
26
+ def initialize(app, config=nil)
27
+ @config = Config.default.merge config
28
+ validation_errors = @config.validate
29
+ if validation_errors.length > 0
30
+ raise Exception.new validation_errors
31
+ end
32
+ @app = app
33
+ @masker = SpeakeasyRubySdk::Masker.new @config
34
+ credentials = GRPC::Core::ChannelCredentials.new()
35
+ @ingest_client = Ingest::IngestService::Stub.new(@config.ingestion_server_url, credentials)
36
+
37
+ end
38
+
39
+ def call(env)
40
+ if env.include? 'time_utils'
41
+ time_utils = env['time_utils']
42
+ else
43
+ time_utils = TimeUtils.new
44
+ end
45
+
46
+ status, response_headers, response_body = @app.call(env)
47
+
48
+ ## If we are not in test, record the end time after calling the app
49
+ if !env.include? 'time_utils'
50
+ time_utils.set_end_time
51
+ end
52
+
53
+ http_transaction = HttpTransaction.new time_utils, env, status, response_headers, response_body, @masker
54
+
55
+ path_hint = ''
56
+ # todo - support other frameworks than rails
57
+ if @config.routes
58
+ req = ActionDispatch::Request.new(env)
59
+ found_route = nil
60
+ @config.routes.router.recognize(req) do |route, params|
61
+ found_route = route
62
+ end
63
+ if ! found_route.nil?
64
+ path_hint = RouteWrapper.new(found_route).path
65
+ end
66
+ end
67
+ if ! env[:path_hint].nil?
68
+ path_hint = env[:path_hint]
69
+ end
70
+
71
+ har_builder = HarBuilder.new @config
72
+ har = har_builder.construct_har http_transaction
73
+
74
+ request = Ingest::IngestRequest.new
75
+ request.api_id = @config.api_id
76
+ request.path_hint = path_hint
77
+ request.version_id = @config.version_id
78
+ if env.include? :customer_id
79
+ request.customer_id = env[:customer_id].to_s
80
+ end
81
+ request.har = har
82
+
83
+ metadata = {"x-api-key": @config.api_key}
84
+ response = @ingest_client.ingest(request, metadata: metadata)
85
+
86
+ [status, response_headers, response_body]
87
+ end
88
+ end
89
+
90
+ def self.get_embedded_access_token key, operator, value, config=nil
91
+ if config.nil?
92
+ working_config = Middleware::Config.default
93
+ else
94
+ working_config = Middleware::Config.default.merge config
95
+ end
96
+
97
+ credentials = GRPC::Core::ChannelCredentials.new()
98
+ embed_client = Embedaccesstoken::EmbedAccessTokenService::Stub.new(working_config.ingestion_server_url, credentials)
99
+ request = Embedaccesstoken::EmbedAccessTokenRequest.new
100
+
101
+ filter = Embedaccesstoken::EmbedAccessTokenRequest::Filter.new
102
+ filter.key = key
103
+ filter.operator = operator
104
+ filter.value = value
105
+
106
+ request.filters.push filter
107
+
108
+ metadata = {"x-api-key": working_config.api_key}
109
+ response = embed_client.get(request, metadata: metadata)
110
+
111
+ end
112
+ end
data/test/bulk_test.rb ADDED
@@ -0,0 +1,176 @@
1
+ require 'test/unit'
2
+ require 'rack/test'
3
+ require 'json'
4
+ require 'ostruct'
5
+ require 'spy'
6
+ require 'timecop'
7
+ require_relative '../lib/speakeasy_ruby_sdk'
8
+
9
+ require 'speakeasy_pb'
10
+ include Ingest
11
+
12
+ def add_http_prefix header_dict
13
+ modified_headers = {}
14
+ for k, v in header_dict
15
+ modified_headers["HTTP_#{k}"] = v
16
+ end
17
+ modified_headers
18
+ end
19
+
20
+ def header_to_dict headers
21
+ modified_headers = {}
22
+ for d in headers
23
+ key = d['key']
24
+ if d['values'].length > 1
25
+ modified_headers[key] = d['values']
26
+ else
27
+ modified_headers[key] = d['values'][0]
28
+ end
29
+ end
30
+ modified_headers
31
+ end
32
+
33
+ class BulkTest < Test::Unit::TestCase
34
+ include Rack::Test::Methods
35
+
36
+ def test_bulk
37
+ file_pairs = []
38
+ ## Find all test files, and pair input and outputs
39
+ Dir.foreach("./test/testdata") do |file_name|
40
+ if file_name == "." || file_name == ".."
41
+ next
42
+ end
43
+ infile = nil
44
+ outfile = nil
45
+ if file_name.end_with?("input.json")
46
+ prefix_name = file_name.slice(0..-11)
47
+ output_filename = "#{prefix_name}output.json"
48
+ pair = OpenStruct.new
49
+ pair.infile = "./test/testdata/#{file_name}"
50
+ pair.outfile = "./test/testdata/#{output_filename}"
51
+ file_pairs << pair
52
+ end
53
+ end
54
+ for pair in file_pairs
55
+ input = JSON.parse(File.read(pair.infile))
56
+ puts "Testing #{pair.infile}, #{pair.outfile}"
57
+ config = {api_id: '123', version_id: '1.0'}
58
+
59
+ ## Parse Masking Inputs
60
+ masking = []
61
+ if input['args'].include? 'query_string_masks'
62
+ for k, v in input['args']['query_string_masks']
63
+ masking << SpeakeasyRubySdk::MaskConfig.new(:query_params, [k], [v])
64
+ end
65
+ end
66
+ if input['args'].include? 'request_header_masks'
67
+ for k, v in input['args']['request_header_masks']
68
+ masking << SpeakeasyRubySdk::MaskConfig.new(:request_headers, [k], [v])
69
+ end
70
+ end
71
+ if input['args'].include? 'request_cookie_masks'
72
+ for k, v in input['args']['request_cookie_masks']
73
+ masking << SpeakeasyRubySdk::MaskConfig.new(:request_cookies, [k], [v])
74
+ end
75
+ end
76
+ if input['args'].include? 'request_field_masks_string'
77
+ for k, v in input['args']['request_field_masks_string']
78
+ masking << SpeakeasyRubySdk::MaskConfig.new(:request_body_string, [k], [v])
79
+ end
80
+ end
81
+ if input['args'].include? 'request_field_masks_number'
82
+ for k, v in input['args']['request_field_masks_number']
83
+ masking << SpeakeasyRubySdk::MaskConfig.new(:request_body_number, [k], [v])
84
+ end
85
+ end
86
+ if input['args'].include? 'response_header_masks'
87
+ for k, v in input['args']['response_header_masks']
88
+ masking << SpeakeasyRubySdk::MaskConfig.new(:response_headers, [k], [v])
89
+ end
90
+ end
91
+ if input['args'].include? 'response_cookie_masks'
92
+ for k, v in input['args']['response_cookie_masks']
93
+ masking << SpeakeasyRubySdk::MaskConfig.new(:response_cookies, [k], [v])
94
+ end
95
+ end
96
+ if input['args'].include? 'response_field_masks_string'
97
+ for k, v in input['args']['response_field_masks_string']
98
+ masking << SpeakeasyRubySdk::MaskConfig.new(:response_body_string, [k], [v])
99
+ end
100
+ end
101
+ if input['args'].include? 'response_field_masks_number'
102
+ for k, v in input['args']['response_field_masks_number']
103
+ masking << SpeakeasyRubySdk::MaskConfig.new(:response_body_number, [k], [v])
104
+ end
105
+ end
106
+
107
+ config[:masking] = masking
108
+
109
+
110
+ if input['fields'].include? 'max_capture_size'
111
+ config[:max_capture_size] = input['fields']['max_capture_size']
112
+ end
113
+
114
+ response_status = input['args']['response_status']
115
+
116
+ if input['args'].include? 'response_headers'
117
+ input['args']['response_headers'] = header_to_dict(input['args']['response_headers'])
118
+ else
119
+ input['args']['response_headers'] = {}
120
+ end
121
+
122
+ def simple_app input
123
+ ->(_env) { [input['args']['response_status'], input['args']['response_headers'], input['args']['response_body']]}
124
+ end
125
+
126
+ opts = {
127
+ "SERVER_PROTOCOL" => 'HTTP/1.1'
128
+ }
129
+ if input['args'].include? 'method'
130
+ opts[:method] = input['args']['method']
131
+ end
132
+ if input['args'].include? 'headers'
133
+ opts = opts.merge add_http_prefix(header_to_dict(input['args']['headers']))
134
+ end
135
+
136
+ if input['args'].include? 'request_start_time'
137
+ start_time = Time.parse(input['args']['request_start_time'])
138
+ else
139
+ start_time = Time.utc(2020, 1, 1, 0, 0, 0)
140
+ end
141
+
142
+ if input['args'].include? 'elapsed_time'
143
+ elapsed_time = input['args']['elapsed_time']
144
+ else
145
+ elapsed_time = 1
146
+ end
147
+
148
+ opts['time_utils'] = SpeakeasyRubySdk::TimeUtils.new(start_time, elapsed_time)
149
+
150
+ ## override Time.now for the cookie maxTime handling
151
+ Timecop.freeze(2020, 1, 1, 0, 0, 0)
152
+
153
+ if input['args'].include? 'body'
154
+ rack_input = StringIO.open(input['args']['body'])
155
+ opts[:input] = rack_input
156
+ end
157
+ request = Rack::MockRequest.env_for(input['args']['url'], opts)
158
+
159
+ subject = SpeakeasyRubySdk::Middleware.new(simple_app(input), config)
160
+ subject.ingest_client = Spy.mock(Ingest::IngestService::Stub)
161
+
162
+ spy = Spy.on(subject.ingest_client, :ingest).and_return(nil)
163
+
164
+ status, _headers, _response = subject.call(request)
165
+
166
+ har = JSON.parse(spy.calls.first.args[0].har)
167
+ output = JSON.parse(File.read(pair.outfile))
168
+
169
+ assert_equal(har, output)
170
+
171
+ ## reset time mock
172
+ Timecop.return
173
+ end
174
+ end
175
+
176
+ end
data/test/http_test.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'test/unit'
2
+ require 'rack/test'
3
+ require 'json'
4
+ require_relative '../lib/speakeasy_ruby_sdk'
5
+
6
+ class BasicHttpTest < Test::Unit::TestCase
7
+ include Rack::Test::Methods
8
+
9
+ def simple_app
10
+ ->(_env) { [200, { 'content-type' => 'text/javascript' }, ActionDispatch::Response::RackBody.new(['All responses are OK'])] }
11
+ end
12
+
13
+ def fuller_app
14
+ ->(_env) { [200, {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0", "X-Content-Type-Options"=>"nosniff", "X-Download-Options"=>"noopen", "X-Permitted-Cross-Domain-Policies"=>"none", "Referrer-Policy"=>"strict-origin-when-cross-origin", "Content-Type"=>"application/json; charset=utf-8"}, ActionDispatch::Response::RackBody.new]}
15
+ end
16
+
17
+ def test_response_is_unchanged
18
+ request = Rack::MockRequest.env_for('/', method: :get)
19
+
20
+ subject = SpeakeasyRubySdk::Middleware.new(simple_app)
21
+ status, _headers, _response = subject.call(request)
22
+ assert_equal status, 200
23
+ end
24
+
25
+ def test_full_request
26
+ request = Rack::MockRequest.env_for('/', method: :get)
27
+ subject = SpeakeasyRubySdk::Middleware.new(fuller_app)
28
+ status, _headers, _response = subject.call(request)
29
+ assert_equal status, 200
30
+ end
31
+
32
+ end
@@ -0,0 +1,124 @@
1
+ require 'test/unit'
2
+ require 'rack/test'
3
+ require 'json'
4
+ require_relative '../lib/speakeasy_ruby_sdk'
5
+
6
+ class MaskerTest < Test::Unit::TestCase
7
+ include Rack::Test::Methods
8
+
9
+ def self.init_masker config
10
+ config = SpeakeasyRubySdk::Middleware::Config.default.merge config
11
+ SpeakeasyRubySdk::Masker.new config
12
+ end
13
+
14
+ def test_mask_query_param_simple
15
+ masker_config = {
16
+ routes: nil,
17
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:query_params, 'carrot')]
18
+ }
19
+ masker = MaskerTest::init_masker(masker_config)
20
+
21
+ query_params = {'carrot': 'stick', 'parsnip': 'triangular'}
22
+
23
+ masked_params = masker.mask_query_params '', query_params
24
+
25
+ assert_equal(SpeakeasyRubySdk::Masker::SIMPLE_MASK, masked_params[:carrot])
26
+ assert_equal(query_params[:parsnip], masked_params[:parsnip]) # Should be unchanged
27
+ end
28
+
29
+ def test_mask_query_param_multiple_attrs
30
+ masker_config = {
31
+ routes: nil,
32
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:query_params, ['carrot', 'parsnip', 'unknown'])]
33
+ }
34
+ masker = MaskerTest::init_masker(masker_config)
35
+
36
+ query_params = {'carrot': 'stick', 'parsnip': 'triangular'}
37
+
38
+ masked_params = masker.mask_query_params '', query_params
39
+
40
+ assert_equal(SpeakeasyRubySdk::Masker::SIMPLE_MASK, masked_params[:carrot])
41
+ assert_equal(SpeakeasyRubySdk::Masker::SIMPLE_MASK, masked_params[:parsnip])
42
+ end
43
+
44
+ def test_mask_query_param_custom_mask
45
+ masker_config = {
46
+ routes: nil,
47
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:query_params, ['carrot'], ['__MASK__'])]
48
+ }
49
+ masker = MaskerTest::init_masker(masker_config)
50
+
51
+ query_params = {'carrot': 'stick', 'parsnip': 'triangular'}
52
+
53
+ masked_params = masker.mask_query_params '', query_params
54
+
55
+ assert_equal("__MASK__", masked_params[:carrot])
56
+ assert_equal(query_params[:parsnip], masked_params[:parsnip]) # Should be unchanged
57
+ end
58
+
59
+
60
+ def test_mask_response_cookies_simple
61
+ masker_config = {
62
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:response_cookies, ['magic'])]
63
+ }
64
+ masker = MaskerTest::init_masker(masker_config)
65
+
66
+ response_cookies = HTTP::Cookie.parse("magic=itsnotmagic; path=/; max-age=86400; SameSite=Lax", 'http://localhost:8000')
67
+
68
+ masked_cookies = masker.mask_response_cookies '', response_cookies
69
+
70
+ first_cookie = masked_cookies[0]
71
+
72
+ assert_equal(SpeakeasyRubySdk::Masker::SIMPLE_MASK, first_cookie.value)
73
+
74
+ end
75
+
76
+ def test_mask_body_string_simple
77
+ masker_config = {
78
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:request_body_string, ['sign_in'])]
79
+ }
80
+ masker = MaskerTest::init_masker(masker_config)
81
+
82
+ body = {'sign_in': 'wiley', 'nobody': 'somebody'}.to_json
83
+
84
+ masked_body = masker.mask_request_body('', body)
85
+ assert !masked_body.include?('wiley')
86
+ assert masked_body.include?('somebody')
87
+ end
88
+
89
+ def test_mask_body_number_simple
90
+ masker_config = {
91
+ masking: [SpeakeasyRubySdk::MaskConfig.new(:request_body_number, ['sign_in'])]
92
+ }
93
+ masker = MaskerTest::init_masker(masker_config)
94
+
95
+ body = {
96
+ 'sign_in': 5476,
97
+ 'nobody': 'somebody'
98
+ }.to_json
99
+ masked_body = masker.mask_request_body('', body)
100
+ assert ! masked_body.include?('5476')
101
+ assert masked_body.include?('somebody')
102
+ end
103
+
104
+ def test_mask_body_number_and_string
105
+ masker_config = {
106
+ masking: [
107
+ SpeakeasyRubySdk::MaskConfig.new(:request_body_number, ['attr1']),
108
+ SpeakeasyRubySdk::MaskConfig.new(:request_body_string, ['attr2']),
109
+ ]
110
+ }
111
+ masker = MaskerTest::init_masker(masker_config)
112
+
113
+ body = {
114
+ 'attr1': 5476,
115
+ 'attr2': 'somebody',
116
+ 'attr3': 'keep it'
117
+ }.to_json
118
+
119
+ masked_body = masker.mask_request_body('', body)
120
+ assert !masked_body.include?('5476')
121
+ assert !masked_body.include?('somebody')
122
+ assert masked_body.include?('keep it')
123
+ end
124
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "captures basic request and no response body",
3
+ "fields": {
4
+ "max_capture_size": 9437184
5
+ },
6
+ "args": {
7
+ "method": "GET",
8
+ "url": "http://test.com:8080/test",
9
+ "headers": [
10
+ { "key": "Host", "values": ["test.com"] },
11
+ { "key": "Accept-Encoding", "values": ["gzip, deflate"] },
12
+ { "key": "Connection", "values": ["close"] }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "log": {
3
+ "version": "1.2",
4
+ "creator": {
5
+ "name": "SpeakeasyRubySdk",
6
+ "version": "0.0.1"
7
+ },
8
+ "entries": [
9
+ {
10
+ "startedDateTime": "2020-01-01T00:00:00.000000000Z",
11
+ "time": 1,
12
+ "request": {
13
+ "method": "GET",
14
+ "url": "http://test.com:8080/test",
15
+ "httpVersion": "HTTP/1.1",
16
+ "cookies": [],
17
+ "headers": [
18
+ {
19
+ "name": "accept-encoding",
20
+ "value": "gzip, deflate"
21
+ },
22
+ {
23
+ "name": "connection",
24
+ "value": "close"
25
+ },
26
+ {
27
+ "name": "host",
28
+ "value": "test.com"
29
+ }
30
+ ],
31
+ "queryString": [],
32
+ "headersSize": 67,
33
+ "bodySize": -1
34
+ },
35
+ "response": {
36
+ "status": 200,
37
+ "statusText": "OK",
38
+ "httpVersion": "HTTP/1.1",
39
+ "cookies": [],
40
+ "headers": [],
41
+ "content": {
42
+ "size": -1,
43
+ "mimeType": "application/octet-stream"
44
+ },
45
+ "redirectURL": "",
46
+ "headersSize": 0,
47
+ "bodySize": -1
48
+ },
49
+ "cache": {},
50
+ "timings": {
51
+ "send": -1,
52
+ "wait": -1,
53
+ "receive": -1
54
+ },
55
+ "serverIPAddress": "test.com",
56
+ "connection": "8080"
57
+ }
58
+ ],
59
+ "comment": "request capture for http://test.com:8080/test"
60
+ }
61
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "captures basic request and response",
3
+ "fields": {
4
+ "max_capture_size": 9437184
5
+ },
6
+ "args": {
7
+ "method": "GET",
8
+ "url": "http://test.com/test",
9
+ "response_status": 200,
10
+ "response_body": "test",
11
+ "headers": [
12
+ { "key": "Host", "values": ["test.com"] },
13
+ { "key": "Accept-Encoding", "values": ["gzip, deflate"] },
14
+ { "key": "Connection", "values": ["close"] }
15
+ ],
16
+ "response_headers": [
17
+ { "key": "Content-Type", "values": ["text/html; charset=utf-8"] },
18
+ { "key": "Content-Length", "values": ["4"] }
19
+ ]
20
+ }
21
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "log": {
3
+ "version": "1.2",
4
+ "creator": {
5
+ "name": "SpeakeasyRubySdk",
6
+ "version": "0.0.1"
7
+ },
8
+ "entries": [
9
+ {
10
+ "startedDateTime": "2020-01-01T00:00:00.000000000Z",
11
+ "time": 1,
12
+ "request": {
13
+ "method": "GET",
14
+ "url": "http://test.com/test",
15
+ "httpVersion": "HTTP/1.1",
16
+ "cookies": [],
17
+ "headers": [
18
+ {
19
+ "name": "accept-encoding",
20
+ "value": "gzip, deflate"
21
+ },
22
+ {
23
+ "name": "connection",
24
+ "value": "close"
25
+ },
26
+ {
27
+ "name": "host",
28
+ "value": "test.com"
29
+ }
30
+ ],
31
+ "queryString": [],
32
+ "headersSize": 67,
33
+ "bodySize": -1
34
+ },
35
+ "response": {
36
+ "status": 200,
37
+ "statusText": "OK",
38
+ "httpVersion": "HTTP/1.1",
39
+ "cookies": [],
40
+ "headers": [
41
+ {
42
+ "name": "content-length",
43
+ "value": "4"
44
+ },
45
+ {
46
+ "name": "content-type",
47
+ "value": "text/html; charset=utf-8"
48
+ }
49
+ ],
50
+ "content": {
51
+ "size": 4,
52
+ "mimeType": "text/html; charset=utf-8",
53
+ "text": "test"
54
+ },
55
+ "redirectURL": "",
56
+ "headersSize": 59,
57
+ "bodySize": 4
58
+ },
59
+ "cache": {},
60
+ "timings": {
61
+ "send": -1,
62
+ "wait": -1,
63
+ "receive": -1
64
+ },
65
+ "serverIPAddress": "test.com"
66
+ }
67
+ ],
68
+ "comment": "request capture for http://test.com/test"
69
+ }
70
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "captures basic request and response with different content types",
3
+ "fields": {
4
+ "max_capture_size": 9437184
5
+ },
6
+ "args": {
7
+ "method": "GET",
8
+ "url": "http://test.com/test",
9
+ "headers": [
10
+ { "key": "Content-Type", "values": ["application/json"] },
11
+ { "key": "Host", "values": ["test.com"] },
12
+ { "key": "Accept-Encoding", "values": ["gzip, deflate"] },
13
+ { "key": "Connection", "values": ["close"] }
14
+ ],
15
+ "response_status": -1,
16
+ "response_body": "test",
17
+ "response_headers": [
18
+ { "key": "Content-Type", "values": ["text/plain; charset=utf-8"] },
19
+ { "key": "Content-Length", "values": ["4"] }
20
+ ]
21
+ }
22
+ }