macaw_framework 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b35cc5b2fba0adc8ded0f76db41699323cc3957d54382d19e554d0e3dc77f51
4
- data.tar.gz: bcd983f6afe8b8864b5d2c012c120ead8290b521ed66f49477f8defb7eed2ede
3
+ metadata.gz: 6e3d33329ff53ab220c857c255d70b687d50f0703054ecef2866a17676867eed
4
+ data.tar.gz: 2c340f529123646d86a261630f2bcb5bb89dbbfda10a65f4820f1db8ece73504
5
5
  SHA512:
6
- metadata.gz: 9a7f7ef95d76e906b706a036d8cb299ba1b5e3be607ba8cdc13da216b11665b019635bca062ac83fc3067a18ab36148df12d3af2bdbc0fbe42fa3a8e0cb2c018
7
- data.tar.gz: 56a97dff9ddac5f4142b82d441de3264d4044ace0f8f9496a2e0f49a8e384c6cf57ed705bcf68dd051c536b0dc4e4b9a643161f6563a7bcfe095f2b6f691e5ff
6
+ metadata.gz: 79abf5fdf0729ef0464245c8765c88516b5445bff3123160e8f84fe1e43fa5360f311fceb731cb9f2823c49ac0caac5a78e3f35812ef3fe5e38cec2ab373cd81
7
+ data.tar.gz: db3b2bec7d0999497c752d9d1664d2bd99e0b35bb1bd67f95d5e529c7a46950ae62a02334f65016b4a947373a4a3871d5aa75832f33765e930dc7c0c4367dd45
data/.rubocop.yml CHANGED
@@ -26,3 +26,6 @@ Metrics/ParameterLists:
26
26
 
27
27
  Metrics/PerceivedComplexity:
28
28
  Enabled: false
29
+
30
+ Metrics/ClassLength:
31
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -41,3 +41,15 @@
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
50
+
51
+ ## [1.0.2] - 2023-05-06
52
+
53
+ - Fixing a bug with cache where ignored_headers where not being properly loaded
54
+ - Fixed a bug with cache where URL parameters were not being considered in the strategy
55
+ - Updating SECURITY.md with more information
data/README.md CHANGED
@@ -1,8 +1,17 @@
1
1
  # MacawFramework
2
2
 
3
- This is a framework for developing web applications. Please have in mind that this is still a work in progress and
4
- it is strongly advised to not use it for production purposes for now. Actually it supports only HTTP. and HTTPS/SSL
5
- support will be implemented soon. Anyone who wishes to contribute is welcome.
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,59 @@ If bundler is not being used to manage dependencies, install the gem by executin
16
25
 
17
26
  ## Usage
18
27
 
19
- The usage of the framework still very simple. Actually it support 5 HTTP verbs: GET, POST, PUT, PATCH and DELETE.
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
+ ```
20
50
 
21
- The default server port is 8080. To choose a different port, create a file with the name `application.json`
22
- in the same directory of the script that will start the application with the following content:
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
+ ```
58
+
59
+ Observation: To activate caching you also have to set it's properties on the application.json file. If you don't, caching strategy will not work.
60
+ See section below for configurations.
61
+
62
+ ### Session management: Handle user sessions securely with server-side in-memory storage
63
+
64
+ ```ruby
65
+ m.get('/login') do |context|
66
+ # Authenticate user
67
+ context[:client][:user_id] = user_id
68
+ end
69
+
70
+ m.get('/dashboard') do |context|
71
+ # Check if the user is logged in
72
+ if context[:client][:user_id]
73
+ # Show dashboard
74
+ else
75
+ # Redirect to login
76
+ end
77
+ end
78
+ ```
79
+
80
+ ### Configuration: Customize various aspects of the framework through the application.json configuration file, such as rate limiting, SSL support, and Prometheus integration
23
81
 
24
82
  ```json
25
83
  {
@@ -28,68 +86,54 @@ in the same directory of the script that will start the application with the fol
28
86
  "bind": "localhost",
29
87
  "threads": 10,
30
88
  "cache": {
31
- "cache_invalidation": 3600
89
+ "cache_invalidation": 3600,
90
+ "ignore_headers": [
91
+ "header-to-be-ignored-from-caching-strategy",
92
+ "another-header-to-be-ignored-from-caching-strategy"
93
+ ]
32
94
  },
33
95
  "prometheus": {
34
96
  "endpoint": "/metrics"
35
97
  },
36
98
  "rate_limiting": {
37
99
  "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
- ]
100
+ "max_requests": 3
43
101
  },
44
102
  "ssl": {
45
- "ssl": {
46
- "cert_file_name": "path/to/cert/file/file.crt",
47
- "key_file_name": "path/to/cert/key/file.key"
48
- }
103
+ "cert_file_name": "path/to/cert/file/file.crt",
104
+ "key_file_name": "path/to/cert/key/file.key"
49
105
  }
50
106
  }
51
107
  }
52
108
  ```
53
109
 
110
+ ### Monitoring: Easily monitor your application performance and metrics with built-in Prometheus support
111
+
112
+ ```shell
113
+ curl http://localhost:8080/metrics
114
+ ```
115
+
116
+ ### Tips
117
+
54
118
  Cache invalidation time should be specified in seconds. In order to enable caching, The application.json file
55
119
  should exist in the app main directory and it need the `cache_invalidation` config set. It is possible to
56
120
  provide a list of strings in the property `ignore_headers`. All the client headers with the same name of any
57
121
  of the strings provided will be ignored from caching strategy. This is useful to exclude headers like
58
122
  correlation IDs from the caching strategy.
59
123
 
60
- Rate Limit window should also be specified in seconds. Rate limit will be activated only if the `rate_limiting` config
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:
124
+ URL parameters like `...endOfUrl?key1=value1&key2=value2` can be find in the `context[:params]`
67
125
 
68
126
  ```ruby
69
- require 'macaw_framework'
70
- require 'json'
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
127
+ m.get('/test_params') do |context|
128
+ context[:params]["key1"] # returns: value1
87
129
  end
88
-
89
- m.start!
90
130
  ```
91
131
 
92
- The example above starts a server and creates a GET endpoint at localhost/hello_world.
132
+ Rate Limit window should also be specified in seconds. Rate limit will be activated only if the `rate_limiting` config
133
+ exists inside `application.json`.
134
+
135
+ If the SSL configuration is provided in the `application.json` file with valid certificate and key files, the TCP server
136
+ will be wrapped with HTTPS security using the provided certificate.
93
137
 
94
138
  If prometheus is enabled, a get endpoint will be defined at path `/metrics` to collect prometheus metrics. This path
95
139
  is configurable via the `application.json` file.
data/SECURITY.md ADDED
@@ -0,0 +1,27 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ We are committed to addressing security issues in a timely manner. The following versions of MacawFramework are currently supported with security updates:
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ | 1.0.x | :white_check_mark: |
10
+ | < 1.x | :x: |
11
+
12
+ ## Reporting a Vulnerability
13
+
14
+ We encourage responsible disclosure of security vulnerabilities. If you find a vulnerability in MacawFramework, please follow the steps below:
15
+
16
+ 1. Open an issue on the [GitHub repository](https://github.com/ariasdiniz/macaw_framework/issues) describing the vulnerability. Please include as much detail as possible, such as the affected version, the steps to reproduce the issue, and the potential impact of the vulnerability.
17
+
18
+ Alternatively, you can send an email to aria.diniz.dev@gmail.com with the same information.
19
+
20
+ 2. We will review and acknowledge the report within a reasonable time frame. We may ask for additional information or guidance to help us understand and reproduce the issue.
21
+
22
+ 3. We will work on addressing the vulnerability and will provide updates on the progress.
23
+
24
+ 4. Once the issue is resolved, we will release a new version of MacawFramework with the necessary security fixes.
25
+
26
+ Please remember to follow the project's [Code of Conduct](https://github.com/ariasdiniz/macaw_framework/blob/main/CODE_OF_CONDUCT.md) when reporting security vulnerabilities.
27
+
@@ -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].include?(args[0])
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.include?(key) }
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 {CachingMiddleware} cache
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 = set_rate_limiting
38
- set_ssl
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,15 +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
- if @macaw.config&.dig("macaw", "rate_limiting")
111
- ignored_headers = @macaw.config["macaw"]["rate_limiting"]["ignore_headers"] || []
112
- @rate_limit = RateLimiterMiddleware.new(
113
- @macaw.config["macaw"]["rate_limiting"]["window"].to_i || 1,
114
- @macaw.config["macaw"]["rate_limiting"]["max_requests"].to_i || 60
115
- )
116
- end
117
- ignored_headers
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
+ return unless @macaw.config&.dig("macaw", "cache", "ignore_headers")
127
+
128
+ @macaw.config["macaw"]["cache"]["ignore_headers"] || []
118
129
  end
119
130
 
120
131
  def set_ssl
@@ -131,14 +142,35 @@ class Server
131
142
  raise e
132
143
  end
133
144
 
134
- def call_endpoint(name, client_data)
145
+ def set_session
146
+ @session = {}
147
+ inv = if @macaw.config&.dig("macaw", "session", "invalidation_time")
148
+ MemoryInvalidationMiddleware.new(@macaw.config["macaw"]["session"]["invalidation_time"])
149
+ else
150
+ MemoryInvalidationMiddleware.new
151
+ end
152
+ inv.cache = @session
153
+ end
154
+
155
+ def set_features
156
+ set_rate_limiting
157
+ set_session
158
+ set_ssl
159
+ end
160
+
161
+ def call_endpoint(name, client_data, client_ip)
135
162
  @macaw.send(
136
163
  name.to_sym,
137
- { headers: client_data[:headers], body: client_data[:body], params: client_data[:parameters] }
164
+ {
165
+ headers: client_data[:headers],
166
+ body: client_data[:body],
167
+ params: client_data[:params],
168
+ client: @session[client_ip][0]
169
+ }
138
170
  )
139
171
  end
140
172
 
141
173
  def get_client_data(body, headers, parameters)
142
- { body: body, headers: headers, parameters: parameters }
174
+ { body: body, headers: headers, params: parameters }
143
175
  end
144
176
  end
@@ -3,7 +3,7 @@
3
3
  ##
4
4
  # Middleware responsible for storing and
5
5
  # invalidating cache.
6
- class CachingMiddleware
6
+ class MemoryInvalidationMiddleware
7
7
  attr_accessor :cache, :mutex
8
8
 
9
9
  def initialize(inv_time_seconds = 3_600)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MacawFramework
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.2"
5
5
  end
@@ -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/caching_middleware"
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 = CachingMiddleware.new(@config["macaw"]["cache"]["cache_invalidation"].to_i || 3_600)
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"]
data/main/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @ariasdiniz
@@ -1,4 +1,4 @@
1
- class CachingMiddleware
1
+ class MemoryInvalidationMiddleware
2
2
  @cache: Hash[String, Array[string]]
3
3
 
4
4
  attr_accessor cache: Hash[String, Array[string]]
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.0
4
+ version: 1.0.2
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-04-28 00:00:00.000000000 Z
11
+ date: 2023-05-07 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: A project started for study purpose that I intend to keep working on.
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,18 @@ 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/caching_middleware.rb
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
59
+ - main/CODEOWNERS
57
60
  - sig/http_status_code.rbs
58
61
  - sig/logging_aspect.rbs
59
62
  - sig/macaw_framework.rbs
60
63
  - sig/macaw_framework/macaw.rbs
64
+ - sig/memory_invalidation_middleware.rbs
61
65
  - sig/request_data_filtering.rbs
62
66
  - sig/server.rbs
63
67
  homepage: https://github.com/ariasdiniz/macaw_framework
@@ -85,5 +89,5 @@ requirements: []
85
89
  rubygems_version: 3.4.12
86
90
  signing_key:
87
91
  specification_version: 4
88
- summary: A web framework still in development.
92
+ summary: A lightweight back-end web framework
89
93
  test_files: []