speakeasy_ruby_sdk 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ }