From 77a5e9a6b5e40dfdf6f6383af8abae18866d3834 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Sun, 30 Apr 2023 03:19:40 +0400 Subject: [PATCH] added sinatra-like method matching, redirect and rewrite functions --- hyde.rb | 142 +++++++++++++++++++++++++++++------------------ test_combined.rb | 27 +++++---- 2 files changed, 104 insertions(+), 65 deletions(-) diff --git a/hyde.rb b/hyde.rb index 32bf320..1c06088 100644 --- a/hyde.rb +++ b/hyde.rb @@ -1,9 +1,10 @@ require 'mime-types' require 'webrick' +require 'uri' module Hyde # Branding and version - VERSION = "0.1" + VERSION = "0.5 (alpha)" attr_reader :VERSION VLINE = "
\n Hyde/#{Hyde::VERSION} on WEBrick/#{WEBrick::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n
" attr_reader :VLINE @@ -13,7 +14,14 @@ module Hyde super(config) @hyde_pathspec = Hyde::Pathspec.new "/", &setup 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 @@ -36,14 +44,12 @@ module Hyde # Interchangeable glob/regex/string pattern matching module PatternMatching - def prep_path(path,match_full_path: false) - raise Exception, "Not permitted" if @lock_methods - @match_full_path = match_full_path - @path = normalize(path) if path.kind_of? String + def _prep_path(path,safe_regexp: true) + @safe_regexp = safe_regexp + @path = _normalize(path) if path.kind_of? String @path = path if path.kind_of? Regexp end - def match?(path) - raise Exception, "Not permitted" if @lock_methods + def _match?(path) # behvaiour used by "index" method return true if @path == "" 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: # 1) unsafe regexp means match the whole (remaining) line. # 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 - test = @path.match normalize_input(path) if @match_full_path - test = @path.match split_path[0] unless @match_full_path + test = @path.match _normalize_input(path) unless @safe_regexp + test = @path.match split_path[0] if @safe_regexp if test and (test.pre_match == "") and (test.post_match == "") then return true else @@ -69,15 +75,13 @@ module Hyde return true end end - def normalize_input(path) - raise Exception, "Not permitted" if @lock_methods + def _normalize_input(path) # remove duplicate slashes and trim edge slashes (path.split "/").filter { |x| x != "" }.join("/") end - def normalize(path) - raise Exception, "Not permitted" if @lock_methods - # remove duplicate slashes and trim edge slashes - path = normalize_input(path) + def _normalize(path) + # remove duplicate slashe s and trim edge slashes + path = _normalize_input(path) # globbing behaviour simulated with regexp if path.match /(?#{url}.\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 @@ -110,6 +122,12 @@ module Hyde @handles = {} @indexlist = [] @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 attr_reader :request attr_reader :response @@ -117,6 +135,7 @@ module Hyde attr_accessor :path attr_accessor :handles attr_accessor :indexlist + attr_accessor :exit_loop attr_accessor :vars end @@ -129,6 +148,7 @@ module Hyde @response = request.response @handles = request.handles @indexlist = request.indexlist + @exit_loop = request.exit_loop @vars = request.vars end undef :path= @@ -141,26 +161,24 @@ module Hyde class Probe include Hyde::PatternMatching include Hyde::PublicContextControlMethods - def initialize (path, match_full_path: false, &block_optional) - prep_path path, match_full_path: match_full_path + def initialize (path, safe_regexp: true, &block_optional) + _prep_path path, safe_regexp: safe_regexp @block = block_optional end - def match(request) - if @block and (match? request.path) then - @current_request = Hyde::ProtectedContext.new(request) - @lock_methods = true - return_later = self.instance_exec @current_request, &@block - @lock_methods = false + def _match(request) + if @block and (_match? request.path) then + @current_context = Hyde::ProtectedContext.new(request) + return_later = self.instance_exec @current_context, &@block return return_later end end end class Serve < Hyde::Probe - def match(request) + def _match(request) return super if @block - if match? request.path then - match_path = normalize_input(request.path).match(@path)[0] + if _match? request.path then + match_path = _normalize_input(request.path).match(@path)[0] filepath = request.filepath+match_path begin mimetype = MIME::Types.type_for(filepath) @@ -175,9 +193,25 @@ module Hyde 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 - def match(request) - if match? request.path then + def _match(request) + if _match? request.path then puts "#{request.path} matched!" end end @@ -188,7 +222,15 @@ module Hyde { probe: Hyde::Probe, 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| define_method name do |path, *a, **b, &block| if path.kind_of? Array then @@ -205,15 +247,13 @@ module Hyde class Pathspec include Hyde::PatternMatching include Hyde::Handlers - def initialize (path, root_path: nil, match_full_path: false, &block) - prep_path path, match_full_path: match_full_path + 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 - @lock_methods = true self.instance_exec &block - @lock_methods = false end def path(path, *a, **b, &block) if path.kind_of? Array then @@ -225,14 +265,10 @@ module Hyde end end def root(path) - @lock_methods = false - @root_override = "/"+normalize_input(path) - @lock_methods = true + @root_override = "/"+_normalize_input(path) end def remap(path) - @lock_methods = false - @root_override = "/"+normalize_input(path) - @lock_methods = true + @root_override = "/"+_normalize_input(path) @remap = true end def index(list) @@ -241,14 +277,13 @@ module Hyde def handle(code, &block) @handles[code] = block end - def preprocess(&block) + def preprocess(&block) @preprocessor = block end - def match(request) - raise Exception, "Not permitted" if @lock_methods + def _match(request) self.instance_exec request, &@preprocessor if @preprocessor - if match? request.path then - match_path = normalize_input(request.path).match(@path) + if _match? request.path then + match_path = _normalize_input(request.path).match(@path) next_path = match_path[0] request.path = cut_path = match_path.post_match # remap/root method handling @@ -264,17 +299,18 @@ module Hyde request.indexlist = @indexlist if @indexlist # do directory indexing if cut_path.match /^\/?$/ then - request.indexlist.each do |x| - try_index = @chain.find { |y| y.match? x } + request.indexlist.each do |x| + puts "Trying index #{x}" + try_index = @chain.find { |y| y._match? x } if try_index then request.path = x - return try_index.match request + return try_index._match request end end end # passthrough to the next path object - next_pathspec = @chain.find { |x| x.match? cut_path } - next_pathspec.match request if next_pathspec + next_pathspec = @chain.find { |x| x._match? cut_path } + next_pathspec._match request if next_pathspec unless next_pathspec then # throw 404 if nowhere to go Hyde::ErrorPages::error404 request, request.request.path diff --git a/test_combined.rb b/test_combined.rb index 863f29a..36632f5 100644 --- a/test_combined.rb +++ b/test_combined.rb @@ -1,32 +1,35 @@ require_relative 'hyde' server = Hyde::Server.new Port: 8000 do - root Dir::pwd+"/test/" + root "/home/yessiest/Projects/hyde/test/" serve "index.html" index ["index.html"] 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| ctx.response.body = "WEBrick is a modular http server stack" ctx.response['Content-Type'] = "text/plain" end + serve "en_passant" do |ctx| + puts "holy hell!" + redirect "https://google.com/search?q=en+passant" + end 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['Content-Type'] = "text/plain" end end path "uploads" do - preprocess { |request| puts request.inspect } index ["index.html"] - serve ["**/*.md","*.html","**/*.html"], match_full_path: true - end - 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 + serve "**/*.md", safe_regexp: false + serve ["*.html","**/*.html"], safe_regexp: false end end