added sinatra-like method matching, redirect and rewrite functions

This commit is contained in:
Yessiest 2023-04-30 03:19:40 +04:00
parent b20244e2e5
commit 77a5e9a6b5
2 changed files with 104 additions and 65 deletions

138
hyde.rb
View File

@ -1,9 +1,10 @@
require 'mime-types' require 'mime-types'
require 'webrick' require 'webrick'
require 'uri'
module Hyde module Hyde
# Branding and version # Branding and version
VERSION = "0.1" VERSION = "0.5 (alpha)"
attr_reader :VERSION attr_reader :VERSION
VLINE = "<ADDRESS>\n Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n </ADDRESS>" VLINE = "<ADDRESS>\n Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n </ADDRESS>"
attr_reader :VLINE attr_reader :VLINE
@ -13,7 +14,14 @@ module Hyde
super(config) super(config)
@hyde_pathspec = Hyde::Pathspec.new "/", &setup @hyde_pathspec = Hyde::Pathspec.new "/", &setup
self.mount_proc '/' do |req,res| self.mount_proc '/' do |req,res|
@hyde_pathspec.match(Hyde::Context.new(req.path, 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
end
end
end end
end end
end end
@ -36,14 +44,12 @@ module Hyde
# Interchangeable glob/regex/string pattern matching # Interchangeable glob/regex/string pattern matching
module PatternMatching module PatternMatching
def prep_path(path,match_full_path: false) def _prep_path(path,safe_regexp: true)
raise Exception, "Not permitted" if @lock_methods @safe_regexp = safe_regexp
@match_full_path = match_full_path @path = _normalize(path) if path.kind_of? String
@path = normalize(path) if path.kind_of? String
@path = path if path.kind_of? Regexp @path = path if path.kind_of? Regexp
end end
def match?(path) def _match?(path)
raise Exception, "Not permitted" if @lock_methods
# behvaiour used by "index" method # behvaiour used by "index" method
return true if @path == "" return true if @path == ""
split_path = path.split("/").filter { |x| x != "" } split_path = path.split("/").filter { |x| x != "" }
@ -51,10 +57,10 @@ module Hyde
# this chunk of fuck is needed to force regexp into 3 rules: # this chunk of fuck is needed to force regexp into 3 rules:
# 1) unsafe regexp means match the whole (remaining) line. # 1) unsafe regexp means match the whole (remaining) line.
# 3) safe regexp means match only the part before the next slash # 3) safe regexp means match only the part before the next slash
# 2) a .match? only returns when there are no leftovers # 2) a ._match? only returns when there are no leftovers
# this forces the matching to work somewhat consistently # this forces the matching to work somewhat consistently
test = @path.match normalize_input(path) if @match_full_path test = @path.match _normalize_input(path) unless @safe_regexp
test = @path.match split_path[0] unless @match_full_path test = @path.match split_path[0] if @safe_regexp
if test and (test.pre_match == "") and (test.post_match == "") then if test and (test.pre_match == "") and (test.post_match == "") then
return true return true
else else
@ -69,15 +75,13 @@ module Hyde
return true return true
end end
end end
def normalize_input(path) def _normalize_input(path)
raise Exception, "Not permitted" if @lock_methods
# remove duplicate slashes and trim edge slashes # remove duplicate slashes and trim edge slashes
(path.split "/").filter { |x| x != "" }.join("/") (path.split "/").filter { |x| x != "" }.join("/")
end end
def normalize(path) def _normalize(path)
raise Exception, "Not permitted" if @lock_methods # remove duplicate slashe s and trim edge slashes
# remove duplicate slashes and trim edge slashes path = _normalize_input(path)
path = normalize_input(path)
# globbing behaviour simulated with regexp # globbing behaviour simulated with regexp
if path.match /(?<!\\)[\*\?\[]/ then if path.match /(?<!\\)[\*\?\[]/ then
path = Regexp.new(path path = Regexp.new(path
@ -95,8 +99,16 @@ module Hyde
# Methods to control requests, accessible from within blocks # Methods to control requests, accessible from within blocks
module PublicContextControlMethods module PublicContextControlMethods
def redirect(url) def redirect(url)
puts "Unimplemented method 'redirect' called" res = @current_context.response
return res.status = 301
res.body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
res.header['location'] = URI(url).to_s
throw :controlled_exit, @current_context
end
def rewrite(url)
new_context = Context::rewrite(@current_context,url)
new_context.exit_loop = false
throw :controlled_exit, new_context
end end
end end
@ -110,6 +122,12 @@ module Hyde
@handles = {} @handles = {}
@indexlist = [] @indexlist = []
@vars = {} @vars = {}
@exit_loop = false
end
def self.rewrite(pctx,newpath)
newctx = self.new(newpath,pctx.request,pctx.response)
newctx.vars = pctx.vars
return newctx
end end
attr_reader :request attr_reader :request
attr_reader :response attr_reader :response
@ -117,6 +135,7 @@ module Hyde
attr_accessor :path attr_accessor :path
attr_accessor :handles attr_accessor :handles
attr_accessor :indexlist attr_accessor :indexlist
attr_accessor :exit_loop
attr_accessor :vars attr_accessor :vars
end end
@ -129,6 +148,7 @@ module Hyde
@response = request.response @response = request.response
@handles = request.handles @handles = request.handles
@indexlist = request.indexlist @indexlist = request.indexlist
@exit_loop = request.exit_loop
@vars = request.vars @vars = request.vars
end end
undef :path= undef :path=
@ -141,26 +161,24 @@ module Hyde
class Probe class Probe
include Hyde::PatternMatching include Hyde::PatternMatching
include Hyde::PublicContextControlMethods include Hyde::PublicContextControlMethods
def initialize (path, match_full_path: false, &block_optional) def initialize (path, safe_regexp: true, &block_optional)
prep_path path, match_full_path: match_full_path _prep_path path, safe_regexp: safe_regexp
@block = block_optional @block = block_optional
end end
def match(request) def _match(request)
if @block and (match? request.path) then if @block and (_match? request.path) then
@current_request = Hyde::ProtectedContext.new(request) @current_context = Hyde::ProtectedContext.new(request)
@lock_methods = true return_later = self.instance_exec @current_context, &@block
return_later = self.instance_exec @current_request, &@block
@lock_methods = false
return return_later return return_later
end end
end end
end end
class Serve < Hyde::Probe class Serve < Hyde::Probe
def match(request) def _match(request)
return super if @block return super if @block
if match? request.path then if _match? request.path then
match_path = normalize_input(request.path).match(@path)[0] match_path = _normalize_input(request.path).match(@path)[0]
filepath = request.filepath+match_path filepath = request.filepath+match_path
begin begin
mimetype = MIME::Types.type_for(filepath) mimetype = MIME::Types.type_for(filepath)
@ -175,9 +193,25 @@ module Hyde
end end
end end
["get","post","put","patch","delete","options","link","unlink"].each { |m|
new_class = Class.new(Hyde::Probe) do
def initialize(*a, **b, &block)
raise Exception, "block required!" if not block
super(*a, **b, &block)
end
def _match(ctx)
if ctx.request.request_method == m.upcase then
super(ctx)
end
end
end
const_set(m.capitalize+"Match", new_class)
}
class PrintProbe < Hyde::Probe class PrintProbe < Hyde::Probe
def match(request) def _match(request)
if match? request.path then if _match? request.path then
puts "#{request.path} matched!" puts "#{request.path} matched!"
end end
end end
@ -188,7 +222,15 @@ module Hyde
{ {
probe: Hyde::Probe, probe: Hyde::Probe,
printProbe: Hyde::PrintProbe, printProbe: Hyde::PrintProbe,
serve: Hyde::Serve serve: Hyde::Serve,
post: Hyde::PostMatch,
get: Hyde::GetMatch,
put: Hyde::PutMatch,
patch: Hyde::PatchMatch,
delete: Hyde::DeleteMatch,
options: Hyde::OptionsMatch,
link: Hyde::LinkMatch,
unlink: Hyde::UnlinkMatch
}.each_pair { |name, newclass| }.each_pair { |name, newclass|
define_method name do |path, *a, **b, &block| define_method name do |path, *a, **b, &block|
if path.kind_of? Array then if path.kind_of? Array then
@ -205,15 +247,13 @@ module Hyde
class Pathspec class Pathspec
include Hyde::PatternMatching include Hyde::PatternMatching
include Hyde::Handlers include Hyde::Handlers
def initialize (path, root_path: nil, match_full_path: false, &block) def initialize (path, root_path: nil, safe_regexp: true, &block)
prep_path path, match_full_path: match_full_path _prep_path path, safe_regexp: safe_regexp
@chain = [] @chain = []
@handles = {} @handles = {}
@root_override = root_path @root_override = root_path
@remap = false @remap = false
@lock_methods = true
self.instance_exec &block self.instance_exec &block
@lock_methods = false
end end
def path(path, *a, **b, &block) def path(path, *a, **b, &block)
if path.kind_of? Array then if path.kind_of? Array then
@ -225,14 +265,10 @@ module Hyde
end end
end end
def root(path) def root(path)
@lock_methods = false @root_override = "/"+_normalize_input(path)
@root_override = "/"+normalize_input(path)
@lock_methods = true
end end
def remap(path) def remap(path)
@lock_methods = false @root_override = "/"+_normalize_input(path)
@root_override = "/"+normalize_input(path)
@lock_methods = true
@remap = true @remap = true
end end
def index(list) def index(list)
@ -244,11 +280,10 @@ module Hyde
def preprocess(&block) def preprocess(&block)
@preprocessor = block @preprocessor = block
end end
def match(request) def _match(request)
raise Exception, "Not permitted" if @lock_methods
self.instance_exec request, &@preprocessor if @preprocessor self.instance_exec request, &@preprocessor if @preprocessor
if match? request.path then if _match? request.path then
match_path = normalize_input(request.path).match(@path) match_path = _normalize_input(request.path).match(@path)
next_path = match_path[0] next_path = match_path[0]
request.path = cut_path = match_path.post_match request.path = cut_path = match_path.post_match
# remap/root method handling # remap/root method handling
@ -265,16 +300,17 @@ module Hyde
# do directory indexing # do directory indexing
if cut_path.match /^\/?$/ then if cut_path.match /^\/?$/ then
request.indexlist.each do |x| request.indexlist.each do |x|
try_index = @chain.find { |y| y.match? x } puts "Trying index #{x}"
try_index = @chain.find { |y| y._match? x }
if try_index then if try_index then
request.path = x request.path = x
return try_index.match request return try_index._match request
end end
end end
end end
# passthrough to the next path object # passthrough to the next path object
next_pathspec = @chain.find { |x| x.match? cut_path } next_pathspec = @chain.find { |x| x._match? cut_path }
next_pathspec.match request if next_pathspec next_pathspec._match request if next_pathspec
unless next_pathspec then unless next_pathspec then
# throw 404 if nowhere to go # throw 404 if nowhere to go
Hyde::ErrorPages::error404 request, request.request.path Hyde::ErrorPages::error404 request, request.request.path

View File

@ -1,32 +1,35 @@
require_relative 'hyde' require_relative 'hyde'
server = Hyde::Server.new Port: 8000 do server = Hyde::Server.new Port: 8000 do
root Dir::pwd+"/test/" root "/home/yessiest/Projects/hyde/test/"
serve "index.html" serve "index.html"
index ["index.html"] index ["index.html"]
path "about" do path "about" do
preprocess do |ctx|
puts "#{ctx} entered fully virtual directory!"
end
serve "portal" do |ctx|
ctx.vars[:ass] = true
rewrite "/about/hyde"
end
serve "webrick" do |ctx| serve "webrick" do |ctx|
ctx.response.body = "WEBrick is a modular http server stack" ctx.response.body = "WEBrick is a modular http server stack"
ctx.response['Content-Type'] = "text/plain" ctx.response['Content-Type'] = "text/plain"
end end
serve "en_passant" do |ctx|
puts "holy hell!"
redirect "https://google.com/search?q=en+passant"
end
serve "hyde" do |ctx| serve "hyde" do |ctx|
puts ctx.vars[:ass]
ctx.response.body = "Hyde is the disgusting side of Jekyll, and, by extension, the thing that makes WEBrick usable." ctx.response.body = "Hyde is the disgusting side of Jekyll, and, by extension, the thing that makes WEBrick usable."
ctx.response['Content-Type'] = "text/plain" ctx.response['Content-Type'] = "text/plain"
end end
end end
path "uploads" do path "uploads" do
preprocess { |request| puts request.inspect }
index ["index.html"] index ["index.html"]
serve ["**/*.md","*.html","**/*.html"], match_full_path: true serve "**/*.md", safe_regexp: false
end serve ["*.html","**/*.html"], safe_regexp: false
path ["vars_1","vars_2"] do
preprocess do |request|
request.vars["flag"] = "yes" if request.filepath.match "vars_1"
end
serve "view" do |ctx|
ctx.response.body = "Flag: #{ctx.vars["flag"]}"
ctx.response['Content-Type'] = "text/plain"
end
end end
end end