low_loop 0.0.0 → 0.2.0
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/lib/factories/response_factory.rb +16 -0
- data/lib/low_loop.rb +48 -0
- data/lib/request_parser.rb +64 -0
- data/lib/response_builder.rb +16 -0
- data/lib/version.rb +2 -2
- metadata +76 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ca13498531cc4abf7f577bee5f78387d0f42fcbb414e5b20be39db848a154e5
|
|
4
|
+
data.tar.gz: '075782581f7fd32f7b7d077bbcf03101561560823f39a6f8ecd38ac312c3a363'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ddaf50e11f1eb8a5af18580932fd4d84117ac0754ea84a468e7e78772ab3894df04774428b10ea8d45ebdaaa2e240cbc95ff2ae8fbd6af6848b31b1e2b299ca0
|
|
7
|
+
data.tar.gz: a3bb6a044dda05bda5bf3f0b73cb1747c3b3c10275541651073b592a2cba3541b5a1d23d8e489d0a7828611e4fc4643c3dc1289c0a94a6d81a181ffe6ad03263
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'protocol/http'
|
|
4
|
+
|
|
5
|
+
module Low
|
|
6
|
+
class ResponseFactory
|
|
7
|
+
class << self
|
|
8
|
+
def response(body:)
|
|
9
|
+
headers = Protocol::HTTP::Headers.new(['content-type', 'text/html'])
|
|
10
|
+
body = Protocol::HTTP::Body::Buffered.wrap(body)
|
|
11
|
+
|
|
12
|
+
Protocol::HTTP::Response.new('http/1.1', 200, headers, body)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/low_loop.rb
CHANGED
|
@@ -1,2 +1,50 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'async'
|
|
4
|
+
require 'socket'
|
|
5
|
+
require 'low_type'
|
|
6
|
+
require 'low_event'
|
|
7
|
+
require 'observers'
|
|
8
|
+
|
|
9
|
+
require_relative 'factories/response_factory'
|
|
10
|
+
require_relative 'request_parser'
|
|
11
|
+
require_relative 'response_builder'
|
|
12
|
+
|
|
13
|
+
module Low
|
|
14
|
+
class Loop
|
|
15
|
+
include Observers
|
|
16
|
+
|
|
17
|
+
def start(config:)
|
|
18
|
+
server = TCPServer.new(config.host, config.port)
|
|
19
|
+
server.listen(10)
|
|
20
|
+
puts "Server@#{config.host}:#{config.port}" if config.matrix_mode
|
|
21
|
+
|
|
22
|
+
Fiber.set_scheduler(Async::Scheduler.new)
|
|
23
|
+
|
|
24
|
+
Fiber.schedule do
|
|
25
|
+
loop do
|
|
26
|
+
socket = server.accept
|
|
27
|
+
|
|
28
|
+
Fiber.schedule do
|
|
29
|
+
request = RequestParser.parse(socket:, host: config.host, port: config.port)
|
|
30
|
+
|
|
31
|
+
if config.mirror_mode
|
|
32
|
+
response = ResponseFactory.response(body: "Thank you for visiting #{request.path} with '#{request.body}'")
|
|
33
|
+
else
|
|
34
|
+
response_event = take(Events::RequestEvent.new(request:))
|
|
35
|
+
response = response_event.response
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
ResponseBuilder.respond(config:, socket:, response:)
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
puts e.message
|
|
41
|
+
ensure
|
|
42
|
+
socket&.close
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
LowLoop = Low::Loop
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/stream'
|
|
4
|
+
require 'async/http'
|
|
5
|
+
require 'protocol/http'
|
|
6
|
+
|
|
7
|
+
module Low
|
|
8
|
+
class RequestParser
|
|
9
|
+
include LowType
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def parse(socket: TCPSocket, host: String, port: Integer) -> { ::Protocol::HTTP::Request }
|
|
13
|
+
stream = IO::Stream(socket)
|
|
14
|
+
protocol = Async::HTTP::Protocol::HTTP.default.protocol_for(stream)
|
|
15
|
+
|
|
16
|
+
method, path, = parse_request(stream:)
|
|
17
|
+
headers = parse_headers(stream:)
|
|
18
|
+
body = parse_body(stream:, method:)
|
|
19
|
+
|
|
20
|
+
::Protocol::HTTP::Request.new('http', "#{host}:#{port}", method, path, protocol::VERSION, headers, body)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# HTTP Request format:
|
|
26
|
+
#
|
|
27
|
+
# :verb :path HTTP/1.1\r\n
|
|
28
|
+
# :header_1\r\n
|
|
29
|
+
# :header_2\r\n
|
|
30
|
+
# :header_3\r\n
|
|
31
|
+
# \r\n
|
|
32
|
+
# :body
|
|
33
|
+
#
|
|
34
|
+
def parse_request(stream:) # TODO: Handle type for namespaced "IO:Stream".
|
|
35
|
+
request_line = stream.gets || raise(StandardError, 'EOF')
|
|
36
|
+
|
|
37
|
+
method, full_path, _http_version = request_line.strip.split(' ', 3)
|
|
38
|
+
path, query = full_path.split('?', 2)
|
|
39
|
+
|
|
40
|
+
[method, full_path, path, query]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# TODO: Handle namespaced stream type "IO:Stream".
|
|
44
|
+
def parse_headers(stream:) -> { ::Protocol::HTTP::Headers }
|
|
45
|
+
fields = []
|
|
46
|
+
|
|
47
|
+
while (line = stream.gets.strip)
|
|
48
|
+
break if line.strip.empty?
|
|
49
|
+
|
|
50
|
+
key, value = line.split(/:\s/, 2)
|
|
51
|
+
fields << [key, value]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
::Protocol::HTTP::Headers.new(fields)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parse_body(stream:, method:)
|
|
58
|
+
return nil unless %w[POST PUT].include?(method)
|
|
59
|
+
|
|
60
|
+
stream.read
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Low
|
|
4
|
+
class ResponseBuilder
|
|
5
|
+
class << self
|
|
6
|
+
# TODO: Use Async wherever we can where it doesn't have "Task" requirement.
|
|
7
|
+
def respond(config:, socket:, response:)
|
|
8
|
+
socket.puts "#{response.version} #{response.status}\r\n"
|
|
9
|
+
socket.puts config.port.nil? ? "Host: #{config.host}\r\n" : "Host: #{config.host}:#{config.port}\r\n"
|
|
10
|
+
socket.puts "\r\n"
|
|
11
|
+
socket.puts(response.body.read)
|
|
12
|
+
socket.close
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: low_loop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -9,6 +9,76 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: async
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: async-http
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: io-stream
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: protocol-http
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: low_event
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
12
82
|
- !ruby/object:Gem::Dependency
|
|
13
83
|
name: low_type
|
|
14
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -23,15 +93,18 @@ dependencies:
|
|
|
23
93
|
- - "~>"
|
|
24
94
|
- !ruby/object:Gem::Version
|
|
25
95
|
version: '1.0'
|
|
26
|
-
description: An asynchronous
|
|
27
|
-
|
|
96
|
+
description: An asynchronous server that creates events for your event-driven application
|
|
97
|
+
to use
|
|
28
98
|
email:
|
|
29
99
|
- maediprichard@gmail.com
|
|
30
100
|
executables: []
|
|
31
101
|
extensions: []
|
|
32
102
|
extra_rdoc_files: []
|
|
33
103
|
files:
|
|
104
|
+
- lib/factories/response_factory.rb
|
|
34
105
|
- lib/low_loop.rb
|
|
106
|
+
- lib/request_parser.rb
|
|
107
|
+
- lib/response_builder.rb
|
|
35
108
|
- lib/version.rb
|
|
36
109
|
homepage: https://github.com/low-rb/low_loop
|
|
37
110
|
licenses: []
|