added sinatra-like method matching, redirect and rewrite functions
This commit is contained in:
parent
b20244e2e5
commit
77a5e9a6b5
136
hyde.rb
136
hyde.rb
|
@ -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 slashe s 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue