Added directory bouncing, status code handlers and pipelining

This commit is contained in:
Yessiest 2023-10-15 03:30:33 +04:00
parent 55e87603e5
commit 447f51570a
14 changed files with 189 additions and 22 deletions

18
examples/dirbounce.ru Normal file
View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
require 'landline'
app = Landline::Server.new do
path "/hello" do
bounce
get "/world" do
"Hello world!"
end
end
get "/hello/user" do
"Hello user!"
end
end
run app

27
examples/errorpages.ru Normal file
View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
require 'landline'
app = Landline::Server.new do
get "/hello" do
header "content-type", "text/plain"
"Hello World!"
end
get "/error" do
raise StandardError, "I raised an error! (and that's very sad)"
end
handle do |status, backtrace: nil|
page = ([Landline::Util::HTTP_STATUS[status]] +
(backtrace || [""])).join("\n")
[
{
"content-length": page.bytesize,
"content-type": "text/plain"
},
page
]
end
end
run app

View File

@ -0,0 +1,3 @@
#
# ~/.bash_logout
#

View File

@ -0,0 +1,5 @@
#
# ~/.bash_profile
#
[[ -f ~/.bashrc ]] && . ~/.bashrc

View File

@ -0,0 +1,10 @@
#
# ~/.bashrc
#
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
# alias ls='ls --color=auto'
# alias grep='grep --color=auto'
# PS1='[\u@\h \W]\$ '

Binary file not shown.

27
examples/uploader/form.ru Normal file
View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
require 'landline'
app = Landline::Server.new do
root ENV["PWD"]
index ["index.html"]
post "/" do
formdata = form if form?
files = {}
if formdata
formdata["form_files"].each do |file|
filename = file.filename.split("/").last
`mv #{file.tempfile.path} $PWD/files/#{filename}`
files[file.filename] = "<a href=\"files/#{filename}\">#{filename}</a>"
end
end
erubi(file("index.rhtml"), { formdata: files }).run
end
serve "/files/*"
get "/" do
erubi(file("index.rhtml")).run
end
end
run app

View File

@ -0,0 +1,31 @@
<!DOCTYPE>
<html>
<head>
<title>Form upload test</title>
<style>
.form { display: flex; flex-direction: column }
</style>
</head>
<body>
<h1>File uploader</h1>
<hr/>
<p> Add files here: <p>
<form method="post"
class="form"
enctype="multipart/form-data">
<input id="form_files" type="file" name="form_files[]" multiple>
<label for="form_files">Attach file</label>
<input type="submit" value="Send form">
</form>
<% if (defined? formdata) and formdata %>
<hr>
<ul>
<% formdata.each do |key, part| %>
<li><%= key %>: <%= part %></li>
<% end %>
</ul>
<% end %>
</body>
</html>

1
examples/uploader/lib Symbolic link
View File

@ -0,0 +1 @@
../../lib

View File

@ -0,0 +1 @@
Example of handling forms in Landline

View File

@ -5,6 +5,26 @@ module Landline
module DSL
# Common path methods
module PathMethods
# Bounce request if no handler found instead of issuing 404
def bounce
@origin.bounce = true
end
# Create a status code handler on path.
# Recursively applies to all paths unless overridden.
# @param [Integer, nil] Specify a status code to handle
def handle(code = "default", &block)
@origin.properties["handle.#{code}"] = block
end
# Insert a pass-through pipeline into request processing
# (i.e. for error handling purposes).
# Passed block should yield request (and return yielded data back).
# @param block [#call] block that yields request
def pipeline(&block)
@origin.pipeline = block
end
# Set path index
# @param index [Array,String]
def index(index)

View File

@ -5,12 +5,16 @@ require_relative 'node'
require_relative 'dsl/constructors_path'
require_relative 'dsl/methods_path'
require_relative 'dsl/methods_common'
require_relative 'dsl/methods_probe'
require_relative 'dsl/constructors_probe'
require_relative 'util/lookup'
module Landline
# Execution context for filters and preprocessors.
class ProcessorContext
include Landline::DSL::CommonMethods
include Landline::DSL::ProbeMethods
include Landline::DSL::ProbeConstructors
def initialize(path)
@origin = path
@ -54,21 +58,11 @@ module Landline
# @return [Boolean] true if further navigation will be done
# @raise [UncaughtThrowError] by default throws :response if no matches found.
def process(request)
return false unless run_filters(request)
run_preprocessors(request)
enqueue_postprocessors(request)
@children.each do |x|
if (value = x.go(request))
return value
end
if @pipeline
@pipeline.call(request) { |inner_req| process_wrapped(inner_req) }
else
process_wrapped(request)
end
value = index(request)
return value if value
_die(404)
rescue StandardError => e
_die(500, backtrace: [e.to_s] + e.backtrace)
end
# Add a preprocessor to the path.
@ -95,7 +89,9 @@ module Landline
@filters.append(block)
end
attr_reader :children, :properties
attr_reader :children, :properties, :request
attr_accessor :bounce, :pipeline
private
@ -123,6 +119,31 @@ module Landline
request.postprocessors.append(*@postprocessors)
end
# Method callback on successful request navigation.
# Finds the next appropriate path to go to.
# (inner pipeline-wrapped handler)
# @return [Boolean] true if further navigation will be done
# @raise [UncaughtThrowError] by default throws :response if no matches found.
def process_wrapped(request)
@request = request
return false unless run_filters(request)
run_preprocessors(request)
enqueue_postprocessors(request)
@children.each do |x|
value = x.go(request)
return value if value
end
value = index(request)
return value if value
@bounce ? false : _die(404)
rescue StandardError => e
_die(500, backtrace: [e.to_s] + e.backtrace)
ensure
@request = nil
end
# Try to perform indexing on the path if possible
# @param request [Landline::Request]
# @return [Boolean] true if indexing succeeded
@ -146,11 +167,12 @@ module Landline
# @raise [UncaughtThrowError] throws :finish to stop processing
def _die(errorcode, backtrace: nil)
throw :finish, [errorcode].append(
*(@properties["handle.#{errorcode}"] or
@properties["handle.default"]).call(
errorcode,
backtrace: backtrace
)
*@proccontext.instance_exec(
errorcode,
backtrace: backtrace,
&(@properties["handle.#{errorcode}"] or
@properties["handle.default"])
)
)
end
end

View File

@ -15,10 +15,10 @@ module Landline
@context = Landline::ProbeContext.new(self)
@response = nil
end
attr_accessor :response
attr_reader :request
# Method callback on successful request navigation.
# Runs block supplied with object initialization.
# Request's #splat and #param are passed to block.

View File

@ -88,6 +88,8 @@ module Landline
# Import a template from within current template
def import(filepath)
filepath = filepath.is_a? File ? filepath : File.open(filepath)
# @sg-ignore
newtemp = self.class.new(filepath, {}, parent: @parent)
newtemp.binding = @binding
newtemp