From ff36326ec827b9a1430ed57ff4684fa46993fc69 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Wed, 23 Aug 2023 04:14:28 +0400 Subject: [PATCH] postprocess/finalize hook, custom error pages --- hyde.rb | 156 +++++++++++++++++++++++++++++++++++------------ test_combined.rb | 6 ++ 2 files changed, 124 insertions(+), 38 deletions(-) diff --git a/hyde.rb b/hyde.rb index 691a569..65587e0 100644 --- a/hyde.rb +++ b/hyde.rb @@ -1,13 +1,53 @@ require 'mime-types' require 'webrick' require 'uri' +require 'pp' module Hyde # Branding and version VERSION = "0.5 (alpha)" attr_reader :VERSION - VLINE = "
\n Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n
" + VLINE = "Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n" attr_reader :VLINE + + def error_template(errortext,backtrace) + < + + + #{WEBrick::HTMLUtils.escape(errortext)} + + + +
+

HYDE

+

Source code

+
+
+

#{WEBrick::HTMLUtils.escape(errortext)}

+

+#{WEBrick::HTMLUtils.escape(backtrace) or "\n\n\n"}
+            
+
+

#{WEBrick::HTMLUtils.escape(VLINE)}

+
+ + +HTMLEOF + end + module_function :error_template + + WEBrick::HTTPResponse.class_exec do + attr_accessor :recent_backtrace + public + def set_backtrace(backtrace) + @recent_backtrace = backtrace + end + def create_error_page + @body = Hyde.error_template(@reason_phrase,@recent_backtrace) + end + end class Server < WEBrick::HTTPServer def initialize(config={},&setup) @@ -15,32 +55,29 @@ module Hyde @hyde_pathspec = Hyde::Pathspec.new "/", &setup self.mount_proc '/' do |req,res| context = Hyde::Context.new(req.path, req, res) - while context and (not context.exit_loop) do - context.exit_loop = true - context = catch :controlled_exit do - @hyde_pathspec._match(context) - context + begin + while context and (not context.exit_loop) do + context.exit_loop = true + context = catch :controlled_exit do + @hyde_pathspec._match(context) + context + end + while postprocessor = context.queue_postprocess.shift do + postprocessor.call(context) + end end + while finalizer = context.queue_finalize.shift do + finalizer.call(context) + end + rescue Exception => e + puts e.message + puts e.backtrace + res.set_backtrace "#{e.message} (#{e.class})\n#{e.backtrace.join "\n"}" + res.status = 500 end end end end - - module ErrorPages - # 404 text - def error404(request, filepath) - request.response.status = 404 - if request.handles.include? 404 - request.response.body = request.handles[404].call( - filepath - ) - else - request.response.body = "\n\n File not found\n \n

File not found

\n #{filepath}\n
\n #{Hyde::VLINE}\n \n" - end - request.response["Content-Type"] = 'text/html' - end - module_function :error404 - end # Interchangeable glob/regex/string pattern matching module PatternMatching @@ -110,6 +147,19 @@ module Hyde new_context.exit_loop = false throw :controlled_exit, new_context end + def die(code, message=nil, backtrace="") + @current_context.response.status = code + @current_context.response.set_backtrace(backtrace) + if not message then + message = WEBrick::HTTPStatus::StatusMessage[code] + end + if @current_context.codehandlers[code] then + @current_context.codehandlers[code].call(@current_context, message, backtrace) + else + @current_context.response.body = Hyde.error_template(message, backtrace) + end + throw :controlled_exit, @current_context + end end # Request wrapper class @@ -119,24 +169,42 @@ module Hyde @filepath = "" @request = request @response = response - @handles = {} @indexlist = [] @vars = {} + @codehandlers = {} + @queue_postprocess = [] + @queue_finalize = [] @exit_loop = false end def self.rewrite(pctx,newpath) - newctx = self.new(newpath,pctx.request,pctx.response) + newctx = Context.new(newpath,pctx.request,pctx.response) newctx.vars = pctx.vars + newctx.queue_finalize = pctx.queue_finalize.clone + newctx.queue_postprocess = pctx.queue_postprocess.clone return newctx end + def enqueue_finalizer(dup: false, &block) + if block_given? then + if dup or not @queue_finalize.include? block then + @queue_finalize.append(block) + end + end + end + def enqueue_postprocessor(&block) + if block_given? then + @queue_postprocess.append(block) + end + end attr_reader :request attr_reader :response - attr_accessor :filepath + attr_accessor :queue_finalize + attr_accessor :queue_postprocess attr_accessor :path - attr_accessor :handles attr_accessor :indexlist + attr_accessor :filepath attr_accessor :exit_loop attr_accessor :vars + attr_accessor :codehandlers end # Context object with safe path encapsulation @@ -146,15 +214,18 @@ module Hyde @filepath = request.filepath @request = request.request @response = request.response - @handles = request.handles @indexlist = request.indexlist @exit_loop = request.exit_loop @vars = request.vars + @codehandlers = request.codehandlers + @queue_postprocess = request.queue_postprocess + @queue_finalize = request.queue_finalize end undef :path= undef :filepath= - undef :handles= undef :indexlist= + undef :queue_postprocess= + undef :queue_finalize= end # Handler classes @@ -185,6 +256,7 @@ module Hyde def _match(request) return super if @block if _match? request.path, request then + @current_context = request match_path = _normalize_input(request.path).match(@path)[0] filepath = request.filepath+match_path begin @@ -194,7 +266,7 @@ module Hyde request.response.body = data request.response["Content-Type"] = mimetype rescue Errno::ENOENT - Hyde::ErrorPages::error404 request, request.request.path + die(404) end end end @@ -282,10 +354,10 @@ module Hyde class Pathspec include Hyde::PatternMatching include Hyde::Handlers + include Hyde::PublicContextControlMethods def initialize (path, root_path: nil, safe_regexp: true, &block) _prep_path path, safe_regexp: safe_regexp @chain = [] - @handles = {} @root_override = root_path @remap = false self.instance_exec &block @@ -308,15 +380,23 @@ module Hyde end def index(list) @indexlist = list if list.kind_of? Array - end - def handle(code, &block) - @handles[code] = block + @indexlist = [list] if list.kind_of? String end def preprocess(&block) @preprocessor = block end - def _match(request) + def postprocess(&block) + @postprocessor = block + end + def finalize(dup: false, &block) + @finalizer = block + @finalizer_dup = dup + end + def _match(request) + @current_context = request self.instance_exec request, &@preprocessor if @preprocessor + request.enqueue_postprocessor &@postprocessor if @preprocessor + request.enqueue_finalizer dup: @finalizer_dup, &@finalizer if @finalizer if _match? request.path, request then match_path = _normalize_input(request.path).match(@path) next_path = match_path[0] @@ -329,8 +409,7 @@ module Hyde else request.filepath = request.filepath+next_path+"/" end - # parameter overrides overlaying - @handles.each_pair { |k,v| request.handles[k] = v } + # redefine indexing parameters if they are defined for a pathspec request.indexlist = @indexlist if @indexlist # do directory indexing if cut_path.match /^\/?$/ then @@ -346,10 +425,11 @@ module Hyde next_pathspec = @chain.find { |x| x._match? cut_path, request } next_pathspec._match request if next_pathspec unless next_pathspec then - # throw 404 if nowhere to go - Hyde::ErrorPages::error404 request, request.request.path + # die and throw up if nowhere to go + die(404) end end + @current_context = nil end end end diff --git a/test_combined.rb b/test_combined.rb index 79aeed0..68418c3 100644 --- a/test_combined.rb +++ b/test_combined.rb @@ -8,6 +8,12 @@ server = Hyde::Server.new Port: 8000 do preprocess do |ctx| puts "#{ctx} entered fully virtual directory!" end + postprocess do |ctx| + puts "#{ctx} reached endpoint!" + end + finalize do |ctx| + puts "#{ctx} finished processing!" + end get "portal" do |ctx| ctx.vars[:ass] = true rewrite "/about/hyde"