sqreen-kit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: feacd9986438ad22e08e0e752a59058956dd63ddfe00b90bdfd094e030db4ced
4
+ data.tar.gz: 318d9d80e07bcc4336740effe1b243e1f279fdbb5bef84be9dab3425884ae7e0
5
+ SHA512:
6
+ metadata.gz: f43f0e22c84f9d15d749ad4ffaf9078ac9b0d1357d24f8f866cd7af5dfaf395489bd5fb913fdaeacc6b74a195491c42292b34b7c075271c3f4e788d95b105e4d
7
+ data.tar.gz: 91879a34437077558e6efb76196a9dd6b71fbdb0bdcc00c1c5d9e348035d749e8c0fd3b9537e8ec189737c7a0df1c0a5813b4ef0afd20c6fb0858b00aae2620c
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ * Extracted code from Ruby agent
data/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ Sqreen for Ruby is free-to-use, proprietary software.
2
+
3
+ Please refer to our terms for more information: https://www.sqreen.com/terms.html
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sqreen/kit'
@@ -0,0 +1 @@
1
+ module Sqreen; end
@@ -0,0 +1,76 @@
1
+ require 'sqreen/kit/configuration'
2
+ require 'sqreen/kit/retry_policy'
3
+ require 'sqreen/kit/http_client'
4
+ require 'sqreen/kit/http_client/authentication_error'
5
+ require 'sqreen/kit/signals/batch_collector'
6
+ require 'sqreen/kit/signals/auth_signals_client'
7
+ require 'sqreen/kit/signals/signals_client'
8
+
9
+ module Sqreen; end
10
+
11
+ module Sqreen
12
+ module Kit
13
+ # Factories using the global configuration
14
+ class << self
15
+ def version
16
+ @version ||= Gem.loaded_specs['sqreen-kit'].version
17
+ end
18
+
19
+ def auth_signals_client
20
+ @auth_signals_client ||=
21
+ Signals::AuthSignalsClient.new(
22
+ signals_client,
23
+ session_key: Configuration.session_key,
24
+ api_key: Configuration.api_key,
25
+ app_name: Configuration.app_name,
26
+ )
27
+ end
28
+
29
+ def signals_client
30
+ @signals_client ||=
31
+ Signals::SignalsClient.new(http_client)
32
+ end
33
+
34
+ def batch_collector
35
+ @batch_collector ||=
36
+ Signals::BatchCollector.new(
37
+ auth_signals_client,
38
+ flush_size: Configuration.batch_flush_size,
39
+ max_batch_size: Configuration.batch_max_size,
40
+ max_delay_s: Configuration.batch_max_delay_s,
41
+ )
42
+ end
43
+
44
+ def reset
45
+ @auth_signals_client = nil
46
+ @signals_client = nil
47
+ @batch_collector = nil
48
+ @http_client = nil
49
+ @retry_policy = nil
50
+ end
51
+
52
+ private
53
+
54
+ def http_client
55
+ @http_client ||=
56
+ HttpClient.new(Configuration.ingestion_url,
57
+ retry_policy,
58
+ proxy_address: Configuration.proxy_address,
59
+ proxy_port: Configuration.proxy_port,
60
+ proxy_user: Configuration.proxy_user,
61
+ proxy_pass: Configuration.proxy_pass,
62
+ connect_timeout: Configuration.connect_timeout,
63
+ read_timeout: Configuration.read_timeout)
64
+ end
65
+
66
+ def retry_policy
67
+ @retry_policy ||=
68
+ RetryPolicy.new(
69
+ max_retries: Configuration.retry_max_retries,
70
+ wait_s: Configuration.retry_max_retries,
71
+ fatal_exceptions: [Sqreen::Kit::HttpClient::AuthenticationError],
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,67 @@
1
+ require 'logger'
2
+
3
+ module Sqreen
4
+ module Kit
5
+ class Configuration
6
+ class << self
7
+ attr_accessor :logger
8
+
9
+ # http client related
10
+ attr_accessor :ingestion_url,
11
+ :proxy_address,
12
+ :proxy_port,
13
+ :proxy_user,
14
+ :proxy_pass,
15
+ :connect_timeout,
16
+ :read_timeout
17
+
18
+ # retry policy
19
+ attr_accessor :retry_max_retries,
20
+ :retry_wait_s
21
+
22
+ # authentication
23
+ attr_accessor :session_key,
24
+ :api_key,
25
+ :app_name
26
+
27
+ # batch collector
28
+ attr_accessor :batch_max_delay_s,
29
+ :batch_flush_size,
30
+ :batch_max_size
31
+
32
+ # @param [String] url
33
+ def proxy_url=(url)
34
+ if url.nil?
35
+ self.proxy_address =
36
+ self.proxy_port =
37
+ self.proxy_user =
38
+ self.proxy_pass = nil
39
+ return
40
+ end
41
+
42
+ parsed = URI.parse(url)
43
+ raise ArgumentError, 'only http proxies are supported' unless parsed.scheme == 'http'
44
+
45
+ self.proxy_address = parsed.host
46
+ self.proxy_port = parsed.port
47
+ self.proxy_user = parsed.user
48
+ self.proxy_port = parsed.port
49
+ end
50
+ end
51
+
52
+ # default values
53
+ def self.set_defaults
54
+ instance_variables.each do |ia|
55
+ instance_variable_set(ia, nil)
56
+ end
57
+ self.logger = ::Logger.new(STDERR)
58
+ logger.level = ::Logger::WARN
59
+ logger.progname = 'sqreen-kit'
60
+
61
+ self.ingestion_url = 'https://ingestion.sqreen.com/'
62
+ end
63
+
64
+ set_defaults
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,160 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ require 'sqreen/kit/loggable'
5
+ require 'sqreen/kit/retry_policy'
6
+ require 'sqreen/kit/http_client/authentication_error'
7
+ require 'sqreen/kit/http_client/unexpected_status_error'
8
+
9
+ module Sqreen
10
+ module Kit
11
+ # An http client doing JSON requests
12
+ class HttpClient
13
+ include Loggable
14
+
15
+ DEFAULT_CONNECT_TIMEOUT_S = 5
16
+ DEFAULT_READ_TIMEOUT_S = 30
17
+
18
+ attr_reader :http
19
+
20
+ # @return [URI]
21
+ attr_reader :base_url
22
+
23
+ # @param server_url [String]
24
+ # @param retry_policy [Sqreen::Kit::RetryPolicy]
25
+ def initialize(server_url, retry_policy, opts = {})
26
+ @base_url = parse_uri(server_url)
27
+
28
+ @retry_policy = retry_policy
29
+
30
+ @http = ::Net::HTTP.new(@base_url.host, @base_url.port,
31
+ opts[:proxy_address] || :ENV,
32
+ opts[:proxy_port], opts[:proxy_user],
33
+ opts[:proxy_pass])
34
+ @http.use_ssl = @base_url.scheme.downcase == 'https'
35
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SQREEN_SSL_NO_VERIFY'] # for testing
36
+
37
+ @http.open_timeout = opts[:connect_timeout] || DEFAULT_CONNECT_TIMEOUT_S
38
+ @http.ssl_timeout = opts[:read_timeout] || DEFAULT_READ_TIMEOUT_S
39
+ @http.read_timeout = opts[:read_timeout] || DEFAULT_READ_TIMEOUT_S
40
+
41
+ @req_nb = 1
42
+
43
+ @static_headers = init_static_headers
44
+ end
45
+
46
+ # @param path [String]
47
+ # @param data [String]
48
+ # @param headers [Hash{String=>String}]
49
+ def post(path, data, headers = {})
50
+ request(:POST, path, data, headers)
51
+ end
52
+
53
+ def get(path, headers = {})
54
+ request(:GET, path, nil, headers)
55
+ end
56
+
57
+ private
58
+
59
+ # @return [URI::HTTP]
60
+ def parse_uri(uri)
61
+ # This regexp is the Ruby constant URI::PATTERN::HOSTNAME augmented
62
+ # with the _ character that is frequent in Docker linked containers.
63
+ re = '(?:(?:[a-zA-Z\\d](?:[-_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.)*(?:[a-zA-Z](?:[-_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.?'
64
+ parser = URI::Parser.new HOSTNAME: re
65
+ uri += '/' unless uri.end_with? '/'
66
+ parser.parse(uri)
67
+ end
68
+
69
+ def with_retry(&block)
70
+ @retry_policy.execute(&block)
71
+ end
72
+
73
+ def connect
74
+ return if connected?
75
+ logger.warn { "Connecting to #{base_url}..." }
76
+
77
+ begin
78
+ with_retry do
79
+ http.start
80
+ end
81
+ rescue StandardError => e
82
+ logger.warn "Cannot connect to #{base_url}: #{e.message}"
83
+ raise
84
+ else
85
+ logger.warn 'Connection success'
86
+ end
87
+ end
88
+
89
+ def connected?
90
+ http.started?
91
+ end
92
+
93
+ def disconnect
94
+ http.finish if connected?
95
+ end
96
+
97
+ def init_static_headers
98
+ platform = RUBY_PLATFORM
99
+ platform += " #{ENV_JAVA['java.runtime.version']}" if defined?(ENV_JAVA)
100
+ ua_string = "sqreen-kit/#{Sqreen::Kit.version} " \
101
+ "(#{platform}) ruby/#{RUBY_VERSION}"
102
+ ua_string += " jruby/#{JRUBY_VERSION}" if defined?(JRUBY_VERSION)
103
+ { 'User-Agent' => ua_string }
104
+ end
105
+
106
+ def request(method, path, data, headers = {})
107
+ connect
108
+
109
+ now = Time.now
110
+
111
+ headers = @static_headers.merge(headers)
112
+ @req_nb += 1
113
+
114
+ path = (base_url + path).path
115
+
116
+ with_retry do
117
+ logger.debug do
118
+ format('%s %s %s (%s)',
119
+ method, path, data.inspect, headers.inspect)
120
+ end
121
+
122
+ resp = case method
123
+ when :GET
124
+ http.get(path, headers)
125
+ when :POST
126
+ http.post(path, data, headers)
127
+ end
128
+
129
+ result = process_response resp
130
+
131
+ logger.debug do
132
+ format('%s %s (DONE: %s in %f ms)',
133
+ method, path, resp && resp.code, (Time.now - now) * 1000)
134
+ end
135
+
136
+ result
137
+ end # end with_retry
138
+ end
139
+
140
+ # @param [Net::HTTPResponse] resp
141
+ def process_response(resp)
142
+ body = resp.body
143
+ logger.debug { "Body of response: #{body}" }
144
+
145
+ if resp.code == '401' || resp.code == '403'
146
+ raise AuthenticationError.new(resp.code, body)
147
+ end
148
+
149
+ unless %w[200 201 202].include?(resp.code)
150
+ raise UnexpectedStatusError.new(resp.code, body)
151
+ end
152
+
153
+ {
154
+ code: resp.code,
155
+ body: body,
156
+ }
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,13 @@
1
+ require 'sqreen/kit/http_client/unexpected_status_error'
2
+
3
+ module Sqreen
4
+ module Kit
5
+ class HttpClient
6
+ class AuthenticationError < UnexpectedStatusError
7
+ def initialize(*args)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Sqreen
2
+ module Kit
3
+ class HttpClient
4
+ class UnexpectedStatusError < StandardError
5
+ attr_reader :code, :body
6
+
7
+ def initialize(code, body = nil)
8
+ @code = code
9
+ @body = body
10
+ end
11
+
12
+ def message
13
+ "Unexpected status #{code}: #{body}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'sqreen/kit/configuration'
2
+
3
+ module Sqreen
4
+ module Kit
5
+ module Loggable
6
+ private
7
+
8
+ # @return [::Logger]
9
+ def logger
10
+ Configuration.logger
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,56 @@
1
+ require 'sqreen/kit/loggable'
2
+
3
+ module Sqreen
4
+ module Kit
5
+ class RetryPolicy
6
+ include Loggable
7
+
8
+ DEFAULT_RETRIES = 2
9
+ DEFAULT_WAITS_S = 3
10
+ DEFAULT_FATAL_EXCEPTIONS = [].freeze
11
+
12
+ attr_reader :max_retries, :wait_s, :fatal_exceptions
13
+
14
+ # @param opts the parameters of the retry policy
15
+ # @option opts [Integer] the maximum number of tries
16
+ # @option opts [Float] wait_s wait these seconds before a retry
17
+ # @option opts [Array<Class>] exception classes for which no retry will
18
+ # be attempted, besides non-StandardError
19
+ def initialize(opts = {})
20
+ @max_retries = opts[:max_retries] || DEFAULT_RETRIES
21
+ @wait_s = opts[:wait_s] || DEFAULT_WAITS_S
22
+ @fatal_exceptions = opts[:fatal_exceptions] || DEFAULT_FATAL_EXCEPTIONS
23
+ end
24
+
25
+ def execute
26
+ attempt = 1
27
+ begin
28
+ yield
29
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
30
+ logger.warn { "Error on attempt ##{attempt}: #{e.message}" }
31
+ logger.debug { e.backtrace }
32
+ if fatal?(e)
33
+ logger.debug { "Not retrying after seeing exception #{e.class}" }
34
+ raise
35
+ end
36
+ if attempt > max_retries
37
+ logger.debug { "Not retrying anymore after #{attempt} attempts" }
38
+ raise
39
+ end
40
+
41
+ logger.debug { "Will retry after #{wait_s} seconds" }
42
+ sleep(wait_s) unless wait_s.zero?
43
+ attempt += 1
44
+ retry
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def fatal?(exception)
51
+ !exception.is_a?(StandardError) ||
52
+ fatal_exceptions.any? { |ec| exception.is_a?(ec) }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,26 @@
1
+ require 'sqreen/kit/signals/dto_helper'
2
+
3
+ module Sqreen
4
+ module Kit
5
+ module Signals
6
+ class Actor
7
+ include DtoHelper
8
+
9
+ add_mandatory_attrs :ip_addresses
10
+
11
+ # mandatory
12
+ # @return [Array<String>]
13
+ attr_accessor :ip_addresses
14
+
15
+ # @return String
16
+ attr_accessor :user_agent
17
+
18
+ # @return [Hash{String=>String}]
19
+ attr_accessor :identifiers
20
+
21
+ # @return [Hash{String=>String}]
22
+ attr_accessor :traits
23
+ end
24
+ end
25
+ end
26
+ end