gloo 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+