macaw_framework 1.3.1 → 1.3.22
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/CHANGELOG.md +7 -0
- data/README.md +4 -3
- data/lib/macaw_framework/aspects/cache_aspect.rb +3 -3
- data/lib/macaw_framework/aspects/logging_aspect.rb +2 -5
- data/lib/macaw_framework/aspects/prometheus_aspect.rb +2 -2
- data/lib/macaw_framework/core/common/server_base.rb +1 -2
- data/lib/macaw_framework/core/thread_server.rb +11 -15
- data/lib/macaw_framework/data_filters/request_data_filtering.rb +18 -16
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +2 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6884907c34b2719e0166f6a3c7fc5992e277126ec738a6a08e3fbaa71c427b0a
|
4
|
+
data.tar.gz: 554b0671565902d395fa289ae970cb7b149f0b61f9dcebdfdd9947bc277807fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bde5aa848d982a6cd112c9f8eb57b84c39aaf30463f0a21e8ce79dfee2b54bc6711598da4abd3c0287a32f502123a48715700c68f937118fe0cdbb2c045733cd
|
7
|
+
data.tar.gz: 2248a9ea06a53f7602d366fdc2524a7c40866042102ec84fb6b1a724e9617175f2278bf2075fd6b9b7f9bc36626b64824f6cd0baee25ce6346f554bba9e76fd1
|
data/CHANGELOG.md
CHANGED
@@ -147,3 +147,10 @@
|
|
147
147
|
- Fixing bug where missing session configuration on `application.json` break the application
|
148
148
|
- Including a Cache module for manual caching.
|
149
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.3]
|
156
|
+
- Fixing error with tests on Ruby 3.4.x due to splash operator
|
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)
|
@@ -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
|
|
@@ -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
|
@@ -8,11 +8,8 @@ require_relative '../data_filters/log_data_filter'
|
|
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
|
|
@@ -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 ||= {}
|
@@ -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
|
@@ -11,7 +11,7 @@ module RequestDataFiltering
|
|
11
11
|
# Method responsible for extracting information
|
12
12
|
# provided by the client like Headers and Body
|
13
13
|
def self.parse_request_data(client, routes)
|
14
|
-
path, parameters = extract_url_parameters(client.gets
|
14
|
+
path, parameters = extract_url_parameters(client.gets&.gsub('HTTP/1.1', ''))
|
15
15
|
parameters = {} if parameters.nil?
|
16
16
|
|
17
17
|
method_name = sanitize_method_name(path)
|
@@ -26,15 +26,15 @@ module RequestDataFiltering
|
|
26
26
|
|
27
27
|
selected_route = nil
|
28
28
|
routes.each do |route|
|
29
|
-
split_route = route
|
30
|
-
split_name = method_name
|
29
|
+
split_route = route&.split('.')
|
30
|
+
split_name = method_name&.split('.')
|
31
31
|
|
32
|
-
next unless split_route
|
32
|
+
next unless split_route&.length == split_name&.length
|
33
33
|
next unless match_path_with_route(split_name, split_route)
|
34
34
|
|
35
35
|
selected_route = route
|
36
|
-
split_route
|
37
|
-
parameters[var[1..].to_sym] = split_name
|
36
|
+
split_route&.each_with_index do |var, index|
|
37
|
+
parameters[var[1..].to_sym] = split_name&.dig(index) if var =~ VARIABLE_PATTERN
|
38
38
|
end
|
39
39
|
break
|
40
40
|
end
|
@@ -45,7 +45,7 @@ module RequestDataFiltering
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def self.match_path_with_route(split_path, split_route)
|
48
|
-
split_route
|
48
|
+
split_route&.each_with_index do |var, index|
|
49
49
|
return false if var != split_path[index] && !var.match?(VARIABLE_PATTERN)
|
50
50
|
end
|
51
51
|
|
@@ -56,26 +56,28 @@ module RequestDataFiltering
|
|
56
56
|
# Method responsible for sanitizing the method name
|
57
57
|
def self.sanitize_method_name(path)
|
58
58
|
path = extract_path(path)
|
59
|
-
method_name = path
|
60
|
-
method_name
|
59
|
+
method_name = path&.gsub('/', '.')&.strip&.downcase
|
60
|
+
method_name&.gsub!(' ', '')
|
61
61
|
method_name
|
62
62
|
end
|
63
63
|
|
64
64
|
##
|
65
65
|
# Method responsible for extracting the path from URI
|
66
66
|
def self.extract_path(path)
|
67
|
+
return path if path.nil?
|
68
|
+
|
67
69
|
path[0] == '/' ? path[1..].gsub('/', '.') : path.gsub('/', '.')
|
68
70
|
end
|
69
71
|
|
70
72
|
##
|
71
73
|
# Method responsible for extracting the headers from request
|
72
74
|
def self.extract_headers(client)
|
73
|
-
header = client.gets
|
75
|
+
header = client.gets&.delete("\n")&.delete("\r")
|
74
76
|
headers = {}
|
75
|
-
while header
|
77
|
+
while header&.match(%r{[a-zA-Z0-9\-/*]*: [a-zA-Z0-9\-/*]})
|
76
78
|
split_header = header.split(':')
|
77
79
|
headers[split_header[0].strip] = split_header[1].strip
|
78
|
-
header = client.gets
|
80
|
+
header = client.gets&.delete("\n")&.delete("\r")
|
79
81
|
end
|
80
82
|
[header, headers]
|
81
83
|
end
|
@@ -83,7 +85,7 @@ module RequestDataFiltering
|
|
83
85
|
##
|
84
86
|
# Method responsible for extracting the body from request
|
85
87
|
def self.extract_body(client, body_first_line, content_length)
|
86
|
-
body = client
|
88
|
+
body = client&.read(content_length)
|
87
89
|
body_first_line << body.to_s
|
88
90
|
end
|
89
91
|
|
@@ -107,13 +109,13 @@ module RequestDataFiltering
|
|
107
109
|
##
|
108
110
|
# Method responsible for sanitizing the parameter name
|
109
111
|
def self.sanitize_parameter_name(name)
|
110
|
-
name
|
112
|
+
name&.gsub(/[^\w\s]/, '')
|
111
113
|
end
|
112
114
|
|
113
115
|
##
|
114
116
|
# Method responsible for sanitizing the parameter value
|
115
117
|
def self.sanitize_parameter_value(value)
|
116
|
-
value
|
117
|
-
value
|
118
|
+
value&.gsub(/[^\w\s]/, '')
|
119
|
+
value&.gsub(/\s/, '')
|
118
120
|
end
|
119
121
|
end
|
data/lib/macaw_framework.rb
CHANGED
@@ -155,11 +155,11 @@ module MacawFramework
|
|
155
155
|
rescue Interrupt
|
156
156
|
if @macaw_log.nil?
|
157
157
|
puts('Stopping server')
|
158
|
-
@server.
|
158
|
+
@server.shutdown
|
159
159
|
puts('Macaw stop flying for some seeds...')
|
160
160
|
else
|
161
161
|
@macaw_log.info('Stopping server')
|
162
|
-
@server.
|
162
|
+
@server.shutdown
|
163
163
|
@macaw_log.info('Macaw stop flying for some seeds...')
|
164
164
|
end
|
165
165
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: macaw_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aria Diniz
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prometheus-client
|
@@ -76,7 +76,7 @@ metadata:
|
|
76
76
|
documentation_uri: https://rubydoc.info/gems/macaw_framework
|
77
77
|
homepage_uri: https://github.com/ariasdiniz/macaw_framework
|
78
78
|
source_code_uri: https://github.com/ariasdiniz/macaw_framework
|
79
|
-
post_install_message:
|
79
|
+
post_install_message:
|
80
80
|
rdoc_options: []
|
81
81
|
require_paths:
|
82
82
|
- lib
|
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
92
|
version: '0'
|
93
93
|
requirements: []
|
94
94
|
rubygems_version: 3.4.10
|
95
|
-
signing_key:
|
95
|
+
signing_key:
|
96
96
|
specification_version: 4
|
97
97
|
summary: A lightweight back-end web framework
|
98
98
|
test_files: []
|