macaw_framework 0.1.1 → 0.1.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 +7 -1
- data/CHANGELOG.md +12 -1
- data/README.md +1 -1
- data/lib/macaw_framework/endpoint_not_mapped_error.rb +1 -1
- data/lib/macaw_framework/http_status_code.rb +61 -64
- data/lib/macaw_framework/request_data_filtering.rb +60 -0
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +33 -47
- data/sig/http_status_code.rbs +3 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46f8af6ed22b7980942f7ec3def269395d1e65e9c7bfd06a96d161c31666997d
|
|
4
|
+
data.tar.gz: 258e71f4b693c410c325e7a7751f8a2702e3b2db814752d0de39566fa1b94576
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc2b8fbd4d25b795df95a47025b7465b3cfb6bd340008bebdfe527232a009bad959e25f9c4528b7eb4ce98f14f6148a47dd44e56b19b4d4c23187a2b4a24b56c
|
|
7
|
+
data.tar.gz: 0d0f265ea93a3467718cbccd2ed93d85d0d98f9c84e53a821d3071b20243ca251e7670a788bd6e848f6381cee213c16ee8164dd27a32a2a28b77de5979a23326
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.
|
|
2
|
+
TargetRubyVersion: 2.7
|
|
3
3
|
|
|
4
4
|
Style/StringLiterals:
|
|
5
5
|
Enabled: true
|
|
@@ -11,3 +11,9 @@ Style/StringLiteralsInInterpolation:
|
|
|
11
11
|
|
|
12
12
|
Layout/LineLength:
|
|
13
13
|
Max: 120
|
|
14
|
+
|
|
15
|
+
Metrics/MethodLength:
|
|
16
|
+
Max: 30
|
|
17
|
+
|
|
18
|
+
Metrics/AbcSize:
|
|
19
|
+
Max: 35
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
- Initial release
|
|
6
6
|
|
|
7
|
-
## [0.1.
|
|
7
|
+
## [0.1.1] - 2022-12-06
|
|
8
8
|
|
|
9
9
|
- Adding support for headers and body
|
|
10
|
+
|
|
11
|
+
## [0.1.2] - 2022-12-10
|
|
12
|
+
|
|
13
|
+
- Adding support to URL parameters
|
|
14
|
+
- Adding logs to the framework activity
|
|
15
|
+
- Removing undefined Status Codes from http_status_code hash
|
|
16
|
+
- Moving methods from Macaw class to RequestDataFiltering module, respecting SOLID
|
|
17
|
+
|
|
18
|
+
## [0.1.3] - 2022-12-13
|
|
19
|
+
|
|
20
|
+
- Adding logger gem to Macaw class to fix a bug on the application start
|
data/README.md
CHANGED
|
@@ -7,69 +7,66 @@ module HttpStatusCode
|
|
|
7
7
|
##
|
|
8
8
|
# Http Status Code Map
|
|
9
9
|
HTTP_STATUS_CODE_MAP = {
|
|
10
|
-
100 =>
|
|
11
|
-
101 =>
|
|
12
|
-
102 =>
|
|
13
|
-
103 =>
|
|
14
|
-
200 =>
|
|
15
|
-
201 =>
|
|
16
|
-
202 =>
|
|
17
|
-
203 =>
|
|
18
|
-
204 =>
|
|
19
|
-
205 =>
|
|
20
|
-
206 =>
|
|
21
|
-
207 =>
|
|
22
|
-
208 =>
|
|
23
|
-
226 =>
|
|
24
|
-
300 =>
|
|
25
|
-
301 =>
|
|
26
|
-
302 =>
|
|
27
|
-
303 =>
|
|
28
|
-
304 =>
|
|
29
|
-
305 =>
|
|
30
|
-
307 =>
|
|
31
|
-
308 =>
|
|
32
|
-
400 =>
|
|
33
|
-
401 =>
|
|
34
|
-
402 =>
|
|
35
|
-
403 =>
|
|
36
|
-
404 =>
|
|
37
|
-
405 =>
|
|
38
|
-
406 =>
|
|
39
|
-
407 =>
|
|
40
|
-
408 =>
|
|
41
|
-
409 =>
|
|
42
|
-
410 =>
|
|
43
|
-
411 =>
|
|
44
|
-
412 =>
|
|
45
|
-
413 =>
|
|
46
|
-
414 =>
|
|
47
|
-
415 =>
|
|
48
|
-
416 =>
|
|
49
|
-
417 =>
|
|
50
|
-
421 =>
|
|
51
|
-
422 =>
|
|
52
|
-
423 =>
|
|
53
|
-
424 =>
|
|
54
|
-
425 =>
|
|
55
|
-
426 =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
509 => 'Unassigned',
|
|
72
|
-
510 => 'Not Extended (OBSOLETED)',
|
|
73
|
-
511 => 'Network Authentication Required'
|
|
10
|
+
100 => "Continue",
|
|
11
|
+
101 => "Switching Protocols",
|
|
12
|
+
102 => "Processing",
|
|
13
|
+
103 => "Early Hints",
|
|
14
|
+
200 => "OK",
|
|
15
|
+
201 => "Created",
|
|
16
|
+
202 => "Accepted",
|
|
17
|
+
203 => "Non-Authoritative Information",
|
|
18
|
+
204 => "No Content",
|
|
19
|
+
205 => "Reset Content",
|
|
20
|
+
206 => "Partial Content",
|
|
21
|
+
207 => "Multi-Status",
|
|
22
|
+
208 => "Already Reported",
|
|
23
|
+
226 => "IM Used",
|
|
24
|
+
300 => "Multiple Choices",
|
|
25
|
+
301 => "Moved Permanently",
|
|
26
|
+
302 => "Found",
|
|
27
|
+
303 => "See Other",
|
|
28
|
+
304 => "Not Modified",
|
|
29
|
+
305 => "Use Proxy",
|
|
30
|
+
307 => "Temporary Redirect",
|
|
31
|
+
308 => "Permanent Redirect",
|
|
32
|
+
400 => "Bad Request",
|
|
33
|
+
401 => "Unauthorized",
|
|
34
|
+
402 => "Payment Required",
|
|
35
|
+
403 => "Forbidden",
|
|
36
|
+
404 => "Not Found",
|
|
37
|
+
405 => "Method Not Allowed",
|
|
38
|
+
406 => "Not Acceptable",
|
|
39
|
+
407 => "Proxy Authentication Required",
|
|
40
|
+
408 => "Request Timeout",
|
|
41
|
+
409 => "Conflict",
|
|
42
|
+
410 => "Gone",
|
|
43
|
+
411 => "Length Required",
|
|
44
|
+
412 => "Precondition Failed",
|
|
45
|
+
413 => "Content Too Large",
|
|
46
|
+
414 => "URI Too Long",
|
|
47
|
+
415 => "Unsupported Media Type",
|
|
48
|
+
416 => "Range Not Satisfiable",
|
|
49
|
+
417 => "Expectation Failed",
|
|
50
|
+
421 => "Misdirected Request",
|
|
51
|
+
422 => "Unprocessable Content",
|
|
52
|
+
423 => "Locked",
|
|
53
|
+
424 => "Failed Dependency",
|
|
54
|
+
425 => "Too Early",
|
|
55
|
+
426 => "Upgrade Required",
|
|
56
|
+
428 => "Precondition Required",
|
|
57
|
+
429 => "Too Many Requests",
|
|
58
|
+
431 => "Request Header Fields Too Large",
|
|
59
|
+
451 => "Unavailable For Legal Reasons",
|
|
60
|
+
500 => "Internal Server Error",
|
|
61
|
+
501 => "Not Implemented",
|
|
62
|
+
502 => "Bad Gateway",
|
|
63
|
+
503 => "Service Unavailable",
|
|
64
|
+
504 => "Gateway Timeout",
|
|
65
|
+
505 => "HTTP Version Not Supported",
|
|
66
|
+
506 => "Variant Also Negotiates",
|
|
67
|
+
507 => "Insufficient Storage",
|
|
68
|
+
508 => "Loop Detected",
|
|
69
|
+
510 => "Not Extended (OBSOLETED)",
|
|
70
|
+
511 => "Network Authentication Required"
|
|
74
71
|
}.freeze
|
|
75
72
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Module containing methods to filter Strings
|
|
5
|
+
module RequestDataFiltering
|
|
6
|
+
##
|
|
7
|
+
# Method responsible for extracting information
|
|
8
|
+
# provided by the client like Headers and Body
|
|
9
|
+
def self.extract_client_info(client)
|
|
10
|
+
path, parameters = extract_url_parameters(client.gets.gsub("HTTP/1.1", ""))
|
|
11
|
+
method_name = path.gsub("/", "_").strip!.downcase
|
|
12
|
+
method_name.gsub!(" ", "")
|
|
13
|
+
body_first_line, headers = extract_headers(client)
|
|
14
|
+
body = extract_body(client, body_first_line, headers["Content-Length"].to_i)
|
|
15
|
+
[path, method_name, headers, body, parameters]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Method responsible for extracting the path from URI
|
|
20
|
+
def self.extract_path(path)
|
|
21
|
+
path[0] == "/" ? path[1..].gsub("/", "_") : path.gsub("/", "_")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Method responsible for extracting the headers from request
|
|
26
|
+
def self.extract_headers(client)
|
|
27
|
+
header = client.gets.delete("\n").delete("\r")
|
|
28
|
+
headers = {}
|
|
29
|
+
while header.match(%r{[a-zA-Z0-9\-/*]*: [a-zA-Z0-9\-/*]})
|
|
30
|
+
split_header = header.split(":")
|
|
31
|
+
headers[split_header[0]] = split_header[1][1..]
|
|
32
|
+
header = client.gets.delete("\n").delete("\r")
|
|
33
|
+
end
|
|
34
|
+
[header, headers]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Method responsible for extracting the body from request
|
|
39
|
+
def self.extract_body(client, body_first_line, content_length)
|
|
40
|
+
body = client.read(content_length)
|
|
41
|
+
body_first_line << body.to_s
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# Method responsible for extracting the parameters from URI
|
|
46
|
+
def self.extract_url_parameters(http_first_line)
|
|
47
|
+
return http_first_line, nil unless http_first_line =~ /\?/
|
|
48
|
+
|
|
49
|
+
path_and_parameters = http_first_line.split("?", 2)
|
|
50
|
+
path = "#{path_and_parameters[0]} "
|
|
51
|
+
parameters_array = path_and_parameters[1].split("&")
|
|
52
|
+
parameters_array.map! do |item|
|
|
53
|
+
split_item = item.split("=")
|
|
54
|
+
{ split_item[0] => split_item[1].gsub("\n", "").gsub("\r", "").gsub("\s", "") }
|
|
55
|
+
end
|
|
56
|
+
parameters = {}
|
|
57
|
+
parameters_array.each { |item| parameters.merge!(item) }
|
|
58
|
+
[path, parameters]
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/macaw_framework.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "macaw_framework/endpoint_not_mapped_error"
|
|
4
|
+
require_relative "macaw_framework/request_data_filtering"
|
|
4
5
|
require_relative "macaw_framework/http_status_code"
|
|
5
6
|
require_relative "macaw_framework/version"
|
|
7
|
+
require "logger"
|
|
6
8
|
require "socket"
|
|
7
9
|
require "json"
|
|
8
10
|
|
|
@@ -12,7 +14,9 @@ module MacawFramework
|
|
|
12
14
|
# starting the web server.
|
|
13
15
|
class Macaw
|
|
14
16
|
include(HttpStatusCode)
|
|
15
|
-
|
|
17
|
+
##
|
|
18
|
+
# @param {Logger} custom_log
|
|
19
|
+
def initialize(custom_log = nil)
|
|
16
20
|
begin
|
|
17
21
|
config = JSON.parse(File.read("application.json"))
|
|
18
22
|
@port = config["macaw"]["port"]
|
|
@@ -20,6 +24,7 @@ module MacawFramework
|
|
|
20
24
|
@port ||= 8080
|
|
21
25
|
end
|
|
22
26
|
@port ||= 8080
|
|
27
|
+
@macaw_log ||= custom_log.nil? ? Logger.new($stdout) : custom_log
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
##
|
|
@@ -29,8 +34,7 @@ module MacawFramework
|
|
|
29
34
|
# @param {Proc} block
|
|
30
35
|
# @return {Integer, String}
|
|
31
36
|
def get(path, &block)
|
|
32
|
-
|
|
33
|
-
define_singleton_method("get_#{path_clean}", block)
|
|
37
|
+
map_new_endpoint("get", path, &block)
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
##
|
|
@@ -40,8 +44,7 @@ module MacawFramework
|
|
|
40
44
|
# @param {Proc} block
|
|
41
45
|
# @return {String, Integer}
|
|
42
46
|
def post(path, &block)
|
|
43
|
-
|
|
44
|
-
define_singleton_method("post_#{path_clean}", block)
|
|
47
|
+
map_new_endpoint("post", path, &block)
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
##
|
|
@@ -51,8 +54,7 @@ module MacawFramework
|
|
|
51
54
|
# @param {Proc} block
|
|
52
55
|
# @return {String, Integer}
|
|
53
56
|
def put(path, &block)
|
|
54
|
-
|
|
55
|
-
define_singleton_method("put_#{path_clean}", block)
|
|
57
|
+
map_new_endpoint("put", path, &block)
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
##
|
|
@@ -62,8 +64,7 @@ module MacawFramework
|
|
|
62
64
|
# @param {Proc} block
|
|
63
65
|
# @return {String, Integer}
|
|
64
66
|
def patch(path, &block)
|
|
65
|
-
|
|
66
|
-
define_singleton_method("patch_#{path_clean}", block)
|
|
67
|
+
map_new_endpoint("patch", path, &block)
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
##
|
|
@@ -73,22 +74,33 @@ module MacawFramework
|
|
|
73
74
|
# @param {Proc} block
|
|
74
75
|
# @return {String, Integer}
|
|
75
76
|
def delete(path, &block)
|
|
76
|
-
|
|
77
|
-
define_singleton_method("delete_#{path_clean}", block)
|
|
77
|
+
map_new_endpoint("delete", path, &block)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
##
|
|
81
81
|
# Starts the web server
|
|
82
82
|
def start!
|
|
83
|
+
@macaw_log.info("Starting server at port #{@port}")
|
|
84
|
+
time = Time.now
|
|
83
85
|
server = TCPServer.open(@port)
|
|
84
|
-
|
|
86
|
+
@macaw_log.info("Server started in #{Time.now - time} seconds.")
|
|
87
|
+
server_loop(server)
|
|
88
|
+
rescue Interrupt
|
|
89
|
+
@macaw_log.info("Stopping server")
|
|
90
|
+
server.close
|
|
91
|
+
@macaw_log.info("Macaw stop flying for some seeds...")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def server_loop(server)
|
|
85
97
|
loop do
|
|
86
98
|
Thread.start(server.accept) do |client|
|
|
87
|
-
client
|
|
88
|
-
method_name, headers, body = extract_client_info(client)
|
|
99
|
+
path, method_name, headers, body, parameters = RequestDataFiltering.extract_client_info(client)
|
|
89
100
|
raise EndpointNotMappedError unless respond_to?(method_name)
|
|
90
101
|
|
|
91
|
-
|
|
102
|
+
@macaw_log.info("Running #{path.gsub("\n", "").gsub("\r", "")}")
|
|
103
|
+
message, status = send(method_name, headers, body, parameters)
|
|
92
104
|
status ||= 200
|
|
93
105
|
message ||= "Ok"
|
|
94
106
|
client.puts "HTTP/1.1 #{status} #{HTTP_STATUS_CODE_MAP[status]} \r\n\r\n#{message}"
|
|
@@ -96,44 +108,18 @@ module MacawFramework
|
|
|
96
108
|
rescue EndpointNotMappedError
|
|
97
109
|
client.print "HTTP/1.1 404 Not Found\r\n\r\n"
|
|
98
110
|
client.close
|
|
99
|
-
rescue StandardError
|
|
111
|
+
rescue StandardError => e
|
|
100
112
|
client.print "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
|
113
|
+
@macaw_log.info("Error: #{e}")
|
|
101
114
|
client.close
|
|
102
115
|
end
|
|
103
116
|
end
|
|
104
|
-
rescue Interrupt
|
|
105
|
-
puts "Macaw stop flying for some seeds..."
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
private
|
|
109
|
-
|
|
110
|
-
##
|
|
111
|
-
# Method responsible for extracting information
|
|
112
|
-
# provided by the client like Headers and Body
|
|
113
|
-
def extract_client_info(client)
|
|
114
|
-
method_name = client.gets.gsub("HTTP/1.1", "").gsub("/", "_").strip!.downcase
|
|
115
|
-
method_name.gsub!(" ", "")
|
|
116
|
-
body_first_line, headers = extract_headers(client)
|
|
117
|
-
body = extract_body(client, body_first_line, headers["Content-Length"].to_i)
|
|
118
|
-
[method_name, headers, body]
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
##
|
|
122
|
-
# Extract application headers
|
|
123
|
-
def extract_headers(client)
|
|
124
|
-
header = client.gets.delete("\n").delete("\r")
|
|
125
|
-
headers = {}
|
|
126
|
-
while header.match(%r{[a-zA-Z0-9\-/*]*: [a-zA-Z0-9\-/*]})
|
|
127
|
-
split_header = header.split(":")
|
|
128
|
-
headers[split_header[0]] = split_header[1][1..]
|
|
129
|
-
header = client.gets.delete("\n").delete("\r")
|
|
130
|
-
end
|
|
131
|
-
[header, headers]
|
|
132
117
|
end
|
|
133
118
|
|
|
134
|
-
def
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
def map_new_endpoint(prefix, path, &block)
|
|
120
|
+
path_clean = RequestDataFiltering.extract_path(path)
|
|
121
|
+
@macaw_log.info("Defining #{prefix.upcase} endpoint at /#{path}")
|
|
122
|
+
define_singleton_method("#{prefix}_#{path_clean}", block)
|
|
137
123
|
end
|
|
138
124
|
end
|
|
139
125
|
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: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aria Diniz
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-12-
|
|
11
|
+
date: 2022-12-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A project started for study purpose that I intend to keep working on.
|
|
14
14
|
email:
|
|
@@ -27,7 +27,9 @@ files:
|
|
|
27
27
|
- lib/macaw_framework.rb
|
|
28
28
|
- lib/macaw_framework/endpoint_not_mapped_error.rb
|
|
29
29
|
- lib/macaw_framework/http_status_code.rb
|
|
30
|
+
- lib/macaw_framework/request_data_filtering.rb
|
|
30
31
|
- lib/macaw_framework/version.rb
|
|
32
|
+
- sig/http_status_code.rbs
|
|
31
33
|
- sig/macaw_framework.rbs
|
|
32
34
|
- sig/macaw_framework/macaw.rbs
|
|
33
35
|
homepage: https://github.com/ariasdiniz/macaw_framework
|