Version bump and, finally, thread safety and shared request context memory
This commit is contained in:
parent
de59eea736
commit
e1e2154677
|
@ -19,31 +19,28 @@ end
|
||||||
|
|
||||||
# Example Landline application as rack middleware
|
# Example Landline application as rack middleware
|
||||||
class HelloServer < Landline::App
|
class HelloServer < Landline::App
|
||||||
setup do
|
get "/test2" do
|
||||||
get "/test2" do
|
"Hello world from #{self}!"
|
||||||
"Hello world from #{self}!"
|
end
|
||||||
end
|
|
||||||
handle do |status, backtrace: nil|
|
handle do |status, backtrace: nil|
|
||||||
page = ([Landline::Util::HTTP_STATUS[status]] +
|
page = ([Landline::Util::HTTP_STATUS[status]] +
|
||||||
(backtrace || [""])).join("\n")
|
(backtrace || [""])).join("\n")
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"content-length": page.bytesize,
|
"content-length": page.bytesize,
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
"x-cascade": true
|
"x-cascade": true
|
||||||
},
|
},
|
||||||
page
|
page
|
||||||
]
|
]
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Example Landline app as rack application
|
# Example Landline app as rack application
|
||||||
class CrossCallServer < Landline::App
|
class CrossCallServer < Landline::App
|
||||||
setup do
|
get "/inner_test" do
|
||||||
get "/inner_test" do
|
"Hello world, through crosscall!"
|
||||||
"Hello world, through crosscall!"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,21 +49,19 @@ class Server < Landline::App
|
||||||
use TimerMiddleware
|
use TimerMiddleware
|
||||||
use HelloServer
|
use HelloServer
|
||||||
|
|
||||||
setup do
|
crosscall_server = CrossCallServer.new
|
||||||
crosscall_server = CrossCallServer.new
|
|
||||||
|
|
||||||
get "/test" do
|
get "/test" do
|
||||||
"Hello from #{self}!"
|
"Hello from #{self}!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Cross-callable application included as a subpath
|
# Cross-callable application included as a subpath
|
||||||
link "/outer", crosscall_server
|
link "/outer", crosscall_server
|
||||||
|
|
||||||
# Cross calling an application in a probe context
|
# Cross calling an application in a probe context
|
||||||
get "/crosscall" do
|
get "/crosscall" do
|
||||||
request.path = "/inner_test"
|
request.path = "/inner_test"
|
||||||
call(crosscall_server)
|
call(crosscall_server)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'landline'
|
||||||
|
|
||||||
|
class Application < Landline::App
|
||||||
|
def initialize(*args)
|
||||||
|
puts self
|
||||||
|
super(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
preprocess do
|
||||||
|
puts Thread.current.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/long" do
|
||||||
|
before = request
|
||||||
|
sleep 10
|
||||||
|
"Request didn't change mid-execution: #{before == request}"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/long2" do
|
||||||
|
puts self
|
||||||
|
@instance_property = (rand * 1000).floor
|
||||||
|
response = "Your magic number is #{@instance_property}\n"
|
||||||
|
sleep 20
|
||||||
|
response += "Your magic number after waiting 20 seconds is #{@instance_property}"
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/magic" do
|
||||||
|
@instance_property = (rand * 1000).floor
|
||||||
|
end
|
||||||
|
|
||||||
|
postprocess do
|
||||||
|
puts "Last value of instance_property: #{@instance_property}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run Application.new
|
|
@ -4,28 +4,26 @@ require 'landline'
|
||||||
require 'landline/extensions/websocket'
|
require 'landline/extensions/websocket'
|
||||||
|
|
||||||
class Test < Landline::App
|
class Test < Landline::App
|
||||||
setup do
|
websocket "/test", version: 7 do |socket|
|
||||||
websocket "/test", version: 7 do |socket|
|
socket.on :message do |msg|
|
||||||
socket.on :message do |msg|
|
puts "Client wrote: #{msg}"
|
||||||
puts "Client wrote: #{msg}"
|
|
||||||
end
|
|
||||||
socket.on :error do |err|
|
|
||||||
puts "Error occured: #{err.inspect}"
|
|
||||||
puts err.backtrace
|
|
||||||
end
|
|
||||||
socket.on :close do
|
|
||||||
puts "Client closed read connection"
|
|
||||||
end
|
|
||||||
socket.ready
|
|
||||||
socket.write("Hi!")
|
|
||||||
response = socket.read
|
|
||||||
socket.write("You said: #{response}")
|
|
||||||
socket.write("Goodbye!")
|
|
||||||
socket.close
|
|
||||||
rescue Exception => e
|
|
||||||
puts e.inspect
|
|
||||||
puts e.backtrace
|
|
||||||
end
|
end
|
||||||
|
socket.on :error do |err|
|
||||||
|
puts "Error occured: #{err.inspect}"
|
||||||
|
puts err.backtrace
|
||||||
|
end
|
||||||
|
socket.on :close do
|
||||||
|
puts "Client closed read connection"
|
||||||
|
end
|
||||||
|
socket.ready
|
||||||
|
socket.write("Hi!")
|
||||||
|
response = socket.read
|
||||||
|
socket.write("You said: #{response}")
|
||||||
|
socket.write("Goodbye!")
|
||||||
|
socket.close
|
||||||
|
rescue Exception => e
|
||||||
|
puts e.inspect
|
||||||
|
puts e.backtrace
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = "landline"
|
spec.name = "landline"
|
||||||
spec.version = "0.12.0"
|
spec.version = "0.12.1"
|
||||||
spec.summary = "Elegant HTTP DSL"
|
spec.summary = "Elegant HTTP DSL"
|
||||||
spec.description = <<~DESC
|
spec.description = <<~DESC
|
||||||
Landline is a no-hard-dependencies HTTP routing DSL that was made entirely for fun.
|
Landline is a no-hard-dependencies HTTP routing DSL that was made entirely for fun.
|
||||||
|
|
|
@ -12,7 +12,7 @@ require_relative 'landline/app'
|
||||||
# Landline is a backend framework born as a by-product of experimentation
|
# Landline is a backend framework born as a by-product of experimentation
|
||||||
module Landline
|
module Landline
|
||||||
# Landline version
|
# Landline version
|
||||||
VERSION = '0.12 "Concrete and Gold" (pre-alpha)'
|
VERSION = '0.12.1 "Concrete and Gold" (pre-alpha)'
|
||||||
|
|
||||||
# Landline branding and version
|
# Landline branding and version
|
||||||
VLINE = "Landline/#{Landline::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n".freeze
|
VLINE = "Landline/#{Landline::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n".freeze
|
||||||
|
|
|
@ -3,18 +3,20 @@
|
||||||
module Landline
|
module Landline
|
||||||
# Rack application interface
|
# Rack application interface
|
||||||
class App
|
class App
|
||||||
# TODO: fix this mess somehow (probably impossible)
|
|
||||||
|
|
||||||
# @!parse include Landline::DSL::PathMethods
|
|
||||||
# @!parse include Landline::DSL::PathConstructors
|
|
||||||
# @!parse include Landline::DSL::ProbeConstructors
|
|
||||||
# @!parse include Landline::DSL::ProbeMethods
|
|
||||||
# @!parse include Landline::DSL::CommonMethods
|
|
||||||
class << self
|
class << self
|
||||||
|
# TODO: fix this mess somehow (probably impossible)
|
||||||
|
# @!parse include Landline::DSL::PathMethods
|
||||||
|
# @!parse include Landline::DSL::PathConstructors
|
||||||
|
# @!parse include Landline::DSL::ProbeConstructors
|
||||||
|
# @!parse include Landline::DSL::ProbeMethods
|
||||||
|
# @!parse include Landline::DSL::CommonMethods
|
||||||
|
|
||||||
# Duplicate used middleware for the subclassed app
|
# Duplicate used middleware for the subclassed app
|
||||||
def inherited(subclass)
|
def inherited(subclass)
|
||||||
super(subclass)
|
super(subclass)
|
||||||
|
@setup_chain ||= []
|
||||||
subclass.middleware = @middleware.dup
|
subclass.middleware = @middleware.dup
|
||||||
|
subclass.setup_chain = @setup_chain.dup
|
||||||
end
|
end
|
||||||
|
|
||||||
# Include a middleware in application
|
# Include a middleware in application
|
||||||
|
@ -24,17 +26,30 @@ module Landline
|
||||||
@middleware.append(middleware)
|
@middleware.append(middleware)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Setup block
|
# Check if Server can respond to given symbol
|
||||||
# @param block [#call]
|
def respond_to_missing?(symbol, _include_private)
|
||||||
def setup(&block)
|
Landline::ServerContext.instance_methods.include?(symbol) || super
|
||||||
@setup_block = block
|
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :middleware, :setup_block
|
# Store applied app manipulations
|
||||||
|
def method_missing(symbol, *args, **params, &callback)
|
||||||
|
if Landline::ServerContext.instance_methods.include? symbol
|
||||||
|
@setup_chain.append([symbol, args, params, callback])
|
||||||
|
else
|
||||||
|
super(symbol, *args, **params, &callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :middleware, :setup_chain
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(*args, **opts)
|
def initialize(*args, **opts)
|
||||||
@app = ::Landline::Server.new(*args, **opts, &self.class.setup_block)
|
setup_chain = self.class.setup_chain
|
||||||
|
@app = ::Landline::Server.new(*args, **opts) do
|
||||||
|
setup_chain.each do |symbol, cargs, cparams, callback|
|
||||||
|
send(symbol, *cargs, **cparams, &callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
self.class.middleware&.reverse_each do |cls|
|
self.class.middleware&.reverse_each do |cls|
|
||||||
@app = cls.new(@app)
|
@app = cls.new(@app)
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,23 +4,9 @@ require_relative 'pattern_matching'
|
||||||
require_relative 'node'
|
require_relative 'node'
|
||||||
require_relative 'dsl/constructors_path'
|
require_relative 'dsl/constructors_path'
|
||||||
require_relative 'dsl/methods_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'
|
require_relative 'util/lookup'
|
||||||
|
|
||||||
module Landline
|
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Execution context for path setup block.
|
# Execution context for path setup block.
|
||||||
class PathContext
|
class PathContext
|
||||||
include Landline::DSL::PathConstructors
|
include Landline::DSL::PathConstructors
|
||||||
|
@ -31,21 +17,8 @@ module Landline
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Ephemeral proxy class to which callback execution binds
|
|
||||||
class PathExecutionOrigin
|
|
||||||
def initialize(request, properties)
|
|
||||||
@request = request
|
|
||||||
@properties = Landline::Util::LookupROProxy.new(properties)
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :response
|
|
||||||
attr_reader :request, :properties
|
|
||||||
end
|
|
||||||
|
|
||||||
# Primary building block of request navigation.
|
# Primary building block of request navigation.
|
||||||
class Path < Landline::Node
|
class Path < Landline::Node
|
||||||
ExecutionOrigin = Landline::PathExecutionOrigin
|
|
||||||
ProcContext = Landline::ProcessorContext
|
|
||||||
Context = Landline::PathContext
|
Context = Landline::PathContext
|
||||||
|
|
||||||
# @param path [Object] Object to generate {Landline::Pattern} from
|
# @param path [Object] Object to generate {Landline::Pattern} from
|
||||||
|
@ -108,8 +81,8 @@ module Landline
|
||||||
|
|
||||||
# Create an execution context for in-path processing blocks
|
# Create an execution context for in-path processing blocks
|
||||||
def get_context(request)
|
def get_context(request)
|
||||||
exec_origin = self.class::ExecutionOrigin.new(request, @properties)
|
request.context.origin.properties.lookup = @properties
|
||||||
self.class::ProcContext.new(exec_origin)
|
request.context
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sequentially run through all filters and drop request if one is false
|
# Sequentially run through all filters and drop request if one is false
|
||||||
|
|
|
@ -24,28 +24,6 @@ module Landline
|
||||||
autoload :Link, "landline/probe/crosscall_handler"
|
autoload :Link, "landline/probe/crosscall_handler"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Context that provides execution context for Probes.
|
|
||||||
class ProbeContext
|
|
||||||
include Landline::DSL::ProbeConstructors
|
|
||||||
include Landline::DSL::ProbeMethods
|
|
||||||
include Landline::DSL::CommonMethods
|
|
||||||
|
|
||||||
def initialize(origin)
|
|
||||||
@origin = origin
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ephemeral proxy class to which callback execution binds
|
|
||||||
class ProbeExecutionOrigin
|
|
||||||
def initialize(request, properties)
|
|
||||||
@request = request
|
|
||||||
@properties = Landline::Util::LookupROProxy.new(properties)
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :response
|
|
||||||
attr_reader :request, :properties
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test probe. Also base for all "reactive" nodes.
|
# Test probe. Also base for all "reactive" nodes.
|
||||||
class Probe < Landline::Node
|
class Probe < Landline::Node
|
||||||
# @param path [Object]
|
# @param path [Object]
|
||||||
|
|
|
@ -29,8 +29,8 @@ module Landline
|
||||||
# @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)
|
||||||
origin = Landline::ProbeExecutionOrigin.new(request, @properties)
|
origin, context = get_context(request)
|
||||||
context = Landline::ProbeContext.new(origin)
|
|
||||||
return reject(request) unless request.path.match?(/^\/?$/)
|
return reject(request) unless request.path.match?(/^\/?$/)
|
||||||
|
|
||||||
response = catch(:break) do
|
response = catch(:break) do
|
||||||
|
@ -47,6 +47,14 @@ module Landline
|
||||||
end
|
end
|
||||||
throw :finish, response
|
throw :finish, response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Create a context to run handler in
|
||||||
|
def get_context(request)
|
||||||
|
request.context.origin.properties.lookup = @properties
|
||||||
|
[request.context.origin, request.context]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require_relative 'util/query'
|
require_relative 'util/query'
|
||||||
require_relative 'util/cookie'
|
require_relative 'util/cookie'
|
||||||
|
require_relative 'sandbox'
|
||||||
|
|
||||||
module Landline
|
module Landline
|
||||||
# Request wrapper for Rack protocol
|
# Request wrapper for Rack protocol
|
||||||
|
@ -30,13 +31,16 @@ module Landline
|
||||||
@states = []
|
@states = []
|
||||||
# Postprocessors for current request
|
# Postprocessors for current request
|
||||||
@postprocessors = []
|
@postprocessors = []
|
||||||
|
# Execution context
|
||||||
|
@context = init_context
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run postprocessors
|
# Run postprocessors
|
||||||
# @param response [Landline::Response]
|
# @param response [Landline::Response]
|
||||||
def run_postprocessors(response)
|
def run_postprocessors(response)
|
||||||
|
@context.origin.properties.lookup = {}
|
||||||
@postprocessors.reverse_each do |postproc|
|
@postprocessors.reverse_each do |postproc|
|
||||||
postproc.call(self, response)
|
@context.instance_exec(self, response, &postproc)
|
||||||
end
|
end
|
||||||
@postprocessors = []
|
@postprocessors = []
|
||||||
end
|
end
|
||||||
|
@ -90,11 +94,17 @@ module Landline
|
||||||
|
|
||||||
attr_reader :request_method, :script_name, :path_info, :server_name,
|
attr_reader :request_method, :script_name, :path_info, :server_name,
|
||||||
:server_port, :server_protocol, :headers, :param, :splat,
|
:server_port, :server_protocol, :headers, :param, :splat,
|
||||||
:postprocessors, :cookies, :rack
|
:postprocessors, :cookies, :rack, :context
|
||||||
attr_accessor :path, :filepath, :query
|
attr_accessor :path, :filepath, :query
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Initialize execution context
|
||||||
|
def init_context
|
||||||
|
origin = Landline::ProcessorOrigin.new(self, {})
|
||||||
|
Landline::ProcessorContext.new(origin)
|
||||||
|
end
|
||||||
|
|
||||||
# Initialize basic rack request parameters
|
# Initialize basic rack request parameters
|
||||||
# @param env [Hash]
|
# @param env [Hash]
|
||||||
def init_request_params(env)
|
def init_request_params(env)
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :origin
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ephemeral proxy class to which callback execution binds
|
||||||
|
class ProcessorOrigin
|
||||||
|
def initialize(request, properties)
|
||||||
|
@request = request
|
||||||
|
@properties = Landline::Util::LookupROProxy.new(properties)
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :response
|
||||||
|
attr_reader :request, :properties
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ module Landline
|
||||||
# @param parent [Landline::Node, nil] Parent object to inherit properties to
|
# @param parent [Landline::Node, nil] Parent object to inherit properties to
|
||||||
# @param setup [#call] Setup block
|
# @param setup [#call] Setup block
|
||||||
def initialize(passthrough = nil, parent: nil, **opts, &setup)
|
def initialize(passthrough = nil, parent: nil, **opts, &setup)
|
||||||
super("", parent: nil, **opts, &setup)
|
super("", parent: parent, **opts, &setup)
|
||||||
return if parent
|
return if parent
|
||||||
|
|
||||||
@passthrough = passthrough
|
@passthrough = passthrough
|
||||||
|
|
|
@ -46,6 +46,8 @@ module Landline
|
||||||
def [](key)
|
def [](key)
|
||||||
@lookup.[](key)
|
@lookup.[](key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_accessor :lookup
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue