macaw_framework 1.0.0 → 1.0.1
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 +3 -0
- data/CHANGELOG.md +6 -0
- data/README.md +85 -44
- data/SECURITY.md +13 -0
- data/lib/macaw_framework/aspects/cache_aspect.rb +2 -2
- data/lib/macaw_framework/core/server.rb +46 -12
- data/lib/macaw_framework/middlewares/{caching_middleware.rb → memory_invalidation_middleware.rb} +1 -1
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +2 -2
- data/sig/{caching_middleware.rbs → memory_invalidation_middleware.rbs} +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03ac2a8df24de8757381abc1e9a85f7854ac71a49b44d51aa7a23f1d42f95380
|
4
|
+
data.tar.gz: 4431ce9ab63887660aaa5cc464c00a1dd3a81b08cf4f99bb53926d0aec440fc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f981191cafd8504428de742979979a82ce1609c69ab08afc9f0b8e131381d12cc298b9b7447679b1611719605b825fcf409c9b4afee2b0d0f3a4fa8f28b5c79
|
7
|
+
data.tar.gz: cda629f9c4c779d11712c18dd5f9b7c8c90805688630d75c14b7fcd7c485b3cdcb2ea2386b5f6bcde62296a7b283337ee45418a08b67e61686f23ef18bf56bb2
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -41,3 +41,9 @@
|
|
41
41
|
- Implemented a middleware for rate limiting to prevent DoS attacks
|
42
42
|
- Improvement of caching strategy to ignore optional headers
|
43
43
|
- First production-ready version
|
44
|
+
|
45
|
+
## [1.0.1] - 2023-05-03
|
46
|
+
|
47
|
+
- Introducing server-side session management
|
48
|
+
- Fixing a bug with cache
|
49
|
+
- Improving README
|
data/README.md
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
# MacawFramework
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
MacawFramework is a lightweight, easy-to-use web framework for Ruby designed to simplify the development of small to
|
4
|
+
medium-sized web applications. With support for various HTTP methods, caching, and session management, MacawFramework
|
5
|
+
provides developers with the essential tools to quickly build and deploy their applications.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Simple routing with support for GET, POST, PUT, PATCH, and DELETE HTTP methods
|
10
|
+
- Caching middleware for improved performance
|
11
|
+
- Session management with server-side in-memory storage
|
12
|
+
- Basic rate limiting and SSL support
|
13
|
+
- Prometheus integration for monitoring and metrics
|
14
|
+
- Lightweight and easy to learn
|
6
15
|
|
7
16
|
## Installation
|
8
17
|
|
@@ -16,10 +25,56 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
25
|
|
17
26
|
## Usage
|
18
27
|
|
19
|
-
|
28
|
+
### Basic routing: Define routes with support for GET, POST, PUT, PATCH, and DELETE HTTP methods
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'macaw_framework'
|
32
|
+
|
33
|
+
m = MacawFramework::Macaw.new
|
34
|
+
|
35
|
+
m.get('/hello_world') do |_context|
|
36
|
+
return "Hello, World!", 200, {"Content-Type" => "text/plain"}
|
37
|
+
end
|
38
|
+
|
39
|
+
m.post('/submit_data/:path_variable') do |context|
|
40
|
+
context[:body] # Client body data
|
41
|
+
context[:params] # Client params, like url parameters or variables
|
42
|
+
context[:headers] # Client headers
|
43
|
+
context[:params][:path_variable] # The defined path variable can be found in :params
|
44
|
+
context[:client] # Client session
|
45
|
+
end
|
46
|
+
|
47
|
+
m.start!
|
48
|
+
|
49
|
+
```
|
50
|
+
|
51
|
+
### Caching: Improve performance by caching responses and configuring cache invalidation
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
m.get('/cached_data', cache: true) do |context|
|
55
|
+
# Retrieve data
|
56
|
+
end
|
57
|
+
```
|
20
58
|
|
21
|
-
|
22
|
-
|
59
|
+
### Session management: Handle user sessions securely with server-side in-memory storage
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
m.get('/login') do |context|
|
63
|
+
# Authenticate user
|
64
|
+
context[:session][:user_id] = user_id
|
65
|
+
end
|
66
|
+
|
67
|
+
m.get('/dashboard') do |context|
|
68
|
+
# Check if the user is logged in
|
69
|
+
if context[:session][:user_id]
|
70
|
+
# Show dashboard
|
71
|
+
else
|
72
|
+
# Redirect to login
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
### Configuration: Customize various aspects of the framework through the application.json configuration file, such as rate limiting, SSL support, and Prometheus integration
|
23
78
|
|
24
79
|
```json
|
25
80
|
{
|
@@ -28,68 +83,54 @@ in the same directory of the script that will start the application with the fol
|
|
28
83
|
"bind": "localhost",
|
29
84
|
"threads": 10,
|
30
85
|
"cache": {
|
31
|
-
"cache_invalidation": 3600
|
86
|
+
"cache_invalidation": 3600,
|
87
|
+
"ignore_headers": [
|
88
|
+
"header-to-be-ignored-from-caching-strategy",
|
89
|
+
"another-header-to-be-ignored-from-caching-strategy"
|
90
|
+
]
|
32
91
|
},
|
33
92
|
"prometheus": {
|
34
93
|
"endpoint": "/metrics"
|
35
94
|
},
|
36
95
|
"rate_limiting": {
|
37
96
|
"window": 10,
|
38
|
-
"max_requests": 3
|
39
|
-
"ignore_headers": [
|
40
|
-
"header-to-be-ignored-from-caching-strategy",
|
41
|
-
"another-header-to-be-ignored-from-caching-strategy"
|
42
|
-
]
|
97
|
+
"max_requests": 3
|
43
98
|
},
|
44
99
|
"ssl": {
|
45
|
-
"
|
46
|
-
|
47
|
-
"key_file_name": "path/to/cert/key/file.key"
|
48
|
-
}
|
100
|
+
"cert_file_name": "path/to/cert/file/file.crt",
|
101
|
+
"key_file_name": "path/to/cert/key/file.key"
|
49
102
|
}
|
50
103
|
}
|
51
104
|
}
|
52
105
|
```
|
53
106
|
|
107
|
+
### Monitoring: Easily monitor your application performance and metrics with built-in Prometheus support
|
108
|
+
|
109
|
+
```shell
|
110
|
+
curl http://localhost:8080/metrics
|
111
|
+
```
|
112
|
+
|
113
|
+
### Tips
|
114
|
+
|
54
115
|
Cache invalidation time should be specified in seconds. In order to enable caching, The application.json file
|
55
116
|
should exist in the app main directory and it need the `cache_invalidation` config set. It is possible to
|
56
117
|
provide a list of strings in the property `ignore_headers`. All the client headers with the same name of any
|
57
118
|
of the strings provided will be ignored from caching strategy. This is useful to exclude headers like
|
58
119
|
correlation IDs from the caching strategy.
|
59
120
|
|
60
|
-
|
61
|
-
exists inside `application.json`.
|
62
|
-
|
63
|
-
If the SSL configuration is provided in the `application.json` file with valid certificate and key files, the TCP server
|
64
|
-
will be wrapped with HTTPS security using the provided certificate.
|
65
|
-
|
66
|
-
Example of usage:
|
121
|
+
URL parameters like `...endOfUrl?key1=value1&key2=value2` can be find in the `context[:params]`
|
67
122
|
|
68
123
|
```ruby
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
m = MacawFramework::Macaw.new
|
73
|
-
|
74
|
-
m.get('/hello_world', cache: true) do |context|
|
75
|
-
context[:body] # Returns the request body as string
|
76
|
-
context[:params] # Returns query parameters and path variables as a hash
|
77
|
-
context[:headers] # Returns headers as a hash
|
78
|
-
return JSON.pretty_generate({ hello_message: 'Hello World!' }), 200, {"Content-Type" => "application/json"}
|
79
|
-
end
|
80
|
-
|
81
|
-
m.post('/hello_world/:path_variable') do |context|
|
82
|
-
context[:body] # Returns the request body as string
|
83
|
-
context[:params] # Returns query parameters and path variables as a hash
|
84
|
-
context[:headers] # Returns headers as a hash
|
85
|
-
context[:params][:path_variable] # The defined path variable can be found in :params
|
86
|
-
return JSON.pretty_generate({ hello_message: 'Hello World!' }), 200
|
124
|
+
m.get('/test_params') do |context|
|
125
|
+
context[:params]["key1"] # returns: value1
|
87
126
|
end
|
88
|
-
|
89
|
-
m.start!
|
90
127
|
```
|
91
128
|
|
92
|
-
|
129
|
+
Rate Limit window should also be specified in seconds. Rate limit will be activated only if the `rate_limiting` config
|
130
|
+
exists inside `application.json`.
|
131
|
+
|
132
|
+
If the SSL configuration is provided in the `application.json` file with valid certificate and key files, the TCP server
|
133
|
+
will be wrapped with HTTPS security using the provided certificate.
|
93
134
|
|
94
135
|
If prometheus is enabled, a get endpoint will be defined at path `/metrics` to collect prometheus metrics. This path
|
95
136
|
is configurable via the `application.json` file.
|
data/SECURITY.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Supported Versions
|
4
|
+
|
5
|
+
| Version | Supported |
|
6
|
+
| ------- | ------------------ |
|
7
|
+
| 1.0.x | :white_check_mark: |
|
8
|
+
| < 1.x | :x: |
|
9
|
+
|
10
|
+
|
11
|
+
## Reporting a Vulnerability
|
12
|
+
|
13
|
+
If you find a vulnerability, please open an issue or send an e-mail to aria.diniz.dev@gmail.com
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# Aspect that provide cache for the endpoints.
|
5
5
|
module CacheAspect
|
6
6
|
def call_endpoint(cache, *args)
|
7
|
-
return super(*args) unless !cache[:cache].nil? && cache[:endpoints_to_cache]
|
7
|
+
return super(*args) unless !cache[:cache].nil? && cache[:endpoints_to_cache]&.include?(args[0])
|
8
8
|
|
9
9
|
cache_filtered_name = cache_name_filter(args[1], cache[:ignored_headers])
|
10
10
|
|
@@ -20,7 +20,7 @@ module CacheAspect
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def cache_name_filter(client_data, ignored_headers)
|
23
|
-
filtered_headers = client_data[:headers].filter { |key, _value| !ignored_headers
|
23
|
+
filtered_headers = client_data[:headers].filter { |key, _value| !ignored_headers&.include?(key) }
|
24
24
|
[{ body: client_data[:body], params: client_data[:params], headers: filtered_headers }].to_s.to_sym
|
25
25
|
end
|
26
26
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "../middlewares/rate_limiter_middleware"
|
4
4
|
require_relative "../data_filters/response_data_filter"
|
5
|
+
require_relative "../middlewares/memory_invalidation_middleware"
|
5
6
|
require_relative "../errors/too_many_requests_error"
|
6
7
|
require_relative "../aspects/prometheus_aspect"
|
7
8
|
require_relative "../aspects/logging_aspect"
|
@@ -24,7 +25,7 @@ class Server
|
|
24
25
|
# @param {Integer} port
|
25
26
|
# @param {String} bind
|
26
27
|
# @param {Integer} num_threads
|
27
|
-
# @param {
|
28
|
+
# @param {MemoryInvalidationMiddleware} cache
|
28
29
|
# @param {Prometheus::Client:Registry} prometheus
|
29
30
|
# @return {Server}
|
30
31
|
def initialize(macaw, endpoints_to_cache = nil, cache = nil, prometheus = nil, prometheus_mw = nil)
|
@@ -34,8 +35,8 @@ class Server
|
|
34
35
|
@macaw_log = macaw.macaw_log
|
35
36
|
@num_threads = macaw.threads
|
36
37
|
@work_queue = Queue.new
|
37
|
-
ignored_headers =
|
38
|
-
|
38
|
+
ignored_headers = set_cache_ignored_h
|
39
|
+
set_features
|
39
40
|
@rate_limit ||= nil
|
40
41
|
ignored_headers ||= nil
|
41
42
|
@cache = { cache: cache, endpoints_to_cache: endpoints_to_cache || [], ignored_headers: ignored_headers }
|
@@ -86,11 +87,12 @@ class Server
|
|
86
87
|
raise EndpointNotMappedError unless @macaw.respond_to?(method_name)
|
87
88
|
raise TooManyRequestsError unless @rate_limit.nil? || @rate_limit.allow?(client.peeraddr[3])
|
88
89
|
|
90
|
+
declare_client_session(client)
|
89
91
|
client_data = get_client_data(body, headers, parameters)
|
90
92
|
|
91
93
|
@macaw_log.info("Running #{path.gsub("\n", "").gsub("\r", "")}")
|
92
94
|
message, status, response_headers = call_endpoint(@prometheus_middleware, @macaw_log, @cache,
|
93
|
-
method_name, client_data)
|
95
|
+
method_name, client_data, client.peeraddr[3])
|
94
96
|
status ||= 200
|
95
97
|
message ||= nil
|
96
98
|
response_headers ||= nil
|
@@ -106,13 +108,24 @@ class Server
|
|
106
108
|
client.close
|
107
109
|
end
|
108
110
|
|
111
|
+
def declare_client_session(client)
|
112
|
+
@session[client.peeraddr[3]] ||= [{}, Time.now]
|
113
|
+
@session[client.peeraddr[3]] = [{}, Time.now] if @session[client.peeraddr[3]][0].nil?
|
114
|
+
end
|
115
|
+
|
109
116
|
def set_rate_limiting
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
return unless @macaw.config&.dig("macaw", "rate_limiting")
|
118
|
+
|
119
|
+
@rate_limit = RateLimiterMiddleware.new(
|
120
|
+
@macaw.config["macaw"]["rate_limiting"]["window"].to_i || 1,
|
121
|
+
@macaw.config["macaw"]["rate_limiting"]["max_requests"].to_i || 60
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def set_cache_ignored_h
|
126
|
+
ignored_headers = []
|
127
|
+
if @macaw.config&.dig("macaw", "cache", "ignored_headers")
|
128
|
+
ignored_headers = @macaw.config["macaw"]["cache"]["ignore_headers"] || []
|
116
129
|
end
|
117
130
|
ignored_headers
|
118
131
|
end
|
@@ -131,10 +144,31 @@ class Server
|
|
131
144
|
raise e
|
132
145
|
end
|
133
146
|
|
134
|
-
def
|
147
|
+
def set_session
|
148
|
+
@session = {}
|
149
|
+
inv = if @macaw.config&.dig("macaw", "session", "invalidation_time")
|
150
|
+
MemoryInvalidationMiddleware.new(@macaw.config["macaw"]["session"]["invalidation_time"])
|
151
|
+
else
|
152
|
+
MemoryInvalidationMiddleware.new
|
153
|
+
end
|
154
|
+
inv.cache = @session
|
155
|
+
end
|
156
|
+
|
157
|
+
def set_features
|
158
|
+
set_rate_limiting
|
159
|
+
set_session
|
160
|
+
set_ssl
|
161
|
+
end
|
162
|
+
|
163
|
+
def call_endpoint(name, client_data, client_ip)
|
135
164
|
@macaw.send(
|
136
165
|
name.to_sym,
|
137
|
-
{
|
166
|
+
{
|
167
|
+
headers: client_data[:headers],
|
168
|
+
body: client_data[:body],
|
169
|
+
params: client_data[:parameters],
|
170
|
+
client: @session[client_ip][0]
|
171
|
+
}
|
138
172
|
)
|
139
173
|
end
|
140
174
|
|
data/lib/macaw_framework.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative "macaw_framework/errors/endpoint_not_mapped_error"
|
4
4
|
require_relative "macaw_framework/middlewares/prometheus_middleware"
|
5
5
|
require_relative "macaw_framework/data_filters/request_data_filtering"
|
6
|
-
require_relative "macaw_framework/middlewares/
|
6
|
+
require_relative "macaw_framework/middlewares/memory_invalidation_middleware"
|
7
7
|
require_relative "macaw_framework/core/server"
|
8
8
|
require_relative "macaw_framework/version"
|
9
9
|
require "prometheus/client"
|
@@ -31,7 +31,7 @@ module MacawFramework
|
|
31
31
|
@bind = @config["macaw"]["bind"] || "localhost"
|
32
32
|
@threads = @config["macaw"]["threads"] || 5
|
33
33
|
unless @config["macaw"]["cache"].nil?
|
34
|
-
@cache =
|
34
|
+
@cache = MemoryInvalidationMiddleware.new(@config["macaw"]["cache"]["cache_invalidation"].to_i || 3_600)
|
35
35
|
end
|
36
36
|
@prometheus = Prometheus::Client::Registry.new if @config["macaw"]["prometheus"]
|
37
37
|
@prometheus_middleware = PrometheusMiddleware.new if @config["macaw"]["prometheus"]
|
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.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aria Diniz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prometheus-client
|
@@ -24,7 +24,9 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.1'
|
27
|
-
description:
|
27
|
+
description: |-
|
28
|
+
A lightweight web framework designed for building efficient backend applications. Initially
|
29
|
+
created for study purposes, now production-ready and open for contributions.
|
28
30
|
email:
|
29
31
|
- aria.diniz.dev@gmail.com
|
30
32
|
executables: []
|
@@ -38,6 +40,7 @@ files:
|
|
38
40
|
- LICENSE.txt
|
39
41
|
- README.md
|
40
42
|
- Rakefile
|
43
|
+
- SECURITY.md
|
41
44
|
- lib/macaw_framework.rb
|
42
45
|
- lib/macaw_framework/aspects/cache_aspect.rb
|
43
46
|
- lib/macaw_framework/aspects/logging_aspect.rb
|
@@ -47,17 +50,17 @@ files:
|
|
47
50
|
- lib/macaw_framework/data_filters/response_data_filter.rb
|
48
51
|
- lib/macaw_framework/errors/endpoint_not_mapped_error.rb
|
49
52
|
- lib/macaw_framework/errors/too_many_requests_error.rb
|
50
|
-
- lib/macaw_framework/middlewares/
|
53
|
+
- lib/macaw_framework/middlewares/memory_invalidation_middleware.rb
|
51
54
|
- lib/macaw_framework/middlewares/prometheus_middleware.rb
|
52
55
|
- lib/macaw_framework/middlewares/rate_limiter_middleware.rb
|
53
56
|
- lib/macaw_framework/utils/http_status_code.rb
|
54
57
|
- lib/macaw_framework/version.rb
|
55
58
|
- macaw_logo.png
|
56
|
-
- sig/caching_middleware.rbs
|
57
59
|
- sig/http_status_code.rbs
|
58
60
|
- sig/logging_aspect.rbs
|
59
61
|
- sig/macaw_framework.rbs
|
60
62
|
- sig/macaw_framework/macaw.rbs
|
63
|
+
- sig/memory_invalidation_middleware.rbs
|
61
64
|
- sig/request_data_filtering.rbs
|
62
65
|
- sig/server.rbs
|
63
66
|
homepage: https://github.com/ariasdiniz/macaw_framework
|
@@ -85,5 +88,5 @@ requirements: []
|
|
85
88
|
rubygems_version: 3.4.12
|
86
89
|
signing_key:
|
87
90
|
specification_version: 4
|
88
|
-
summary: A web framework
|
91
|
+
summary: A lightweight back-end web framework
|
89
92
|
test_files: []
|