File serving, root/remap directives, filters, preprocessors, postprocessors
This commit is contained in:
parent
fccc7ea9f0
commit
bbfb6f00d9
|
@ -0,0 +1,30 @@
|
||||||
|
# Notes and things to consider on Hyde hacking
|
||||||
|
|
||||||
|
The structure of the Hyde rewrite was specifically redesigned to allow for
|
||||||
|
extensive modification and extension. So to keep things that way, you may
|
||||||
|
want to consider the methodology of writing Hyde code.
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
To keep things beautiful, consider following recommendations:
|
||||||
|
|
||||||
|
- **USE COMMON SENSE**. None of these guidelines will ever be adequate
|
||||||
|
enough to replace common sense.
|
||||||
|
- **Code less, think with ~~portals~~ what you have**. To minimize code
|
||||||
|
overhead, try to use existing functionality to get the effect you want.
|
||||||
|
(i.e. if you want to append headers to a request when it traverses a path,
|
||||||
|
don't write a new class variable and handler for this - just create a new
|
||||||
|
DSL method that really just appends a preprocessor to the path. Or avoid
|
||||||
|
making something like this at all - after all, preprocessors exist
|
||||||
|
exactly for that reason.)
|
||||||
|
- Preferably, **extend the DSL and not the class*. Extensive class
|
||||||
|
modifications make code a lot less maintainable, if it wasn't obvious
|
||||||
|
already. If it can't be helped, then at the very least use Rubocop.
|
||||||
|
- Document classes as if the next maintainer after you has you at gunpoint.
|
||||||
|
Document thoroughly, use YARD tags and **never** skip on public method
|
||||||
|
docs and class docs. As an example, consider Hyde::PatternMatching::Glob.
|
||||||
|
- Unit tests suck for many reasons. However, if you're writing a class that
|
||||||
|
does not have any dependents and which is frequently used, consider making
|
||||||
|
a unit test for it. People that might have to fix things further along
|
||||||
|
will be very thankful.
|
||||||
|
|
38
config.ru
38
config.ru
|
@ -1,38 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
|
|
||||||
require_relative 'lib/hyde'
|
|
||||||
|
|
||||||
app = Hyde::Server.new do
|
|
||||||
path /^test\/\w+/ do
|
|
||||||
probe "probe"
|
|
||||||
end
|
|
||||||
|
|
||||||
path "/subdir/test" do
|
|
||||||
probe "probe"
|
|
||||||
end
|
|
||||||
|
|
||||||
path "/match/*/test/:test/" do
|
|
||||||
probe "probe"
|
|
||||||
end
|
|
||||||
|
|
||||||
path "/match/:test/" do
|
|
||||||
probe "probe"
|
|
||||||
end
|
|
||||||
|
|
||||||
path "/match2/*/" do
|
|
||||||
head "probe" do
|
|
||||||
"<html><body><p>Hello world!</p></body></html>"
|
|
||||||
end
|
|
||||||
get "probe" do
|
|
||||||
code 400
|
|
||||||
header "Random-Shit", "peepee"
|
|
||||||
"<html><body><p>Hello world!</p></body></html>"
|
|
||||||
end
|
|
||||||
post "probe" do
|
|
||||||
"<html><body><p>Hello world!</p></body></html>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
run app
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p> index page </p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,3 @@
|
||||||
|
.a {
|
||||||
|
color: #FF00FF;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
|
||||||
|
require_relative 'lib/hyde'
|
||||||
|
|
||||||
|
app = Hyde::Server.new do
|
||||||
|
preprocess do |request|
|
||||||
|
puts "New request: #{request}"
|
||||||
|
end
|
||||||
|
filter do
|
||||||
|
rand < 0.5
|
||||||
|
end
|
||||||
|
index ["index"]
|
||||||
|
root "#{ENV['PWD']}/assets"
|
||||||
|
serve "*.(html|css|js)"
|
||||||
|
end
|
||||||
|
|
||||||
|
run app
|
|
@ -0,0 +1 @@
|
||||||
|
../lib
|
|
@ -71,6 +71,10 @@ module Hyde
|
||||||
register(Hyde::OPTIONSHandler.new(path, parent: @origin, &setup))
|
register(Hyde::OPTIONSHandler.new(path, parent: @origin, &setup))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Create a new {Hyde::GETHandler} that serves static files
|
||||||
|
def serve(path)
|
||||||
|
register(Hyde::ServeHandler.new(path, parent: @origin))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Hyde
|
||||||
|
# Shared DSL methods
|
||||||
|
module DSL
|
||||||
|
# Common path methods
|
||||||
|
module PathMethods
|
||||||
|
# Set path index
|
||||||
|
# @param index [Array,String]
|
||||||
|
def index(index)
|
||||||
|
case index
|
||||||
|
when Array
|
||||||
|
@origin.properties['index'] = index
|
||||||
|
when String
|
||||||
|
@origin.properties['index'] = [index]
|
||||||
|
else
|
||||||
|
raise StandardError, "index should be an Array or a String"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set root path (appends matched part of the path).
|
||||||
|
# @param path [String
|
||||||
|
def root(path)
|
||||||
|
raise StandardError, "path should be a String" unless path.is_a? String
|
||||||
|
|
||||||
|
@origin.root = path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set root path (without appending matched part).
|
||||||
|
# @param path [String
|
||||||
|
def remap(path)
|
||||||
|
root(path)
|
||||||
|
@origin.remap = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a preprocessor to the path.
|
||||||
|
# Does not modify path execution.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
def preprocess(&block)
|
||||||
|
@origin.preprocess(&block)
|
||||||
|
block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a postprocessor to the path.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
# @yieldparam response [Hyde::Response]
|
||||||
|
def postprocess(&block)
|
||||||
|
@origin.postprocess(&block)
|
||||||
|
block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a filter to the path.
|
||||||
|
# Blocks path access if a filter returns false.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
def filter(&block)
|
||||||
|
@origin.filter(&block)
|
||||||
|
block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative 'pattern_matching/util'
|
||||||
|
|
||||||
module Hyde
|
module Hyde
|
||||||
# Abstract class that reacts to request navigation.
|
# Abstract class that reacts to request navigation.
|
||||||
# Does nothing by default, behaviour should be overriden through
|
# Does nothing by default, behaviour should be overriden through
|
||||||
|
@ -9,6 +11,8 @@ module Hyde
|
||||||
# @param path [Object]
|
# @param path [Object]
|
||||||
def initialize(path)
|
def initialize(path)
|
||||||
@pattern = Pattern.new(path).freeze
|
@pattern = Pattern.new(path).freeze
|
||||||
|
@root = nil
|
||||||
|
@remap = false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try to navigate the path. Run method callback in response.
|
# Try to navigate the path. Run method callback in response.
|
||||||
|
@ -19,7 +23,9 @@ module Hyde
|
||||||
return reject(request) unless @pattern.match?(request.path)
|
return reject(request) unless @pattern.match?(request.path)
|
||||||
|
|
||||||
request.push_state
|
request.push_state
|
||||||
request.path, splat, param = @pattern.match(request.path)
|
path, splat, param = @pattern.match(request.path)
|
||||||
|
do_filepath(request, request.path.delete_suffix(path))
|
||||||
|
request.path = path
|
||||||
request.splat.append(*splat)
|
request.splat.append(*splat)
|
||||||
request.param.merge!(param)
|
request.param.merge!(param)
|
||||||
value = process(request)
|
value = process(request)
|
||||||
|
@ -42,5 +48,19 @@ module Hyde
|
||||||
def process(_request)
|
def process(_request)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_accessor :remap, :root
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Process filepath for request
|
||||||
|
def do_filepath(request, path)
|
||||||
|
if @root
|
||||||
|
request.filepath = "#{@root}/#{@remap ? '' : path}/"
|
||||||
|
else
|
||||||
|
request.filepath += "/#{path}/"
|
||||||
|
end
|
||||||
|
request.filepath.gsub!(/\/+/, "/")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
require_relative 'pattern_matching'
|
require_relative 'pattern_matching'
|
||||||
require_relative 'node'
|
require_relative 'node'
|
||||||
require_relative 'dsl/path_constructors'
|
require_relative 'dsl/path_constructors'
|
||||||
|
require_relative 'dsl/path_methods'
|
||||||
require_relative 'util/lookup'
|
require_relative 'util/lookup'
|
||||||
|
|
||||||
module Hyde
|
module Hyde
|
||||||
# Protected interface that provides DSL context for setup block.
|
# Protected interface that provides DSL context for setup block.
|
||||||
class PathBinding
|
class PathBinding
|
||||||
include Hyde::DSL::PathConstructors
|
include Hyde::DSL::PathConstructors
|
||||||
|
include Hyde::DSL::PathMethods
|
||||||
|
|
||||||
def initialize(path)
|
def initialize(path)
|
||||||
@origin = path
|
@origin = path
|
||||||
|
@ -24,8 +26,14 @@ module Hyde
|
||||||
# @param setup [#call] Setup block
|
# @param setup [#call] Setup block
|
||||||
def initialize(path, parent:, &setup)
|
def initialize(path, parent:, &setup)
|
||||||
super(path)
|
super(path)
|
||||||
|
# Child nodes array
|
||||||
@children = []
|
@children = []
|
||||||
|
# Inherited properties array
|
||||||
@properties = Hyde::Util::Lookup.new(parent&.properties)
|
@properties = Hyde::Util::Lookup.new(parent&.properties)
|
||||||
|
# Arrays of preprocessors, postprocessors and filters
|
||||||
|
@preprocessors = []
|
||||||
|
@postprocessors = []
|
||||||
|
@filters = []
|
||||||
|
|
||||||
binding = Binding.new(self)
|
binding = Binding.new(self)
|
||||||
binding.instance_exec(&setup)
|
binding.instance_exec(&setup)
|
||||||
|
@ -36,6 +44,10 @@ module Hyde
|
||||||
# @return [Boolean] true if further navigation will be done
|
# @return [Boolean] true if further navigation will be done
|
||||||
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
||||||
def process(request)
|
def process(request)
|
||||||
|
return false unless run_filters(request)
|
||||||
|
|
||||||
|
run_preprocessors(request)
|
||||||
|
enqueue_postprocessors(request)
|
||||||
@children.each do |x|
|
@children.each do |x|
|
||||||
if (value = x.go(request))
|
if (value = x.go(request))
|
||||||
return value
|
return value
|
||||||
|
@ -49,10 +61,58 @@ module Hyde
|
||||||
_die(500, backtrace: [e.to_s] + e.backtrace)
|
_die(500, backtrace: [e.to_s] + e.backtrace)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add a preprocessor to the path.
|
||||||
|
# Does not modify path execution.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
def preprocess(&block)
|
||||||
|
@preprocessors.append(block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a postprocessor to the path.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
# @yieldparam response [Hyde::Response]
|
||||||
|
def postprocess(&block)
|
||||||
|
@postprocessors.append(block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a filter to the path.
|
||||||
|
# Blocks path access if a filter returns false.
|
||||||
|
# @param block [#call]
|
||||||
|
# @yieldparam request [Hyde::Request]
|
||||||
|
def filter(&block)
|
||||||
|
@filters.append(block)
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :children, :properties
|
attr_reader :children, :properties
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Sequentially run through all filters and drop request if one is false
|
||||||
|
# @param request [Hyde::Request]
|
||||||
|
# @return [Boolean] true if request passed all filters
|
||||||
|
def run_filters(request)
|
||||||
|
@filters.each do |filter|
|
||||||
|
return false if filter.call(request).is_a? FalseClass
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sequentially run all preprocessors on a request
|
||||||
|
# @param request [Hyde::Request]
|
||||||
|
def run_preprocessors(request)
|
||||||
|
@preprocessors.each do |preproc|
|
||||||
|
preproc.call(request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Append postprocessors to request
|
||||||
|
# @param request [Hyde::Request]
|
||||||
|
def enqueue_postprocessors(request)
|
||||||
|
request.postprocessors.append(*@postprocessors)
|
||||||
|
end
|
||||||
|
|
||||||
# Try to perform indexing on the path if possible
|
# Try to perform indexing on the path if possible
|
||||||
# @param request [Hyde::Request]
|
# @param request [Hyde::Request]
|
||||||
# @return [Boolean] true if indexing succeeded
|
# @return [Boolean] true if indexing succeeded
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
require_relative 'node'
|
require_relative 'node'
|
||||||
require_relative 'util/lookup'
|
require_relative 'util/lookup'
|
||||||
|
require 'pp'
|
||||||
|
|
||||||
module Hyde
|
module Hyde
|
||||||
autoload :Handler, "hyde/probe/handler"
|
autoload :Handler, "hyde/probe/handler"
|
||||||
|
@ -14,7 +15,7 @@ module Hyde
|
||||||
autoload :OPTIONSHandler, "hyde/probe/http_method"
|
autoload :OPTIONSHandler, "hyde/probe/http_method"
|
||||||
autoload :TRACEHandler, "hyde/probe/http_method"
|
autoload :TRACEHandler, "hyde/probe/http_method"
|
||||||
autoload :PATCHHandler, "hyde/probe/http_method"
|
autoload :PATCHHandler, "hyde/probe/http_method"
|
||||||
|
autoload :ServeHandler, "hyde/probe/serve_handler"
|
||||||
# Test probe. Also base for all "reactive" nodes.
|
# Test probe. Also base for all "reactive" nodes.
|
||||||
class Probe < Hyde::Node
|
class Probe < Hyde::Node
|
||||||
# @param path [Object]
|
# @param path [Object]
|
||||||
|
@ -31,8 +32,11 @@ module Hyde
|
||||||
# @return [Boolean] true if further navigation is possible
|
# @return [Boolean] true if further navigation is possible
|
||||||
# @raise [StandardError]
|
# @raise [StandardError]
|
||||||
def process(request)
|
def process(request)
|
||||||
|
return reject(request) unless request.path.match?(/^\/?$/)
|
||||||
|
|
||||||
raise StandardError, <<~STREND
|
raise StandardError, <<~STREND
|
||||||
probe reached #{request.splat.inspect}, #{request.param.inspect}
|
probe reached #{request.splat.inspect}, #{request.param.inspect}
|
||||||
|
#{request.pretty_inspect}
|
||||||
STREND
|
STREND
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,8 @@ module Hyde
|
||||||
# @return [Boolean] true if further navigation is possible
|
# @return [Boolean] true if further navigation is possible
|
||||||
# @raise [UncaughtThrowError] may raise if die() is called.
|
# @raise [UncaughtThrowError] may raise if die() is called.
|
||||||
def process(request)
|
def process(request)
|
||||||
|
return reject(request) unless request.path.match?(/^\/?$/)
|
||||||
|
|
||||||
@request = request
|
@request = request
|
||||||
response = catch(:break) do
|
response = catch(:break) do
|
||||||
@binding.instance_exec(*request.splat,
|
@binding.instance_exec(*request.splat,
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative '../probe'
|
||||||
|
require_relative 'binding'
|
||||||
|
|
||||||
|
module Hyde
|
||||||
|
# Probe that sends files from a location
|
||||||
|
class ServeHandler < Hyde::Probe
|
||||||
|
# @param path [Object]
|
||||||
|
# @param parent [Hyde::Node]
|
||||||
|
# @param exec [#call]
|
||||||
|
def initialize(path, parent:)
|
||||||
|
super(path, parent: parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :response
|
||||||
|
|
||||||
|
# Method callback on successful request navigation.
|
||||||
|
# Tries to serve files matched by handler
|
||||||
|
# @param request [Hyde::Request]
|
||||||
|
# @return [Boolean] true if file was found
|
||||||
|
def process(request)
|
||||||
|
File.open(request.filepath.delete_suffix("/"))
|
||||||
|
rescue StandardError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,7 +9,57 @@ module Hyde
|
||||||
def initialize(env)
|
def initialize(env)
|
||||||
# Should not be used under regular circumstances or depended upon.
|
# Should not be used under regular circumstances or depended upon.
|
||||||
@_original_env = env
|
@_original_env = env
|
||||||
# Rack environment variable bindings. Should be public and readonly.
|
# Rack environment variable bindings. Should be public and frozen.
|
||||||
|
init_request_params(env)
|
||||||
|
# Pattern matching parameters. Public, readable, unfrozen.
|
||||||
|
@param = {}
|
||||||
|
@splat = []
|
||||||
|
# Traversal route. Public and writable.
|
||||||
|
@path = env["PATH_INFO"].dup
|
||||||
|
# File serving path. Public and writable.
|
||||||
|
@filepath = "/"
|
||||||
|
# Encapsulates all rack variables. Should not be public.
|
||||||
|
@rack = init_rack_vars(env)
|
||||||
|
# Internal navigation states. Private.
|
||||||
|
@states = []
|
||||||
|
# Postprocessors for current request
|
||||||
|
@postprocessors = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run postprocessors
|
||||||
|
# @param response [Hyde::Response]
|
||||||
|
def run_postprocessors(response)
|
||||||
|
@postprocessors.each do |postproc|
|
||||||
|
postproc.call(self, response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns request body (if POST data exists)
|
||||||
|
# @return [nil, String]
|
||||||
|
def body
|
||||||
|
@rack.input&.gets
|
||||||
|
end
|
||||||
|
|
||||||
|
# Push current navigation state (path, splat, param) onto state stack
|
||||||
|
def push_state
|
||||||
|
@states.push([@path, @param.dup, @splat.dup, @filepath.dup])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Load last navigation state (path, splat, param) from state stack
|
||||||
|
def pop_state
|
||||||
|
@path, @param, @splat, @filepath = @states.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :request_method, :script_name, :path_info, :server_name,
|
||||||
|
:server_port, :server_protocol, :headers, :param, :splat,
|
||||||
|
:postprocessors
|
||||||
|
attr_accessor :path, :filepath
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Initialize basic rack request parameters
|
||||||
|
# @param env [Hash]
|
||||||
|
def init_request_params(env)
|
||||||
@request_method = env["REQUEST_METHOD"]
|
@request_method = env["REQUEST_METHOD"]
|
||||||
@script_name = env["SCRIPT_NAME"]
|
@script_name = env["SCRIPT_NAME"]
|
||||||
@path_info = env["PATH_INFO"]
|
@path_info = env["PATH_INFO"]
|
||||||
|
@ -17,37 +67,8 @@ module Hyde
|
||||||
@server_port = env["SERVER_PORT"]
|
@server_port = env["SERVER_PORT"]
|
||||||
@server_protocol = env["SERVER_PROTOCOL"]
|
@server_protocol = env["SERVER_PROTOCOL"]
|
||||||
@headers = init_headers(env)
|
@headers = init_headers(env)
|
||||||
@param = {}
|
|
||||||
@splat = []
|
|
||||||
# Traversal route. Public, writable and readable.
|
|
||||||
@path = env["PATH_INFO"].dup
|
|
||||||
# Encapsulates all rack variables. Should not be public.
|
|
||||||
@rack = init_rack_vars(env)
|
|
||||||
# Internal navigation states
|
|
||||||
@states = []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns request body (if POST data exists)
|
|
||||||
def body
|
|
||||||
@rack.input&.gets
|
|
||||||
end
|
|
||||||
|
|
||||||
# Push current navigation state (path, splat, param) onto state stack
|
|
||||||
def push_state
|
|
||||||
@states.push([@path, @param.dup, @splat.dup])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Load last navigation state (path, splat, param) from state stack
|
|
||||||
def pop_state
|
|
||||||
@path, @param, @splat = @states.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :request_method, :script_name, :path_info, :server_name,
|
|
||||||
:server_port, :server_protocol, :headers, :param, :splat
|
|
||||||
attr_accessor :path
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Initialize rack parameters struct
|
# Initialize rack parameters struct
|
||||||
# @param env [Hash]
|
# @param env [Hash]
|
||||||
# @return Object
|
# @return Object
|
||||||
|
|
|
@ -27,15 +27,15 @@ module Hyde
|
||||||
end
|
end
|
||||||
|
|
||||||
# Make internal representation conformant
|
# Make internal representation conformant
|
||||||
|
# @return [Hyde::Response]
|
||||||
def validate
|
def validate
|
||||||
if [204, 304].include?(@status) or (100..199).include?(@status)
|
if [204, 304].include?(@status) or (100..199).include?(@status)
|
||||||
@headers.delete "content-length"
|
@headers.delete "content-length"
|
||||||
@headers.delete "content-type"
|
@headers.delete "content-type"
|
||||||
@body = []
|
@body = []
|
||||||
elsif @headers.empty?
|
elsif @headers.empty?
|
||||||
length = @body.is_a?(String) ? @body.length : @body.join.length
|
|
||||||
@headers = {
|
@headers = {
|
||||||
"content-length" => length,
|
"content-length" => content_size,
|
||||||
"content-type" => "text/html"
|
"content-type" => "text/html"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -82,19 +82,30 @@ module Hyde
|
||||||
when String, File, IO
|
when String, File, IO
|
||||||
Response.new([200,
|
Response.new([200,
|
||||||
{
|
{
|
||||||
"content-type" => "text/html",
|
"content-type" => "text/html"
|
||||||
"content-length" => obj.length
|
|
||||||
},
|
},
|
||||||
chunk_body(obj)])
|
obj]).validate
|
||||||
|
else
|
||||||
|
Response.new([404, {}, []])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Turn body into array of chunks
|
# Turn body into array of chunks
|
||||||
|
# @param text [String]
|
||||||
|
# @return [Array(String)]
|
||||||
def self.chunk_body(text)
|
def self.chunk_body(text)
|
||||||
if text.is_a? String
|
text.chars.each_slice(@chunk_size).map(&:join)
|
||||||
text.chars.each_slice(@chunk_size).map(&:join)
|
end
|
||||||
elsif text.is_a? Array
|
|
||||||
text
|
private
|
||||||
|
|
||||||
|
# Try to figure out content length
|
||||||
|
# @return [Integer, nil]
|
||||||
|
def content_size
|
||||||
|
case @body
|
||||||
|
when String then @body.length
|
||||||
|
when Array then @body.join.length
|
||||||
|
when File then @body.size
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,6 +13,8 @@ module Hyde
|
||||||
class Server < Hyde::Path
|
class Server < Hyde::Path
|
||||||
Binding = ServerBinding
|
Binding = ServerBinding
|
||||||
|
|
||||||
|
# @param parent [Hyde::Node, nil] Parent object to inherit properties to
|
||||||
|
# @param setup [#call] Setup block
|
||||||
def initialize(parent: nil, &setup)
|
def initialize(parent: nil, &setup)
|
||||||
super("", parent: parent, &setup)
|
super("", parent: parent, &setup)
|
||||||
return if parent
|
return if parent
|
||||||
|
@ -28,18 +30,21 @@ module Hyde
|
||||||
[headers, page]
|
[headers, page]
|
||||||
end
|
end
|
||||||
}.each do |k, v|
|
}.each do |k, v|
|
||||||
@properties[k] = v
|
@properties[k] = v unless @properties[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rack ingress point.
|
# Rack ingress point.
|
||||||
# This should not be called under any circumstances twice in the same application,
|
# This should not be called under any circumstances twice in the same application,
|
||||||
# although server nesting for the purpose of creating virtual hosts is allowed.
|
# although server nesting for the purpose of creating virtual hosts is allowed.
|
||||||
|
# @param env [Hash]
|
||||||
|
# @return [Array(Integer,Hash,Array)]
|
||||||
def call(env)
|
def call(env)
|
||||||
|
request = Hyde::Request.new(env)
|
||||||
response = catch(:finish) do
|
response = catch(:finish) do
|
||||||
request = Hyde::Request.new(env)
|
|
||||||
go(request)
|
go(request)
|
||||||
end
|
end
|
||||||
|
request.run_postprocessors(response)
|
||||||
Response.convert(response).finalize
|
Response.convert(response).finalize
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue