macaw_framework 1.3.0 → 1.3.3
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 +4 -4
- data/.rubocop.yml +13 -9
- data/CHANGELOG.md +14 -0
- data/Gemfile +11 -9
- data/README.md +28 -10
- data/Rakefile +6 -6
- data/lib/macaw_framework/aspects/cache_aspect.rb +3 -3
- data/lib/macaw_framework/aspects/logging_aspect.rb +4 -7
- data/lib/macaw_framework/aspects/prometheus_aspect.rb +3 -3
- data/lib/macaw_framework/cache.rb +91 -0
- data/lib/macaw_framework/core/common/server_base.rb +29 -28
- data/lib/macaw_framework/core/thread_server.rb +13 -17
- data/lib/macaw_framework/data_filters/log_data_filter.rb +9 -9
- data/lib/macaw_framework/data_filters/request_data_filtering.rb +26 -24
- data/lib/macaw_framework/data_filters/response_data_filter.rb +3 -3
- data/lib/macaw_framework/errors/endpoint_not_mapped_error.rb +1 -1
- data/lib/macaw_framework/macaw.rb +264 -0
- data/lib/macaw_framework/middlewares/prometheus_middleware.rb +7 -7
- data/lib/macaw_framework/utils/http_status_code.rb +61 -61
- data/lib/macaw_framework/utils/supported_ssl_versions.rb +6 -6
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +5 -230
- metadata +6 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5ce341c22fbc784508cd3678e12b800d9313bb3966ca933d4970798fc5d8d14
|
|
4
|
+
data.tar.gz: 7a4d3d9926365062786b919e7114c16d09e14ae9e1a6828f49322985fb9d8f9e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92d923dfa4e841177186a4b6c35c7a41a543c19b2d0036fc7caa7fd9194f39479d602b8083966ad19930d9992aba88b9f678d78f1cc63b20be715df07e79f848
|
|
7
|
+
data.tar.gz: 65221f77161d6c3903c4d71c7dd2146371f4f846aa49419cedd457377c955805dbbda9de1f8e554966c36e44f974c236a395f6bb8e338b78891a433dc1b2fb56
|
data/.rubocop.yml
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion:
|
|
2
|
+
TargetRubyVersion: 3.0
|
|
3
3
|
SuggestExtensions: false
|
|
4
4
|
NewCops: disable
|
|
5
5
|
|
|
6
|
-
Style/StringLiterals:
|
|
7
|
-
Enabled: true
|
|
8
|
-
EnforcedStyle: double_quotes
|
|
9
|
-
|
|
10
|
-
Style/StringLiteralsInInterpolation:
|
|
11
|
-
Enabled: true
|
|
12
|
-
EnforcedStyle: double_quotes
|
|
13
|
-
|
|
14
6
|
Layout/LineLength:
|
|
15
7
|
Max: 120
|
|
16
8
|
|
|
@@ -31,3 +23,15 @@ Metrics/PerceivedComplexity:
|
|
|
31
23
|
|
|
32
24
|
Metrics/ClassLength:
|
|
33
25
|
Enabled: false
|
|
26
|
+
|
|
27
|
+
Metrics/ModuleLength:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Naming/MemoizedInstanceVariableName:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Naming/MethodName:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/ClassAndModuleChildren:
|
|
37
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -143,3 +143,17 @@
|
|
|
143
143
|
- Fixed a bug where errors were being logged with level INFO
|
|
144
144
|
- Improved error stack trace
|
|
145
145
|
|
|
146
|
+
## [1.3.1]
|
|
147
|
+
- Fixing bug where missing session configuration on `application.json` break the application
|
|
148
|
+
- Including a Cache module for manual caching.
|
|
149
|
+
|
|
150
|
+
## [1.3.21]
|
|
151
|
+
- Refactoring shutdown method
|
|
152
|
+
- Fixing a bug where a HTTP call without client data broke the parser
|
|
153
|
+
- Removing logs registering new HTTP connections to reduce log bloat
|
|
154
|
+
|
|
155
|
+
## [1.3.22]
|
|
156
|
+
- Fixing error with tests on Ruby 3.4.x due to splash operator
|
|
157
|
+
|
|
158
|
+
## [1.3.3]
|
|
159
|
+
- Add missing dependencies for Ruby 4.x
|
data/Gemfile
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
source
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
gem
|
|
8
|
-
gem
|
|
7
|
+
gem 'logger', '~> 1.7'
|
|
8
|
+
gem 'openssl'
|
|
9
|
+
gem 'prometheus-client', '~> 4.1'
|
|
9
10
|
|
|
10
11
|
group :test do
|
|
11
|
-
gem
|
|
12
|
-
gem
|
|
13
|
-
gem
|
|
14
|
-
gem
|
|
15
|
-
gem
|
|
16
|
-
gem
|
|
12
|
+
gem 'minitest', '~> 5.0'
|
|
13
|
+
gem 'ostruct', '~> 0.6.3'
|
|
14
|
+
gem 'rake', '~> 13.0'
|
|
15
|
+
gem 'rubocop', '~> 1.21'
|
|
16
|
+
gem 'simplecov', '~> 0.21.2'
|
|
17
|
+
gem 'simplecov-json'
|
|
18
|
+
gem 'simplecov_json_formatter', '~> 0.1.2'
|
|
17
19
|
end
|
data/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# MacawFramework
|
|
3
3
|
|
|
4
4
|
MacawFramework is a lightweight, easy-to-use web framework for Ruby designed to simplify the development of small to
|
|
5
|
-
medium-sized web applications.
|
|
6
|
-
provides developers with the essential tools to quickly build and deploy their applications.
|
|
5
|
+
medium-sized web applications. Weighting less than 26Kb with support for various HTTP methods, caching, and session management,
|
|
6
|
+
MacawFramework provides developers with the essential tools to quickly build and deploy their applications.
|
|
7
7
|
|
|
8
8
|
- [MacawFramework](#macawframework)
|
|
9
9
|
* [Features](#features)
|
|
@@ -18,7 +18,7 @@ provides developers with the essential tools to quickly build and deploy their a
|
|
|
18
18
|
+ [Configuration: Customize various aspects of the framework through the application.json configuration file, such as rate limiting, SSL support, and Prometheus integration](#configuration-customize-various-aspects-of-the-framework-through-the-applicationjson-configuration-file-such-as-rate-limiting-ssl-support-and-prometheus-integration)
|
|
19
19
|
+ [Monitoring: Easily monitor your application performance and metrics with built-in Prometheus support](#monitoring-easily-monitor-your-application-performance-and-metrics-with-built-in-prometheus-support)
|
|
20
20
|
+ [Routing for "public" Folder: Serve Static Assets](#routing-for-public-folder-serve-static-assets)
|
|
21
|
-
+ [
|
|
21
|
+
+ [Periodic Jobs](#periodic-jobs)
|
|
22
22
|
+ [Tips](#tips)
|
|
23
23
|
* [Contributing](#contributing)
|
|
24
24
|
* [License](#license)
|
|
@@ -31,7 +31,8 @@ provides developers with the essential tools to quickly build and deploy their a
|
|
|
31
31
|
- Session management with server-side in-memory storage
|
|
32
32
|
- Basic rate limiting and SSL support
|
|
33
33
|
- Prometheus integration for monitoring and metrics
|
|
34
|
-
-
|
|
34
|
+
- Less than 26Kb
|
|
35
|
+
- Easy to learn
|
|
35
36
|
|
|
36
37
|
## Installation
|
|
37
38
|
|
|
@@ -51,7 +52,7 @@ We evaluated MacawFramework (Version 1.2.0) to assess its ability to handle simu
|
|
|
51
52
|
|
|
52
53
|
MacawFramework is built to be highly compatible, since it uses only native Ruby code:
|
|
53
54
|
|
|
54
|
-
- **MRI**: MacawFramework is compatible with Matz's Ruby Interpreter (MRI), version
|
|
55
|
+
- **MRI**: MacawFramework is compatible with Matz's Ruby Interpreter (MRI), version 3.0.0 and onwards. If you are using this version or a more recent one, you should not encounter any compatibility issues.
|
|
55
56
|
|
|
56
57
|
- **TruffleRuby**: TruffleRuby is another Ruby interpreter that is fully compatible with MacawFramework. This provides developers with more flexibility in their choice of Ruby interpreter.
|
|
57
58
|
|
|
@@ -104,6 +105,8 @@ m.start!
|
|
|
104
105
|
### Caching: Improve performance by caching responses and configuring cache invalidation
|
|
105
106
|
|
|
106
107
|
```ruby
|
|
108
|
+
m = MacawFramework::Macaw.new
|
|
109
|
+
|
|
107
110
|
m.get('/cached_data', cache: ["header_to_cache", "query_param_to_cache"]) do |context|
|
|
108
111
|
# Retrieve data
|
|
109
112
|
end
|
|
@@ -111,6 +114,17 @@ end
|
|
|
111
114
|
|
|
112
115
|
*Observation: To activate caching, you also have to set its properties in the `application.json` file. If you don't, the caching strategy will not work. See the Configuration section below for more details.*
|
|
113
116
|
|
|
117
|
+
Another method of cache is the manual cache via the `MacawFramework::Cache` class. You can manually
|
|
118
|
+
call the `read` and `write` methods of this singleton to save and recover values inside your methods.
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
MacawFramework::Cache.write(:name, 'Maria', expires_in: 1800)
|
|
122
|
+
# Your code
|
|
123
|
+
MacawFramework::Cache.read(:name) # Maria
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Manual cache does not need any additional configuration.
|
|
127
|
+
|
|
114
128
|
### Session management: Handle user sessions with server-side in-memory storage
|
|
115
129
|
|
|
116
130
|
Session will only be enabled if it's configurations exists in the `application.json` file.
|
|
@@ -123,6 +137,8 @@ a session id in the HTTP request. In the case of the client sending an ID of an
|
|
|
123
137
|
the framework will return a new session with a new ID.
|
|
124
138
|
|
|
125
139
|
```ruby
|
|
140
|
+
m = MacawFramework::Macaw.new
|
|
141
|
+
|
|
126
142
|
m.get('/login') do |context|
|
|
127
143
|
# Authenticate user
|
|
128
144
|
context[:client][:user_id] = user_id
|
|
@@ -195,17 +211,19 @@ be accessible at http://yourdomain.com/img/logo.png without any additional confi
|
|
|
195
211
|
|
|
196
212
|
#### Caution: This is incompatible with most non-unix systems, such as Windows. If you are using a non-unix system, you will need to manually configure the "public" folder and use dir as nil to avoid problems.
|
|
197
213
|
|
|
198
|
-
###
|
|
214
|
+
### Periodic Jobs
|
|
199
215
|
|
|
200
|
-
Macaw Framework supports the declaration of
|
|
216
|
+
Macaw Framework supports the declaration of periodic jobs right in your application code. This feature allows developers to
|
|
201
217
|
define tasks that run at set intervals, starting after an optional delay. Each job runs in a separate thread, meaning
|
|
202
|
-
your
|
|
218
|
+
your periodic jobs can execute in parallel without blocking the rest of your application.
|
|
203
219
|
|
|
204
|
-
Here's an example of how to declare a
|
|
220
|
+
Here's an example of how to declare a periodic job:
|
|
205
221
|
|
|
206
222
|
```ruby
|
|
223
|
+
m = MacawFramework::Macaw.new
|
|
224
|
+
|
|
207
225
|
m.setup_job(interval: 5, start_delay: 5, job_name: "cron job 1") do
|
|
208
|
-
puts "i'm a
|
|
226
|
+
puts "i'm a periodic job that runs every 5 secs!"
|
|
209
227
|
end
|
|
210
228
|
```
|
|
211
229
|
|
data/Rakefile
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rake/testtask'
|
|
5
5
|
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs <<
|
|
8
|
-
t.libs <<
|
|
9
|
-
t.test_files = FileList[
|
|
7
|
+
t.libs << 'test'
|
|
8
|
+
t.libs << 'lib'
|
|
9
|
+
t.test_files = FileList['test/**/test_*.rb']
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
require
|
|
12
|
+
require 'rubocop/rake_task'
|
|
13
13
|
|
|
14
14
|
RuboCop::RakeTask.new
|
|
15
15
|
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
##
|
|
4
4
|
# Aspect that provide cache for the endpoints.
|
|
5
5
|
module CacheAspect
|
|
6
|
-
def call_endpoint(cache, *args)
|
|
7
|
-
return super(*args) unless !cache[:cache].nil? && cache[:endpoints_to_cache]&.include?(args[0])
|
|
6
|
+
def call_endpoint(cache, *args, **kwargs)
|
|
7
|
+
return super(*args, **kwargs) unless !cache[:cache].nil? && cache[:endpoints_to_cache]&.include?(args[0])
|
|
8
8
|
|
|
9
9
|
cache_filtered_name = cache_name_filter(args[1], cache[:cached_methods][args[0]])
|
|
10
10
|
|
|
11
11
|
cache[:cache].mutex.synchronize do
|
|
12
12
|
return cache[:cache].cache[cache_filtered_name][0] unless cache[:cache].cache[cache_filtered_name].nil?
|
|
13
13
|
|
|
14
|
-
response = super(*args)
|
|
14
|
+
response = super(*args, **kwargs)
|
|
15
15
|
cache[:cache].cache[cache_filtered_name] = [response, Time.now] if should_cache_response?(response[1])
|
|
16
16
|
response
|
|
17
17
|
end
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: false
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require_relative
|
|
3
|
+
require 'logger'
|
|
4
|
+
require_relative '../data_filters/log_data_filter'
|
|
5
5
|
|
|
6
6
|
##
|
|
7
7
|
# This Aspect is responsible for logging
|
|
8
8
|
# the input and output of every endpoint called
|
|
9
9
|
# in the framework.
|
|
10
10
|
module LoggingAspect
|
|
11
|
-
def call_endpoint(logger, *args)
|
|
12
|
-
return super(*args) if logger.nil?
|
|
13
|
-
|
|
14
|
-
endpoint_name = args[1].split(".")[1..].join("/")
|
|
15
|
-
logger.info("Request received for [#{endpoint_name}] from [#{args[-1]}]")
|
|
11
|
+
def call_endpoint(logger, *args, **kwargs)
|
|
12
|
+
return super(*args, **kwargs) if logger.nil?
|
|
16
13
|
|
|
17
14
|
begin
|
|
18
15
|
response = super(*args)
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
##
|
|
4
4
|
# Aspect that provides application metrics using prometheus.
|
|
5
5
|
module PrometheusAspect
|
|
6
|
-
def call_endpoint(prometheus_middleware, *args)
|
|
7
|
-
return super(*args) if prometheus_middleware.nil?
|
|
6
|
+
def call_endpoint(prometheus_middleware, *args, **kwargs)
|
|
7
|
+
return super(*args, **kwargs) if prometheus_middleware.nil?
|
|
8
8
|
|
|
9
9
|
start_time = Time.now
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ module PrometheusAspect
|
|
|
13
13
|
ensure
|
|
14
14
|
duration = (Time.now - start_time) * 1_000
|
|
15
15
|
|
|
16
|
-
endpoint_name = args[2].split(
|
|
16
|
+
endpoint_name = args[2].split('.').join('/')
|
|
17
17
|
|
|
18
18
|
prometheus_middleware.request_duration_milliseconds.with_labels(endpoint: endpoint_name).observe(duration)
|
|
19
19
|
prometheus_middleware.request_count.with_labels(endpoint: endpoint_name).increment
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Main module for all Macaw classes
|
|
5
|
+
module MacawFramework; end
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# This singleton class allows to manually cache
|
|
9
|
+
# parameters and other data.
|
|
10
|
+
class MacawFramework::Cache
|
|
11
|
+
include Singleton
|
|
12
|
+
|
|
13
|
+
attr_accessor :invalidation_frequency
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Write a value to Cache memory.
|
|
17
|
+
# Can be called statically or from an instance.
|
|
18
|
+
# @param {String} tag
|
|
19
|
+
# @param {Object} value
|
|
20
|
+
# @param {Integer} expires_in Defaults to 3600.
|
|
21
|
+
# @return nil
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
|
|
25
|
+
def self.write(tag, value, expires_in: 3600)
|
|
26
|
+
MacawFramework::Cache.instance.write(tag, value, expires_in: expires_in)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Write a value to Cache memory.
|
|
31
|
+
# Can be called statically or from an instance.
|
|
32
|
+
# @param {String} tag
|
|
33
|
+
# @param {Object} value
|
|
34
|
+
# @param {Integer} expires_in Defaults to 3600.
|
|
35
|
+
# @return nil
|
|
36
|
+
#
|
|
37
|
+
# @example
|
|
38
|
+
# MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
|
|
39
|
+
def write(tag, value, expires_in: 3600)
|
|
40
|
+
if read(tag).nil?
|
|
41
|
+
@mutex.synchronize do
|
|
42
|
+
@cache.store(tag, { value: value, expires_in: Time.now + expires_in })
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
@cache[tag][:value] = value
|
|
46
|
+
@cache[tag][:expires_in] = Time.now + expires_in
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Read the value with the specified tag.
|
|
52
|
+
# Can be called statically or from an instance.
|
|
53
|
+
# @param {String} tag
|
|
54
|
+
# @return {String|nil}
|
|
55
|
+
#
|
|
56
|
+
# @example
|
|
57
|
+
# MacawFramework::Cache.read("name") # Maria
|
|
58
|
+
def self.read(tag) = MacawFramework::Cache.instance.read(tag)
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Read the value with the specified tag.
|
|
62
|
+
# Can be called statically or from an instance.
|
|
63
|
+
# @param {String} tag
|
|
64
|
+
# @return {String|nil}
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# MacawFramework::Cache.read("name") # Maria
|
|
68
|
+
def read(tag) = @cache.dig(tag, :value)
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def initialize
|
|
73
|
+
@cache = {}
|
|
74
|
+
@mutex = Mutex.new
|
|
75
|
+
@invalidation_frequency = 60
|
|
76
|
+
invalidate_cache
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def invalidate_cache
|
|
80
|
+
@invalidator = Thread.new(&method(:invalidation_process))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def invalidation_process
|
|
84
|
+
loop do
|
|
85
|
+
sleep @invalidation_frequency
|
|
86
|
+
@mutex.synchronize do
|
|
87
|
+
@cache.delete_if { |_, v| v[:expires_in] < Time.now }
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
11
|
-
require
|
|
3
|
+
require_relative '../../middlewares/memory_invalidation_middleware'
|
|
4
|
+
require_relative '../../middlewares/rate_limiter_middleware'
|
|
5
|
+
require_relative '../../data_filters/response_data_filter'
|
|
6
|
+
require_relative '../../errors/too_many_requests_error'
|
|
7
|
+
require_relative '../../utils/supported_ssl_versions'
|
|
8
|
+
require_relative '../../aspects/prometheus_aspect'
|
|
9
|
+
require_relative '../../aspects/logging_aspect'
|
|
10
|
+
require_relative '../../aspects/cache_aspect'
|
|
11
|
+
require 'securerandom'
|
|
12
12
|
|
|
13
13
|
##
|
|
14
14
|
# Base module for Server classes. It contains
|
|
@@ -29,7 +29,7 @@ module ServerBase
|
|
|
29
29
|
headers: client_data[:headers],
|
|
30
30
|
body: client_data[:body],
|
|
31
31
|
params: client_data[:params],
|
|
32
|
-
client: @session
|
|
32
|
+
client: @session&.dig(session_id)&.dig(0)
|
|
33
33
|
}
|
|
34
34
|
)
|
|
35
35
|
end
|
|
@@ -39,14 +39,13 @@ module ServerBase
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def handle_client(client)
|
|
42
|
-
|
|
42
|
+
_path, method_name, headers, body, parameters = RequestDataFiltering.parse_request_data(client, @macaw.routes)
|
|
43
43
|
raise EndpointNotMappedError unless @macaw.respond_to?(method_name)
|
|
44
44
|
raise TooManyRequestsError unless @rate_limit.nil? || @rate_limit.allow?(client.peeraddr[3])
|
|
45
45
|
|
|
46
46
|
client_data = get_client_data(body, headers, parameters)
|
|
47
47
|
session_id = declare_client_session(client_data[:headers], @macaw.secure_header) if @macaw.session
|
|
48
48
|
|
|
49
|
-
@macaw_log&.info("Running #{path.gsub("\n", "").gsub("\r", "")}")
|
|
50
49
|
message, status, response_headers = call_endpoint(@prometheus_middleware, @macaw_log, @cache,
|
|
51
50
|
method_name, client_data, session_id, client.peeraddr[3])
|
|
52
51
|
response_headers ||= {}
|
|
@@ -80,44 +79,46 @@ module ServerBase
|
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
def set_rate_limiting
|
|
83
|
-
return unless @macaw.config&.dig(
|
|
82
|
+
return unless @macaw.config&.dig('macaw', 'rate_limiting')
|
|
84
83
|
|
|
85
84
|
@rate_limit = RateLimiterMiddleware.new(
|
|
86
|
-
@macaw.config[
|
|
87
|
-
@macaw.config[
|
|
85
|
+
@macaw.config['macaw']['rate_limiting']['window'].to_i || 1,
|
|
86
|
+
@macaw.config['macaw']['rate_limiting']['max_requests'].to_i || 60
|
|
88
87
|
)
|
|
89
88
|
end
|
|
90
89
|
|
|
91
90
|
def set_ssl
|
|
92
|
-
ssl_config = @macaw.config[
|
|
91
|
+
ssl_config = @macaw.config['macaw']['ssl'] if @macaw.config&.dig('macaw', 'ssl')
|
|
93
92
|
ssl_config ||= nil
|
|
94
93
|
unless ssl_config.nil?
|
|
95
|
-
version_config = { min: ssl_config[
|
|
94
|
+
version_config = { min: ssl_config['min'], max: ssl_config['max'] }
|
|
96
95
|
@context = OpenSSL::SSL::SSLContext.new
|
|
97
96
|
@context.min_version = SupportedSSLVersions::VERSIONS[version_config[:min]] unless version_config[:min].nil?
|
|
98
97
|
@context.max_version = SupportedSSLVersions::VERSIONS[version_config[:max]] unless version_config[:max].nil?
|
|
99
|
-
@context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_config[
|
|
98
|
+
@context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_config['cert_file_name']))
|
|
100
99
|
|
|
101
|
-
if ssl_config[
|
|
102
|
-
@context.key = OpenSSL::PKey::RSA.new(File.read(ssl_config[
|
|
103
|
-
elsif ssl_config[
|
|
104
|
-
@context.key = OpenSSL::PKey::EC.new(File.read(ssl_config[
|
|
100
|
+
if ssl_config['key_type'] == 'RSA' || ssl_config['key_type'].nil?
|
|
101
|
+
@context.key = OpenSSL::PKey::RSA.new(File.read(ssl_config['key_file_name']))
|
|
102
|
+
elsif ssl_config['key_type'] == 'EC'
|
|
103
|
+
@context.key = OpenSSL::PKey::EC.new(File.read(ssl_config['key_file_name']))
|
|
105
104
|
else
|
|
106
|
-
raise ArgumentError, "Unsupported SSL/TLS key type: #{ssl_config[
|
|
105
|
+
raise ArgumentError, "Unsupported SSL/TLS key type: #{ssl_config['key_type']}"
|
|
107
106
|
end
|
|
108
107
|
end
|
|
109
108
|
@context ||= nil
|
|
110
109
|
rescue IOError => e
|
|
111
|
-
@macaw_log&.error("It was not possible to read files #{@macaw.config[
|
|
112
|
-
#{@macaw.config[
|
|
110
|
+
@macaw_log&.error("It was not possible to read files #{@macaw.config['macaw']['ssl']['cert_file_name']} and
|
|
111
|
+
#{@macaw.config['macaw']['ssl']['key_file_name']}. Please assure the files exist and their names are correct.")
|
|
113
112
|
@macaw_log&.error(e.backtrace)
|
|
114
113
|
raise e
|
|
115
114
|
end
|
|
116
115
|
|
|
117
116
|
def set_session
|
|
117
|
+
return unless @macaw.session
|
|
118
|
+
|
|
118
119
|
@session ||= {}
|
|
119
|
-
inv = if @macaw.config&.dig(
|
|
120
|
-
MemoryInvalidationMiddleware.new(@macaw.config[
|
|
120
|
+
inv = if @macaw.config&.dig('macaw', 'session', 'invalidation_time')
|
|
121
|
+
MemoryInvalidationMiddleware.new(@macaw.config['macaw']['session']['invalidation_time'])
|
|
121
122
|
else
|
|
122
123
|
MemoryInvalidationMiddleware.new
|
|
123
124
|
end
|
|
@@ -127,7 +128,7 @@ module ServerBase
|
|
|
127
128
|
def set_features
|
|
128
129
|
@is_shutting_down = false
|
|
129
130
|
set_rate_limiting
|
|
130
|
-
set_session
|
|
131
|
+
set_session
|
|
131
132
|
set_ssl
|
|
132
133
|
end
|
|
133
134
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require
|
|
3
|
+
require_relative 'common/server_base'
|
|
4
|
+
require 'openssl'
|
|
5
5
|
|
|
6
6
|
##
|
|
7
7
|
# Class responsible for providing a default
|
|
@@ -74,8 +74,17 @@ class ThreadServer
|
|
|
74
74
|
|
|
75
75
|
##
|
|
76
76
|
# Method Responsible for closing the TCP server.
|
|
77
|
-
def
|
|
78
|
-
|
|
77
|
+
def shutdown
|
|
78
|
+
@is_shutting_down = true
|
|
79
|
+
loop do
|
|
80
|
+
break if @work_queue.empty?
|
|
81
|
+
|
|
82
|
+
sleep 0.1
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
@num_threads.times { @work_queue << :shutdown }
|
|
86
|
+
@workers.each(&:join)
|
|
87
|
+
@server.close
|
|
79
88
|
end
|
|
80
89
|
|
|
81
90
|
private
|
|
@@ -107,17 +116,4 @@ class ThreadServer
|
|
|
107
116
|
end
|
|
108
117
|
end
|
|
109
118
|
end
|
|
110
|
-
|
|
111
|
-
def shutdown
|
|
112
|
-
@is_shutting_down = true
|
|
113
|
-
loop do
|
|
114
|
-
break if @work_queue.empty?
|
|
115
|
-
|
|
116
|
-
sleep 0.1
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
@num_threads.times { @work_queue << :shutdown }
|
|
120
|
-
@workers.each(&:join)
|
|
121
|
-
@server.close
|
|
122
|
-
end
|
|
123
119
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: false
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'json'
|
|
4
4
|
|
|
5
5
|
##
|
|
6
6
|
# Module responsible for sanitizing log data
|
|
@@ -10,7 +10,7 @@ module LogDataFilter
|
|
|
10
10
|
|
|
11
11
|
def self.config
|
|
12
12
|
@config ||= begin
|
|
13
|
-
file_path =
|
|
13
|
+
file_path = 'application.json'
|
|
14
14
|
config = {
|
|
15
15
|
max_length: DEFAULT_MAX_LENGTH,
|
|
16
16
|
sensitive_fields: DEFAULT_SENSITIVE_FIELDS
|
|
@@ -19,10 +19,10 @@ module LogDataFilter
|
|
|
19
19
|
if File.exist?(file_path)
|
|
20
20
|
json = JSON.parse(File.read(file_path))
|
|
21
21
|
|
|
22
|
-
if json[
|
|
23
|
-
log_config = json[
|
|
24
|
-
config[:max_length] = log_config[
|
|
25
|
-
config[:sensitive_fields] = log_config[
|
|
22
|
+
if json['macaw'] && json['macaw']['log']
|
|
23
|
+
log_config = json['macaw']['log']
|
|
24
|
+
config[:max_length] = log_config['max_length'] if log_config['max_length']
|
|
25
|
+
config[:sensitive_fields] = log_config['sensitive_fields'] if log_config['sensitive_fields']
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -31,11 +31,11 @@ module LogDataFilter
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def self.sanitize_for_logging(data, sensitive_fields: config[:sensitive_fields])
|
|
34
|
-
return
|
|
34
|
+
return '' if data.nil?
|
|
35
35
|
|
|
36
|
-
data = data.to_s.force_encoding(
|
|
36
|
+
data = data.to_s.force_encoding('UTF-8')
|
|
37
37
|
data = data.slice(0, config[:max_length])
|
|
38
|
-
data = data.gsub(
|
|
38
|
+
data = data.gsub('\\', '')
|
|
39
39
|
|
|
40
40
|
sensitive_fields.each do |field|
|
|
41
41
|
next unless data.include?(field.to_s)
|