low_loop 0.2.1 → 0.3.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: 7d01329530968986952de86dbb5f82d6e4fc7d2c64111176fca14c403ffd483b
4
- data.tar.gz: fc7475b5dafdf37c2e63d6a95f206c363a7a4476cd161f04c725cf04feb5fa13
3
+ metadata.gz: 61a2d187bf64d4db14f12ab1dbdbe215cb3f608e3d529e17b5af8694c4bf453d
4
+ data.tar.gz: cea153ef1443bd98f22cecb8370e77136e7bfddb0143171bf94cbf18882b9e26
5
5
  SHA512:
6
- metadata.gz: 514b047cf407d3960a37745b166f29c299f4d9976ff717d70c9f44c8f7db5a18fb65072df0c343224b13c81c3c320a7354a44ea05684b5793c9ec35f54039139
7
- data.tar.gz: 02d577dfe82a415b723060e9c2ed4c2517c40d34db71a33e4e71cb1ae55efd4aa9f2d11293e4327fdacc2e7c497e74573b370889bf0f6b0dd79b0ea9db719679
6
+ metadata.gz: 0dae5020c04416c92d23463f1def7f5dafd14bfe324d059e3242e2e1ecc65e583df12064c4981b8c34621dba10fab2162aa04bdb534cc3db81a53d1d9bfe7809
7
+ data.tar.gz: ec738cd515d3fee61f5ee98055c4fdeab4ae510c9ee50cc7591a5922874030c876da9d224b6e4f1060d17b2b098c11a1b0cb64ab73a99393ea815a76c6df7de9
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'low_event'
4
+
5
+ module Low
6
+ module Events
7
+ class FileEvent < LowEvent
8
+ attr_reader :file, :request
9
+
10
+ # TODO: For RouteEvent/FileEvent parse and provide query params as attributes on the event.
11
+ def initialize(file:, request: nil)
12
+ super()
13
+
14
+ @file = file
15
+ @request = request
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,16 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'protocol/http'
4
+ require 'protocol/http/body/file'
4
5
 
5
6
  module Low
6
7
  class ResponseFactory
7
8
  class << self
8
- def response(body:)
9
+ def html(body:)
9
10
  headers = Protocol::HTTP::Headers.new(['content-type', 'text/html'])
10
11
  body = Protocol::HTTP::Body::Buffered.wrap(body)
11
12
 
12
13
  Protocol::HTTP::Response.new('http/1.1', 200, headers, body)
13
14
  end
15
+
16
+ def file(path:, content_type:)
17
+ headers = Protocol::HTTP::Headers.new(['content-type', content_type])
18
+ file = File.open(path, 'rb')
19
+ body = Protocol::HTTP::Body::File.new(file)
20
+
21
+ Protocol::HTTP::Response.new('http/1.1', 200, headers, body)
22
+ end
14
23
  end
15
24
  end
16
25
  end
data/lib/low_loop.rb CHANGED
@@ -7,8 +7,8 @@ require 'low_event'
7
7
  require 'observers'
8
8
 
9
9
  require_relative 'factories/response_factory'
10
- require_relative 'request_parser'
11
- require_relative 'response_builder'
10
+ require_relative 'requests/request_parser'
11
+ require_relative 'responses/response_builder'
12
12
 
13
13
  module Low
14
14
  class Loop
@@ -16,12 +16,14 @@ module Low
16
16
 
17
17
  attr_reader :config
18
18
 
19
- def initialize(config:, router: nil)
19
+ def initialize(config:, dependencies: [])
20
20
  @config = config
21
21
 
22
+ dependencies.each do |dependency|
23
+ observers << dependency
24
+ end
25
+
22
26
  observers.push(self, action: :mirror) if config.mirror_mode
23
- observers << FileServer.new
24
- observers << router if router
25
27
  end
26
28
 
27
29
  def start
@@ -56,9 +58,10 @@ module Low
56
58
  server
57
59
  end
58
60
 
61
+ # Fallback mode for when there's no dependencies and you want to know that the server is still working.
59
62
  def mirror(event:)
60
63
  request = event.request
61
- response = Events::ResponseFactory.response(body: "Thank you for visiting #{request.path} with body: '#{request.body}'")
64
+ response = ResponseFactory.html(body: "Thank you for visiting #{request.path} with body: '#{request.body}'")
62
65
  Events::ResponseEvent.new(response:)
63
66
  end
64
67
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../factories/response_factory'
4
+
5
+ module Low
6
+ class FileResponse
7
+ class << self
8
+ def handle(event:)
9
+ file = event.file
10
+
11
+ if File.exist?(file.path)
12
+ response = ResponseFactory.file(path: file.path, content_type: file.content_type)
13
+ return Low::Events::ResponseEvent.new(response:)
14
+ end
15
+
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ end
@@ -7,8 +7,19 @@ module Low
7
7
  def respond(config:, socket:, response:)
8
8
  socket.puts "#{response.version} #{response.status}\r\n"
9
9
  socket.puts config.port.nil? ? "Host: #{config.host}\r\n" : "Host: #{config.host}:#{config.port}\r\n"
10
+
11
+ response.headers.fields.each_slice(2) do |key, value|
12
+ socket.puts "#{key}: #{value}\r\n"
13
+ end
14
+
10
15
  socket.puts "\r\n"
11
- socket.puts(response.body.read)
16
+
17
+ if response.body.respond_to?(:file)
18
+ IO.copy_stream(response.body.file, socket)
19
+ else
20
+ socket.puts(response.body.read)
21
+ end
22
+
12
23
  socket.close
13
24
  end
14
25
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'protocol/url'
4
+
5
+ require_relative '../events/file_event'
6
+ require_relative '../responses/file_response'
7
+ require_relative '../states/file_state'
8
+
9
+ module Low
10
+ class FileServer
11
+ include LowType
12
+ include Observers
13
+
14
+ def initialize(web_root:, content_types:)
15
+ @web_root = web_root
16
+ @content_types = content_types.transform_keys(&:to_s)
17
+
18
+ observers(File) << FileResponse
19
+ end
20
+
21
+ def extension(filepath:)
22
+ extension = File.extname(filepath).delete_prefix('.')
23
+
24
+ return nil if extension == ''
25
+ return nil unless @content_types.key?(extension)
26
+
27
+ extension
28
+ end
29
+
30
+ # TODO: Define type: Events::RequestEvent
31
+ def handle(event:)
32
+ filepath = Protocol::URL[event.request.path].path
33
+
34
+ extension = extension(filepath:)
35
+ return nil if extension.nil?
36
+
37
+ file = FileState.new(path: safe_path(filepath), content_type: @content_types[extension])
38
+
39
+ trigger File, event: Events::FileEvent.new(file:, request: event.request)
40
+ end
41
+
42
+ private
43
+
44
+ def safe_path(path)
45
+ safe_path = path.split('/').reject { |segment| ['.', '..'].include?(segment) }.join('/')
46
+ File.join(@web_root, safe_path)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ class FileState
5
+ attr_reader :path, :content_type
6
+
7
+ def initialize(path:, content_type:)
8
+ @path = path
9
+ @content_type = content_type
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConfigSupport
4
+ class << self
5
+ def parse_boolean(value)
6
+ return true if value == '1'
7
+ return false if value == '0'
8
+
9
+ nil
10
+ end
11
+ end
12
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Low
4
- LOOP_VERSION = '0.2.1'
4
+ LOOP_VERSION = '0.3.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.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: protocol-url
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'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: low_event
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -101,10 +115,15 @@ executables: []
101
115
  extensions: []
102
116
  extra_rdoc_files: []
103
117
  files:
118
+ - lib/events/file_event.rb
104
119
  - lib/factories/response_factory.rb
105
120
  - lib/low_loop.rb
106
- - lib/request_parser.rb
107
- - lib/response_builder.rb
121
+ - lib/requests/request_parser.rb
122
+ - lib/responses/file_response.rb
123
+ - lib/responses/response_builder.rb
124
+ - lib/servers/file_server.rb
125
+ - lib/states/file_state.rb
126
+ - lib/support/config_support.rb
108
127
  - lib/version.rb
109
128
  homepage: https://github.com/low-rb/low_loop
110
129
  licenses: []