postprocess/finalize hook, custom error pages

This commit is contained in:
Yessiest 2023-08-23 04:14:28 +04:00
parent f1f13faddf
commit ff36326ec8
2 changed files with 124 additions and 38 deletions

144
hyde.rb
View File

@ -1,47 +1,84 @@
require 'mime-types'
require 'webrick'
require 'uri'
require 'pp'
module Hyde
# Branding and version
VERSION = "0.5 (alpha)"
attr_reader :VERSION
VLINE = "<ADDRESS>\n Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n </ADDRESS>"
VLINE = "Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n"
attr_reader :VLINE
def error_template(errortext,backtrace)
<<HTMLEOF
<!DOCTYPE HTML>
<html>
<head>
<title>#{WEBrick::HTMLUtils.escape(errortext)}</title>
<style> .header {background-color: #CC7878; padding: 0.5rem; border-bottom-width: 0.2rem; border-bottom-style: solid; border-bottom-color: #202222; overflow: auto;} .title { font-weight: bolder; font-size: 36px; margin: 0 0; text-shadow: 1px 1px 1px #202222, 2px 2px 2px #404444; float: left } body { margin: 0;
} .text { font-size 1rem; } .small { color: #7D7D7D; font-size: 12px;} .code { font-family: monospace; font-size: 0.7rem; } </style>
</head>
<body>
<div class="header">
<p class="title">HYDE</p>
<p style="float: right"><a href="https://adastra7.net/git/yessiest/hyde">Source code</a></p>
</div>
<div style="padding: 0.5rem">
<p class="text">#{WEBrick::HTMLUtils.escape(errortext)}</p>
<pre><code class="text code">
#{WEBrick::HTMLUtils.escape(backtrace) or "\n\n\n"}
</code></pre>
<hr/>
<p class="small">#{WEBrick::HTMLUtils.escape(VLINE)}</p>
</div>
</body>
</html>
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)
super(config)
@hyde_pathspec = Hyde::Pathspec.new "/", &setup
self.mount_proc '/' do |req,res|
context = Hyde::Context.new(req.path, req, res)
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 = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<HTML>\n <HEAD><TITLE>File not found</TITLE></HEAD>\n <BODY>\n <H1>File not found</H1>\n #{filepath}\n <HR>\n #{Hyde::VLINE}\n </BODY>\n</HTML>"
end
request.response["Content-Type"] = 'text/html'
end
module_function :error404
end
# Interchangeable glob/regex/string pattern matching
module PatternMatching
def _prep_path(path,safe_regexp: true)
@ -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 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

View File

@ -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"