metlo 0.0.2-x86_64-linux

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 04c4f41b397eecedc7f0a5963c1a1c0888678d5c84ae11c053b799bc199f43bd
4
+ data.tar.gz: 13077725d93eb8c0d8cf38cd72cb81865f6b655cf3cae2fe104522761d5d76a8
5
+ SHA512:
6
+ metadata.gz: c2261ba50f9bf8cbb62115fc3fdee710bbf077e9bebf61295cf73e24574a76537f43fa808cdcd999295b0b00b2c24029a8362047c4c64d0f6983d039ca2e4e88
7
+ data.tar.gz: 801e240efef61523a410cc979f69bdbd7dbb7eaf6abd36694a2208fe5a06f4844769a574785053ce14cb0fe63c5ec1844d7cc3d4933052dff90412df554d5bbe
data/.standard.yml ADDED
@@ -0,0 +1,2 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in metlo.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 12.0"
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Metlo
2
+
3
+ The Metlo agent for ruby frameworks able to utilize rack based middleware
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'metlo'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install metlo
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "standard/rake"
5
+
6
+ task default: :standard
data/lib/libmetlo.so ADDED
Binary file
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metlo
4
+ VERSION = "0.0.2"
5
+ end
data/lib/metlo.rb ADDED
@@ -0,0 +1,260 @@
1
+ require_relative "metlo/version"
2
+ require "json"
3
+ require_relative "./libmetlo"
4
+
5
+ module Metlo
6
+
7
+ class ResponseTimer
8
+
9
+ @@MAX_RES_ITER_COUNT = 10
10
+ @@MAX_BODY_LENGTH = 10 * 1024
11
+ @@LOG_LEVELS = ["trace","debug","info","warn","error"]
12
+
13
+ def initialize(app, params = {})
14
+ @app = app
15
+ @disable = false
16
+ @block_fn = method(:metlo_default_block)
17
+ @get_user = nil
18
+
19
+ metlo_url = params[:metlo_url]
20
+ api_key = params[:api_key]
21
+ backend_port = params[:backend_port]
22
+ collector_port = params[:collector_port]
23
+ encryption_key = params[:encryption_key]
24
+ log_level = params[:log_level]
25
+ block_response = params[:block_response]
26
+ get_user = params[:get_user]
27
+
28
+ # Metlo URL
29
+ if not (metlo_url.is_a? String)
30
+ p "metlo_url is not a string"
31
+ @disable = true
32
+ end
33
+
34
+ # Metlo API Key
35
+ if not (api_key.is_a? String)
36
+ p "api_key is not a string"
37
+ @disable = true
38
+ end
39
+
40
+ # Backend Port
41
+ if not (backend_port.nil?)
42
+ # Value is present
43
+ if not (backend_port.is_a? Integer)
44
+ p "backend_port is not an integer"
45
+ @disable = true
46
+ end
47
+ else
48
+ # Value missing.
49
+ if metlo_url.include? "app.metlo.com"
50
+ backend_port = 443
51
+ else
52
+ backend_port = 8000
53
+ end
54
+ end
55
+
56
+ # Collector Port
57
+ if not (collector_port.nil?)
58
+ if not (collector_port.is_a? Integer)
59
+ p "collector_port is not an integer"
60
+ @disable = true
61
+ end
62
+ else
63
+ collector_port = 8081
64
+ end
65
+
66
+ if not (log_level.nil?)
67
+ if not (log_level.is_a? String)
68
+ p "log_level is not a string. log_level is expected to be one of trace, debug, info, warn, error."
69
+ p "Defaulting log level to info."
70
+ log_level = "info"
71
+ elsif not (@@LOG_LEVELS.include? log_level)
72
+ p "log_level is not one of trace, debug, info, warn, error."
73
+ p "Defaulting log level to info"
74
+ log_level = "info"
75
+ end
76
+ else
77
+ log_level = "info"
78
+ end
79
+
80
+ if not (encryption_key.nil?) and not (encryption_key.is_a? String)
81
+ p "Encryption key is expected to be a string."
82
+ encryption_key = nil
83
+ end
84
+
85
+ if not (block_response.nil?)
86
+ if not defined? block_response == "expression"
87
+ p "block_response param is expected to be a symbol to a method/function"
88
+ @block_fn = method(:metlo_default_block)
89
+ else
90
+ if not(((defined? method(block_response)) == "method") and (method(block_response).arity == 1))
91
+ p "block_response param is expected to be a method that takes a single param"
92
+ @block_fn = method(:metlo_default_block)
93
+ else
94
+ @block_fn = method(block_response)
95
+ end
96
+ end
97
+ else
98
+ @block_fn = method(:metlo_default_block)
99
+ end
100
+
101
+
102
+ if not (get_user.nil?)
103
+ if not (defined? get_user == "expression" and defined? method(get_user) == "method" and method(get_user).arity == 1)
104
+ p "get_user param is expected to be a method that takes a single param env"
105
+ @get_user = nil
106
+ else
107
+ @get_user = method(get_user)
108
+ end
109
+ end
110
+
111
+ if not (@disable)
112
+ startup(
113
+ metlo_url,
114
+ api_key,
115
+ backend_port,
116
+ collector_port,
117
+ log_level,
118
+ encryption_key
119
+ )
120
+ end
121
+ end
122
+
123
+ def call(env)
124
+ if not (@disable)
125
+ req = nil
126
+ meta = nil
127
+ res = nil
128
+ begin
129
+ req_raw = create_req(env)
130
+ req = JSON.generate(req_raw)
131
+ meta_raw = create_meta(env)
132
+ meta = JSON.generate(meta_raw)
133
+ rescue StandardError => e
134
+ status, headers, response = @app.call(env)
135
+ [status, headers, response]
136
+ else
137
+ block = block_trace(req,meta)
138
+ if block
139
+ status, headers, response = (@block_fn).call env
140
+ else
141
+ status, headers, response = @app.call(env)
142
+ end
143
+ res_raw = create_res(status,headers,response)
144
+ res = JSON.generate(res_raw)
145
+
146
+ ingest_trace(req,res,meta)
147
+
148
+ [status, headers, response]
149
+ end
150
+ else
151
+ @app.call(env)
152
+ end
153
+
154
+ end
155
+
156
+ private
157
+
158
+ def create_req(env)
159
+ input_stream = env["rack.input"]
160
+ body = input_stream.read(1024*10)
161
+ input_stream.rewind
162
+ if body.nil?
163
+ body = ""
164
+ end
165
+
166
+ user = nil
167
+
168
+ if not (@get_user.nil?)
169
+ user = get_user(env)
170
+ end
171
+
172
+ body = body.to_str.force_encoding("UTF-8")
173
+ req = {
174
+ "url"=>{
175
+ "host"=>env["SERVER_NAME"],
176
+ "path"=>env["PATH_INFO"],
177
+ "parameters"=>env["QUERY_STRING"] != nil ? env["QUERY_STRING"].split("&").map{|s|s.split("=")}.map{|a,b| {"name"=>a,"value"=>b}}:[],
178
+ },
179
+ "headers"=>env.select{|k,v| k.start_with? "HTTP_"}.map{|k,v| {"name":k[5..-1].gsub("_","-"),"value":v}},
180
+ "method"=>env["REQUEST_METHOD"],
181
+ "body"=>body.to_str,
182
+ "user"=>user
183
+ }
184
+ return req
185
+ end
186
+
187
+ def create_res(status,headers,response)
188
+ body = ""
189
+ current_body_fill = ""
190
+ if defined? response.each
191
+ body_tmp = response.each
192
+ if body_tmp.class == Enumerator
193
+ count = 0
194
+ curr_body_length = 0
195
+ loop do
196
+ segment = body_tmp.next
197
+
198
+ remaining_length = @@MAX_BODY_LENGTH - curr_body_length
199
+ curr_segment = segment[0..remaining_length]
200
+ curr_body_length = curr_body_length + curr_segment.length
201
+ body += curr_segment
202
+
203
+ # Make sure that we don't keep reading from an input
204
+ if count < @@MAX_RES_ITER_COUNT
205
+ count = count+1
206
+ else
207
+ break
208
+ end
209
+
210
+ end
211
+ body_tmp.rewind
212
+ end
213
+ else
214
+ p "Metlo doesn't currently handle streaming responses"
215
+ end
216
+
217
+ # Check response type
218
+
219
+ # If enumerable
220
+ # Give preference to enumerable over stream
221
+ # Also need to make sure that enumerable is read only once.
222
+ # So instead, pass on a new response object that implements the same methods
223
+ # and when each method is called on that object, intercept that and read it and pass on the information to metlo
224
+ # defined?(response.each)
225
+
226
+ # If streaming
227
+ # defined?(response.call)
228
+ # response.call takes in a single argument for stream.
229
+ # Stream must implement methods: read, write, <<, flush, close, close_read, close_write, closed?
230
+
231
+ # If array
232
+ # defined?(response.to_ary)
233
+
234
+ # If path
235
+ # defined?(response.to_path)
236
+ return {
237
+ "status"=>status,
238
+ "headers"=>headers.map{|k,v| {"name":k,"value":v}},
239
+ "body"=>body,
240
+ }
241
+ end
242
+
243
+ def create_meta(env)
244
+ server = env['rack.hijack'].io.addr
245
+ client = env['rack.hijack'].io.peeraddr
246
+ return {
247
+ "environment": "production",
248
+ "incoming": true,
249
+ "source": client[2],
250
+ "sourcePort": client[1],
251
+ "destination": server[2],
252
+ "destinationPort": server[1]
253
+ }
254
+ end
255
+
256
+ def metlo_default_block(env)
257
+ return [403,{},["Forbidden"]]
258
+ end
259
+ end
260
+ end
data/metlo.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/metlo/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "metlo"
7
+ spec.version = Metlo::VERSION
8
+ spec.authors = ["Metlo"]
9
+ spec.description = "The Ruby Agent for Metlo"
10
+ spec.summary = "The Ruby Agent for Metlo"
11
+ spec.homepage = "https://www.metlo.com"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = ">= 2.6.0"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://www.github.com/metlo-labs/metlo"
17
+
18
+ spec.platform = Gem::Platform::CURRENT
19
+
20
+ spec.files = [
21
+ ".standard.yml",
22
+ "Gemfile",
23
+ "README.md",
24
+ "Rakefile",
25
+ "lib/metlo.rb",
26
+ "lib/libmetlo.so",
27
+ "lib/metlo/version.rb",
28
+ "metlo.gemspec",
29
+ "sig/metlo.rbs",
30
+ ]
31
+ spec.require_paths = ["lib"]
32
+
33
+ end
data/sig/metlo.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Metlo
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metlo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: x86_64-linux
6
+ authors:
7
+ - Metlo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: The Ruby Agent for Metlo
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".standard.yml"
20
+ - Gemfile
21
+ - README.md
22
+ - Rakefile
23
+ - lib/libmetlo.so
24
+ - lib/metlo.rb
25
+ - lib/metlo/version.rb
26
+ - metlo.gemspec
27
+ - sig/metlo.rbs
28
+ homepage: https://www.metlo.com
29
+ licenses:
30
+ - MIT
31
+ metadata:
32
+ homepage_uri: https://www.metlo.com
33
+ source_code_uri: https://www.github.com/metlo-labs/metlo
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.6.0
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 3.0.3.1
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: The Ruby Agent for Metlo
53
+ test_files: []