low_loop 0.0.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e67dd274a253c25e965d4b27d602aa17b59101cd41e6d6ae2361e8a6559a06a
4
- data.tar.gz: 7ad5eade4a3bab768dbd1e85c6551c5eb91e73af54bd71b56337b68057a15c02
3
+ metadata.gz: 0cf78d69ff18cc8ec9d07e93acb8c3bffce11e38e83667b8ffe852fa842561e0
4
+ data.tar.gz: 9f96d239ec4b1c80823260b3d3718859528f6189d611247f6f6f7e4e34a23082
5
5
  SHA512:
6
- metadata.gz: 18db48648dfe74640196992d2e05aa0dcbcfd087c6b071f64b2b8168034f6481e06c3d1ccfde7678bfea7714e639cea1e6e28eddda267a12fd6b15830bde2768
7
- data.tar.gz: 945dd862c170d70945991babe3b6c289724caed117ed00a7344a83a7a68697c2b26e94ff30ddd5641a9886ef2ce20c83dbcacc8c101cfce25a2d0aa4c99d5286
6
+ metadata.gz: 6a8a845e855e664055b2676d665bdb40d133f4595d4eacc6d0eea11b028b3c992f0c83a82527aaec0f840e3efcd598138e076ce8ef1f95541e118c2439ad6f33
7
+ data.tar.gz: 4dfc52e4259769ff99efba99195f8561689c8b62a1354bd724dc84ba964ed6a057501d3a1b9d3729c404902918a11648ee08d7e3528c13f22c691a1c4772bc4e
@@ -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,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'async'
4
+ require 'socket'
5
+ require 'low_type'
6
+ require 'low_event' # TODO: Move RequestEvent and ResponseEvent into this gem.
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
+ extend Observers
16
+ observable
17
+
18
+ def start(config:)
19
+ server = TCPServer.new(config.host, config.port)
20
+ server.listen(10)
21
+ puts "Server@#{config.host}:#{config.port}" if config.matrix_mode
22
+
23
+ Fiber.set_scheduler(Async::Scheduler.new)
24
+
25
+ Fiber.schedule do
26
+ loop do
27
+ socket = server.accept
28
+
29
+ Fiber.schedule do
30
+ request = RequestParser.parse(socket:, host: config.host, port: config.port)
31
+
32
+ if config.mirror_mode
33
+ response = Low::ResponseFactory.response(body: "Thank you for visiting #{request.path} with '#{request.body}'")
34
+ else
35
+ response_event = LowLoop.take RequestEvent.new(request:)
36
+ response = response_event.response
37
+ end
38
+
39
+ ResponseBuilder.respond(config:, socket:, response:)
40
+ rescue StandardError => e
41
+ puts e.message
42
+ ensure
43
+ socket&.close
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module LowLoop
4
- VERSION = '0.0.0'
3
+ module Low
4
+ LOOP_VERSION = '0.1.0'
5
5
  end
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.0.0
4
+ version: 0.1.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 event-driven server that send events from the request
27
- layer right through to the application and data layers.
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: []