postprocess/finalize hook, custom error pages
This commit is contained in:
parent
f1f13faddf
commit
ff36326ec8
144
hyde.rb
144
hyde.rb
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue