gloo 3.0.1 → 3.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.
@@ -0,0 +1,120 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # Web application request handler.
5
+ # Takes a request and does what is needed to create a response.
6
+ #
7
+
8
+ module Gloo
9
+ module WebSvr
10
+ class Handler
11
+
12
+ attr_reader :server_obj
13
+
14
+
15
+ # ---------------------------------------------------------------------
16
+ # Initialization
17
+ # ---------------------------------------------------------------------
18
+
19
+ #
20
+ # Set up the web server.
21
+ #
22
+ def initialize( engine, obj )
23
+ @engine = engine
24
+ @log = @engine.log
25
+ @server_obj = obj
26
+ end
27
+
28
+
29
+ # ---------------------------------------------------------------------
30
+ # Process Request
31
+ # ---------------------------------------------------------------------
32
+
33
+ #
34
+ # Process the request and return a result.
35
+ #
36
+ def handle request
37
+ @request = request
38
+
39
+ page = @server_obj.router.page_for_route @request.path
40
+ if page
41
+ if page.is_a? Gloo::Objs::FileHandle
42
+ return handle_file page
43
+ else
44
+ return handle_page page
45
+ end
46
+ end
47
+
48
+ return server_error_result
49
+ end
50
+
51
+ #
52
+ # Handle request for a page.
53
+ # Render the page, with possible redirect.
54
+ #
55
+ def handle_page page
56
+ result = page.render
57
+ if redirect_set?
58
+ page = @engine.running_app.obj.redirect
59
+ @log.debug "Redirecting to: #{page.pn}"
60
+ @engine.running_app.obj.redirect = nil
61
+ result = page.render
62
+ end
63
+ return result
64
+ end
65
+
66
+ #
67
+ # Handle a request for a static file such as an image.
68
+ #
69
+ def handle_file file
70
+ pn = @server_obj.asset.path_for_file file
71
+
72
+ # Check to make sure it is a valid file
73
+ # return error if it is not
74
+ return file_error_result unless File.exist? pn
75
+
76
+ return @server_obj.asset.render_file pn
77
+ end
78
+
79
+
80
+ # ---------------------------------------------------------------------
81
+ # Errors
82
+ # ---------------------------------------------------------------------
83
+
84
+ #
85
+ # Return a server error result.
86
+ # Use the app's error if there is one, otherwise a generic message.
87
+ #
88
+ def server_error_result
89
+ err_page = @server_obj.err_page
90
+ return err_page.render if err_page
91
+
92
+ # Last resort, just return a generic error message.
93
+ return Gloo::WebSvr::Response.text_response( @engine,
94
+ "Server error!", Gloo::WebSvr::ResponseCode::SERVER_ERR )
95
+ end
96
+
97
+ #
98
+ # Get a file not found error result.
99
+ #
100
+ def file_error_result
101
+ return Gloo::WebSvr::Response.text_response( @engine,
102
+ "File not found!", Gloo::WebSvr::ResponseCode::NOT_FOUND )
103
+ end
104
+
105
+
106
+ # ---------------------------------------------------------------------
107
+ # Helper functions
108
+ # ---------------------------------------------------------------------
109
+
110
+ #
111
+ # Is there a redirect page set in the running app?
112
+ #
113
+ def redirect_set?
114
+ return false unless @engine.app_running?
115
+ return @engine.running_app.obj.redirect
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,107 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # A web Request for a page, action, or static resource.
5
+ #
6
+ # Kinds of Resources
7
+ # Web Page
8
+ # Action - does something and redirects to a page (or returns nothing)
9
+ # API - returns JSON instead of HTML (but is that different from Web Page?)
10
+ # Static Resource - File, PDF, Image, etc.
11
+ #
12
+
13
+ module Gloo
14
+ module WebSvr
15
+ class Request
16
+
17
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
18
+ REQUEST_PATH = 'REQUEST_PATH'.freeze
19
+ HTTP_HOST = 'HTTP_HOST'.freeze
20
+ QUERY_STRING = 'QUERY_STRING'.freeze
21
+
22
+ attr_reader :method, :host, :path, :query
23
+
24
+
25
+ # ---------------------------------------------------------------------
26
+ # Initialization
27
+ # ---------------------------------------------------------------------
28
+
29
+ #
30
+ # Set up the web server.
31
+ #
32
+ def initialize( engine, handler, env = nil )
33
+ @engine = engine
34
+ @log = @engine.log
35
+
36
+ @handler = handler
37
+
38
+ @env = env
39
+ detect_env
40
+ end
41
+
42
+
43
+ # ---------------------------------------------------------------------
44
+ # Process Request
45
+ # ---------------------------------------------------------------------
46
+
47
+ #
48
+ # Process the request and return a result.
49
+ #
50
+ def process
51
+ start_timer
52
+ result = @handler.handle self
53
+ finish_timer
54
+ return result
55
+ end
56
+
57
+ # ---------------------------------------------------------------------
58
+ # ENV
59
+ # ---------------------------------------------------------------------
60
+
61
+ #
62
+ # Write the request information to the log.
63
+ #
64
+ def detect_env
65
+ @method = @env[ REQUEST_METHOD ]
66
+ @path = @env[ REQUEST_PATH ]
67
+ @host = @env[ HTTP_HOST ]
68
+ @query = @env[ QUERY_STRING ]
69
+ end
70
+
71
+
72
+ # ---------------------------------------------------------------------
73
+ # Request timer
74
+ # ---------------------------------------------------------------------
75
+
76
+ #
77
+ # Keep track of the request start time.
78
+ #
79
+ def start_timer
80
+ @start = Time.now
81
+ end
82
+
83
+ #
84
+ # Write the request completion time to the log.
85
+ #
86
+ def finish_timer
87
+ @finish = Time.now
88
+ @elapsed = ( ( @finish - @start ) * 1000.0 ).round(2)
89
+ @log.info "Web request complete. Elapsed time: #{@elapsed} ms"
90
+ end
91
+
92
+
93
+ # ---------------------------------------------------------------------
94
+ # Helper functions
95
+ # ---------------------------------------------------------------------
96
+
97
+ #
98
+ # Write the request information to the log.
99
+ #
100
+ def log
101
+ @log.info "#{@method} #{@host}#{@path}"
102
+ @log.info "Parameters: #{@query}"
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,118 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # The Response for a web Request.
5
+ #
6
+
7
+ module Gloo
8
+ module WebSvr
9
+ class Response
10
+
11
+ #
12
+ # SEE: https://stackoverflow.com/questions/23714383/what-are-all-the-possible-values-for-http-content-type-header#48704300
13
+ # for a list of content types.
14
+ #
15
+ CONTENT_TYPE = 'Content-Type'.freeze
16
+ TEXT_TYPE = 'text/plain'.freeze
17
+ JSON_TYPE = 'application/json'.freeze
18
+ HTML_TYPE = 'text/html'.freeze
19
+
20
+ attr_reader :code, :type, :data
21
+
22
+
23
+ # ---------------------------------------------------------------------
24
+ # Initialization
25
+ # ---------------------------------------------------------------------
26
+
27
+ #
28
+ # Set up the web server.
29
+ #
30
+ def initialize( engine = nil,
31
+ code = Gloo::WebSvr::ResponseCode::SUCCESS,
32
+ type = HTML_TYPE, data = nil )
33
+
34
+ @engine = engine
35
+ @log = @engine.log if @engine
36
+
37
+ @code = code
38
+ @type = type
39
+ @data = data
40
+ end
41
+
42
+
43
+ # ---------------------------------------------------------------------
44
+ # Static Helper Functions
45
+ # ---------------------------------------------------------------------
46
+
47
+ #
48
+ # Helper to create a successful JSON response with the given data.
49
+ #
50
+ def self.json_response( engine, data,
51
+ code = Gloo::WebSvr::ResponseCode::SUCCESS )
52
+
53
+ return Gloo::WebSvr::Response.new( engine, code, JSON_TYPE, data )
54
+ end
55
+
56
+ #
57
+ # Helper to create a successful text response with the given data.
58
+ #
59
+ def self.text_response( engine, data,
60
+ code = Gloo::WebSvr::ResponseCode::SUCCESS )
61
+
62
+ return Gloo::WebSvr::Response.new( engine, code, TEXT_TYPE, data )
63
+ end
64
+
65
+ #
66
+ # Helper to create a successful web response with the given data.
67
+ #
68
+ def self.html_response( engine, data,
69
+ code = Gloo::WebSvr::ResponseCode::SUCCESS )
70
+
71
+ return Gloo::WebSvr::Response.new( engine, code, HTML_TYPE, data )
72
+ end
73
+
74
+
75
+ # ---------------------------------------------------------------------
76
+ # Data Functions
77
+ # ---------------------------------------------------------------------
78
+
79
+ #
80
+ # Add content to the payload.
81
+ #
82
+ def add content
83
+ @data = '' if @data.nil?
84
+ @data << content
85
+ end
86
+
87
+ #
88
+ # Get the headers for the response.
89
+ #
90
+ def headers
91
+ return { CONTENT_TYPE => @type }
92
+ end
93
+
94
+ #
95
+ # Get the final result that will be returned as the
96
+ # response to the web request.
97
+ #
98
+ def result
99
+ return [ @code, headers, @data ]
100
+ end
101
+
102
+
103
+ # ---------------------------------------------------------------------
104
+ # Helper functions
105
+ # ---------------------------------------------------------------------
106
+
107
+ #
108
+ # Write the result information to the log.
109
+ #
110
+ def log
111
+ return unless @log
112
+
113
+ @log.info "Response #{@code} #{@type}"
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,69 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # Standard Response Codes.
5
+ #
6
+ # See:
7
+ # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
8
+ # https://www.geeksforgeeks.org/10-most-common-http-status-codes/
9
+ #
10
+
11
+ module Gloo
12
+ module WebSvr
13
+ class ResponseCode
14
+
15
+ # Gloo::WebSvr::ResponseCode::SUCCESS
16
+ SUCCESS = 200.freeze
17
+ CODE_200 = 'Success/OK'.freeze
18
+
19
+ CREATED = 201.freeze
20
+ CODE_201 = 'Created'.freeze
21
+
22
+ ACCEPTED = 202.freeze
23
+ CODE_202 = 'Accepted'.freeze
24
+
25
+ NO_CONTENT = 204.freeze
26
+ CODE_204 = 'No Content'.freeze
27
+
28
+ PARTIAL_CONTENT = 206.freeze
29
+ CODE_206 = 'Partial Content'.freeze
30
+
31
+ MOVED_PERM = 301.freeze
32
+ CODE_301 = 'Moved Permanently'.freeze
33
+
34
+ FOUND = 302.freeze
35
+ CODE_302 = 'Found'.freeze
36
+
37
+ SEE_OTHER = 303.freeze
38
+ CODE_303 = 'See Other'.freeze
39
+
40
+ NOT_MODIFIED = 304.freeze
41
+ CODE_304 = 'Not Modified'.freeze
42
+
43
+ TEMP_REDIRECT = 307.freeze
44
+ CODE_307 = 'Temporary Redirect'.freeze
45
+
46
+ PERM_REDIRECT = 307.freeze
47
+ CODE_308 = 'Permanent Redirect'.freeze
48
+
49
+ BAD_REQUEST = 400.freeze
50
+ CODE_400 = 'Bad Request'.freeze
51
+
52
+ UNAUTHORIZED = 401.freeze
53
+ CODE_401 = 'Unauthorized'.freeze
54
+
55
+ FORBIDDEN = 403.freeze
56
+ CODE_403 = 'Forbidden'.freeze
57
+
58
+ NOT_FOUND = 404.freeze
59
+ CODE_404 = 'Not Found'.freeze
60
+
61
+ SERVER_ERR = 500.freeze
62
+ CODE_500 = 'Internal Server Error'.freeze
63
+
64
+ NOT_IMPLEMENTED = 501.freeze
65
+ CODE_501 = 'Not Implemented'.freeze
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,179 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # A helper class for page routing.
5
+ #
6
+
7
+ module Gloo
8
+ module WebSvr
9
+ class Router
10
+
11
+ PAGE_CONTAINER = 'page'.freeze
12
+ INDEX = 'index'.freeze
13
+ SEGMENT_DIVIDER = '/'.freeze
14
+
15
+ attr_reader :route_segments
16
+
17
+
18
+ # ---------------------------------------------------------------------
19
+ # Initialization
20
+ # ---------------------------------------------------------------------
21
+
22
+ #
23
+ # Set up the web server.
24
+ #
25
+ def initialize( engine, web_svr_obj )
26
+ @engine = engine
27
+ @log = @engine.log
28
+
29
+ @web_svr_obj = web_svr_obj
30
+ end
31
+
32
+
33
+ # ---------------------------------------------------------------------
34
+ # Routing
35
+ # ---------------------------------------------------------------------
36
+
37
+ #
38
+ # Find and return the page for the given route.
39
+ #
40
+ def page_for_route path
41
+ @engine.log.info "routing to #{path}"
42
+ detect_segments path
43
+
44
+ return @web_svr_obj.home_page if is_root_path?
45
+
46
+ pages = @web_svr_obj.pages_container
47
+ return find_route_segment( pages.children ) if pages
48
+
49
+ return nil
50
+ end
51
+
52
+
53
+ # ---------------------------------------------------------------------
54
+ # Dynamic Add Page Routes
55
+ # ---------------------------------------------------------------------
56
+
57
+ #
58
+ # Get the root level page container.
59
+ #
60
+ def page_container
61
+ pn = Gloo::Core::Pn.new( @engine, PAGE_CONTAINER )
62
+ return pn.resolve
63
+ end
64
+
65
+ #
66
+ # Add all page routes to the web server pages (routes).
67
+ #
68
+ def add_page_routes
69
+ can = page_container
70
+ return unless can
71
+
72
+ @log.debug 'Adding page routes to web server…'
73
+ @factory = @engine.factory
74
+
75
+ add_pages can, @web_svr_obj.pages_container
76
+ end
77
+
78
+ #
79
+ # Add the pages to the web server pages.
80
+ # This is a recursive function that will add all
81
+ # pages in the folder and subfolders.
82
+ #
83
+ def add_pages can, parent
84
+ # for each file in the page container
85
+ # create a page object and add it to the routes
86
+ can.children.each do |obj|
87
+ if obj.class == Gloo::Objs::Container
88
+ child_can = parent.find_add_child( obj.name, 'container' )
89
+ add_pages( obj, child_can )
90
+ elsif obj.class == Gloo::Objs::Page
91
+ add_route_alias( parent, obj.name, obj.pn )
92
+ end
93
+ end
94
+ end
95
+
96
+ #
97
+ # Add route alias to the page.
98
+ #
99
+ def add_route_alias( parent, name, pn )
100
+ name = name.gsub( '.', '_' )
101
+
102
+ # First make sure the child doesn't already exist.
103
+ child = parent.find_child( name )
104
+ return if child
105
+
106
+ @factory.create_alias( name, pn, parent )
107
+ end
108
+
109
+
110
+ # ---------------------------------------------------------------------
111
+ # Helper funcions
112
+ # ---------------------------------------------------------------------
113
+
114
+ #
115
+ # Find the route segment in the object container.
116
+ #
117
+ def find_route_segment objs
118
+ this_segment = next_segment
119
+
120
+ this_segment = INDEX if this_segment.blank?
121
+
122
+ objs.each do |o|
123
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
124
+
125
+ if o.name == this_segment
126
+ if o.class == Gloo::Objs::Page
127
+ @engine.log.debug "found page for route: #{o.pn}"
128
+ return o
129
+ elsif o.class == Gloo::Objs::FileHandle
130
+ @engine.log.debug "found static file for route: #{o.pn}"
131
+ return o
132
+ else
133
+ return nil unless o.child_count > 0
134
+
135
+ return find_route_segment( o.children )
136
+ end
137
+ end
138
+ end
139
+
140
+ return nil
141
+ end
142
+
143
+ #
144
+ # Get the next segment in the route.
145
+ #
146
+ def next_segment
147
+ this_segment = @route_segments.shift
148
+ return nil if this_segment.nil?
149
+
150
+ # A URL might include a dot in a name, but we can't do that
151
+ # because dot is a reserve path thing. So we replace it with
152
+ # an underscore.
153
+ this_segment = this_segment.gsub( '.', '_' )
154
+
155
+ return this_segment
156
+ end
157
+
158
+ #
159
+ # Is this the root path?
160
+ def is_root_path?
161
+ return @route_segments.count == 0
162
+ end
163
+
164
+ #
165
+ # Create a list of path segments.
166
+ #
167
+ def detect_segments path
168
+ # Split the path into segments.
169
+ @route_segments = path.split SEGMENT_DIVIDER
170
+
171
+ # Remove the first segment if it is empty.
172
+ @route_segments.shift if @route_segments.first.blank?
173
+
174
+ return @route_segments
175
+ end
176
+
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,97 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 20124 Eric Crane. All rights reserved.
3
+ #
4
+ # Starting work on web server inside gloo.
5
+ #
6
+ # UNDER CONSTRUCTION!
7
+ #
8
+ # Simple tests:
9
+ # > curl http://localhost:8087/test/
10
+ # > curl http://localhost:8087/web/
11
+ # > curl http://localhost:8087/test/1
12
+ # > curl http://localhost:8087/test?param=123
13
+ #
14
+ # Run in loop:
15
+ # for i in {1..99}; do curl http://localhost:8087/; done
16
+ #
17
+ # Links:
18
+ # https://github.com/rack/rack
19
+ # https://github.com/rack/rack/blob/main/lib/rack/builder.rb
20
+ # https://thoughtbot.com/blog/ruby-rack-tutorial
21
+ # https://www.rubydoc.info/gems/rack/1.5.5/Rack/Runtime
22
+ #
23
+
24
+ require 'rack'
25
+
26
+ module Gloo
27
+ module WebSvr
28
+ class Server
29
+
30
+ # ---------------------------------------------------------------------
31
+ # Initialization
32
+ # ---------------------------------------------------------------------
33
+
34
+ #
35
+ # Set up the web server.
36
+ #
37
+ def initialize( engine, handler, config = nil )
38
+ @config = config ? config : Gloo::WebSvr::Config.new
39
+ @engine = engine
40
+ @log = @engine.log
41
+ @handler = handler
42
+
43
+ @log.debug 'Gloo web server intialized…'
44
+ end
45
+
46
+
47
+ # ---------------------------------------------------------------------
48
+ # Start and Stop the server.
49
+ # ---------------------------------------------------------------------
50
+
51
+ #
52
+ # Start the web server.
53
+ #
54
+ def start
55
+ opts = {
56
+ :Port => @config.port,
57
+ :Host => @config.host
58
+ }
59
+ Thread.abort_on_exception = true
60
+ @server_thread = Thread.new { Rack::Handler::Thin.run( self, **options=opts ) }
61
+ @log.debug 'Web server has started.'
62
+ end
63
+
64
+ #
65
+ # Stop the web server
66
+ #
67
+ def stop
68
+ @log.debug 'Stopping the web server…'
69
+
70
+ @server_thread.kill
71
+
72
+ @log.debug 'The web server has been stopped.'
73
+ end
74
+
75
+
76
+ # ---------------------------------------------------------------------
77
+ # Handle events
78
+ # ---------------------------------------------------------------------
79
+
80
+ #
81
+ # Handle a request for a resource.
82
+ #
83
+ def call( env )
84
+ request = Gloo::WebSvr::Request.new( @engine, @handler, env )
85
+ request.log
86
+
87
+ response = request.process
88
+ response.log if response
89
+
90
+ return response ? response.result : nil
91
+ end
92
+
93
+
94
+ end
95
+ end
96
+ end
97
+