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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e67dd274a253c25e965d4b27d602aa17b59101cd41e6d6ae2361e8a6559a06a
4
- data.tar.gz: 7ad5eade4a3bab768dbd1e85c6551c5eb91e73af54bd71b56337b68057a15c02
3
+ metadata.gz: 1ca13498531cc4abf7f577bee5f78387d0f42fcbb414e5b20be39db848a154e5
4
+ data.tar.gz: '075782581f7fd32f7b7d077bbcf03101561560823f39a6f8ecd38ac312c3a363'
5
5
  SHA512:
6
- metadata.gz: 18db48648dfe74640196992d2e05aa0dcbcfd087c6b071f64b2b8168034f6481e06c3d1ccfde7678bfea7714e639cea1e6e28eddda267a12fd6b15830bde2768
7
- data.tar.gz: 945dd862c170d70945991babe3b6c289724caed117ed00a7344a83a7a68697c2b26e94ff30ddd5641a9886ef2ce20c83dbcacc8c101cfce25a2d0aa4c99d5286
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
@@ -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.2.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.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 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: []