landline 0.10.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +71 -19
- data/lib/landline/app.rb +65 -0
- data/lib/landline/dsl/constructors_path.rb +21 -0
- data/lib/landline/dsl/constructors_probe.rb +2 -0
- data/lib/landline/dsl/methods_common.rb +2 -0
- data/lib/landline/dsl/methods_path.rb +25 -5
- data/lib/landline/dsl/methods_probe.rb +129 -11
- data/lib/landline/dsl/methods_template.rb +1 -0
- data/lib/landline/extensions/session.rb +98 -0
- data/lib/landline/extensions/websocket.rb +286 -0
- data/lib/landline/path.rb +40 -29
- data/lib/landline/probe/crosscall_handler.rb +25 -0
- data/lib/landline/probe/handler.rb +19 -15
- data/lib/landline/probe.rb +1 -11
- data/lib/landline/request.rb +68 -7
- data/lib/landline/response.rb +1 -1
- data/lib/landline/sandbox.rb +32 -0
- data/lib/landline/server.rb +43 -18
- data/lib/landline/util/cookie.rb +10 -3
- data/lib/landline/util/jwt.rb +82 -0
- data/lib/landline/util/lookup.rb +16 -0
- data/lib/landline/util/mime.rb +1 -0
- data/lib/landline/util/parseutils.rb +13 -11
- data/lib/landline/util/query.rb +2 -0
- data/lib/landline.rb +4 -2
- metadata +10 -6
- data/LAYOUT.md +0 -86
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Landline
|
6
|
+
# Module for controlling session signing secrets
|
7
|
+
module Session
|
8
|
+
# Set hmac secret
|
9
|
+
# @param secret [String]
|
10
|
+
def self.hmac_secret=(secret)
|
11
|
+
@hmac_secret = secret
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get hmac secret
|
15
|
+
def self.hmac_secret
|
16
|
+
unless @hmac_secret or ENV['HMAC_SECRET']
|
17
|
+
warn <<~MSG
|
18
|
+
warn: hmac secret not supplied, using randomized one
|
19
|
+
warn: provide hmac secret with $HMAC_SECRET or Landline::Session.hmac_secret
|
20
|
+
MSG
|
21
|
+
end
|
22
|
+
@hmac_secret ||= ENV.fetch('HMAC_SECRET', SecureRandom.base64(80))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Class for representing session errors
|
26
|
+
class SessionError < ::StandardError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Class for representing session storage
|
30
|
+
class Session
|
31
|
+
def initialize(cookie, cookies_callback)
|
32
|
+
@data = if cookie
|
33
|
+
Landline::Util::JWT.from_string(
|
34
|
+
cookie,
|
35
|
+
Landline::Session.hmac_secret
|
36
|
+
)
|
37
|
+
else
|
38
|
+
Landline::Util::JWT.new({})
|
39
|
+
end
|
40
|
+
@valid = !@data.nil?
|
41
|
+
@cookies_callback = cookies_callback
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieve data from session storage
|
45
|
+
# @param key [String, Symbol] serializable key
|
46
|
+
def [](key)
|
47
|
+
raise Landline::Session::SessionError, "session not valid" unless @valid
|
48
|
+
|
49
|
+
unless key.is_a? String or key.is_a? Symbol
|
50
|
+
raise StandardError, "key not serializable"
|
51
|
+
end
|
52
|
+
|
53
|
+
@data.data[key]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set data to session storage
|
57
|
+
# @param key [String, Symbol] serializable key
|
58
|
+
# @param value [Object] serializable data
|
59
|
+
def []=(key, value)
|
60
|
+
raise Landline::Session::SessionError, "session not valid" unless @valid
|
61
|
+
|
62
|
+
unless key.is_a? String or key.is_a? Symbol
|
63
|
+
raise StandardError, "key not serializable"
|
64
|
+
end
|
65
|
+
|
66
|
+
@data.data[key] = value
|
67
|
+
@cookies_callback.call(@data.make(Landline::Session.hmac_secret))
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_reader :valid
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module Landline
|
76
|
+
module DSL
|
77
|
+
module ProbeMethods
|
78
|
+
# (in Landline::Probe context)
|
79
|
+
# Return session storage hash
|
80
|
+
# @return [Landline::Session::Session]
|
81
|
+
def session
|
82
|
+
return @session if @session
|
83
|
+
|
84
|
+
@session = Landline::Session::Session.new(
|
85
|
+
request.cookies.dig('session', 0)&.value,
|
86
|
+
proc do |value|
|
87
|
+
delete_cookie("session", value)
|
88
|
+
cookie("session", value)
|
89
|
+
end
|
90
|
+
)
|
91
|
+
request.postprocessors.append(proc do
|
92
|
+
@session = nil
|
93
|
+
end)
|
94
|
+
@session
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'websocket'
|
4
|
+
module Landline
|
5
|
+
# Module that holds websocket primitives
|
6
|
+
module WebSocket
|
7
|
+
# Event system
|
8
|
+
module Eventifier
|
9
|
+
# Attach event listener
|
10
|
+
# @param event [Symbol]
|
11
|
+
# @param listener [#call]
|
12
|
+
def on(event, &listener)
|
13
|
+
@__listeners ||= {}
|
14
|
+
@__listeners[event] ||= []
|
15
|
+
@__listeners[event].append(listener)
|
16
|
+
listener
|
17
|
+
end
|
18
|
+
|
19
|
+
# Attach event listener
|
20
|
+
# @param event [Symbol]
|
21
|
+
# @param listener [#call]
|
22
|
+
def off(event, listener)
|
23
|
+
@__listeners ||= {}
|
24
|
+
@__listeners[event]&.delete(listener)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Await for an event
|
28
|
+
# @param event [Symbol, Array<Symbol>] event or array of events to wait for
|
29
|
+
# @return [Array]
|
30
|
+
# @sg-ignore
|
31
|
+
def await(event)
|
32
|
+
blocking = true
|
33
|
+
output = nil
|
34
|
+
listener = proc do |*data|
|
35
|
+
output = data
|
36
|
+
blocking = false
|
37
|
+
end
|
38
|
+
if event.is_a? Array
|
39
|
+
event.each { |x| on(x, &listener) }
|
40
|
+
else
|
41
|
+
on(event, &listener)
|
42
|
+
end
|
43
|
+
while blocking; end
|
44
|
+
return output[0] if output.is_a? Array and output.length == 1
|
45
|
+
|
46
|
+
output
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Trigger the queue clearing process
|
52
|
+
# @return [void]
|
53
|
+
def _process
|
54
|
+
return if @processing
|
55
|
+
|
56
|
+
@__processing = true
|
57
|
+
@__queue ||= []
|
58
|
+
@__listeners ||= {}
|
59
|
+
until @__queue.empty?
|
60
|
+
event, msg = @__queue.shift
|
61
|
+
if @__listeners.include? event
|
62
|
+
@__listeners[event].each { |x| x.call(*msg) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@processing = false
|
66
|
+
end
|
67
|
+
|
68
|
+
# Send internal event
|
69
|
+
# @param event [Symbol]
|
70
|
+
# @param data [Array]
|
71
|
+
# @return [void]
|
72
|
+
def _emit(event, *data)
|
73
|
+
return unless @__listeners
|
74
|
+
|
75
|
+
return unless @__listeners.include? event
|
76
|
+
|
77
|
+
@__queue ||= []
|
78
|
+
@__queue.push([event, data])
|
79
|
+
_process
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Socket-like object representing websocket interface
|
84
|
+
class WSockWrapper
|
85
|
+
include Eventifier
|
86
|
+
|
87
|
+
class WebSocketError < StandardError
|
88
|
+
end
|
89
|
+
|
90
|
+
def initialize(io, version: 7)
|
91
|
+
@io = io
|
92
|
+
@version = version
|
93
|
+
@frame_parser = ::WebSocket::Frame::Incoming::Server.new(
|
94
|
+
version: version
|
95
|
+
)
|
96
|
+
@readable = true
|
97
|
+
@writable = true
|
98
|
+
@data = Queue.new
|
99
|
+
on :message do |msg|
|
100
|
+
@data.enq(msg)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Start the main loop for the eventifier
|
105
|
+
# @return [void]
|
106
|
+
def ready
|
107
|
+
return if @ready
|
108
|
+
|
109
|
+
_loop
|
110
|
+
@ready = true
|
111
|
+
end
|
112
|
+
|
113
|
+
# Send data through websocket
|
114
|
+
# @param data [String] binary data
|
115
|
+
# @return [void]
|
116
|
+
def write(data, type: :text)
|
117
|
+
unless @writable
|
118
|
+
raise self.class::WebSocketError,
|
119
|
+
"socket closed for writing"
|
120
|
+
end
|
121
|
+
|
122
|
+
frame = ::WebSocket::Frame::Outgoing::Server.new(
|
123
|
+
version: @version,
|
124
|
+
data: data,
|
125
|
+
type: type
|
126
|
+
)
|
127
|
+
@io.write(frame.to_s)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Read data from socket synchronously
|
131
|
+
# @return [String, nil] nil returned if socket closes
|
132
|
+
def read
|
133
|
+
unless @readable
|
134
|
+
raise self.class::WebSocketError,
|
135
|
+
"socket closed for reading"
|
136
|
+
end
|
137
|
+
|
138
|
+
@data.deq
|
139
|
+
end
|
140
|
+
|
141
|
+
# Close the socket for reading
|
142
|
+
# @return [void]
|
143
|
+
def close_read
|
144
|
+
_emit :close
|
145
|
+
@readable = false
|
146
|
+
@io.close_read
|
147
|
+
end
|
148
|
+
|
149
|
+
# Close the socket for writing
|
150
|
+
def close_write
|
151
|
+
@writable = false
|
152
|
+
@io.close_write
|
153
|
+
end
|
154
|
+
|
155
|
+
# Establish a connection through handshake
|
156
|
+
# @return [self]
|
157
|
+
def self.handshake(request, version: 7, **opts)
|
158
|
+
raise StandardError, "stream cannot be hijacked" unless request.hijack
|
159
|
+
|
160
|
+
handshake = create_handshake(request, version: version, **opts)
|
161
|
+
return nil unless handshake
|
162
|
+
|
163
|
+
io = request.hijack.call
|
164
|
+
io.sendmsg(handshake.to_s)
|
165
|
+
new(io, version: version)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Initiate a handshake
|
169
|
+
def self.create_handshake(request, **opts)
|
170
|
+
handshake = ::WebSocket::Handshake::Server.new(**opts)
|
171
|
+
handshake.from_hash({
|
172
|
+
headers: request.headers,
|
173
|
+
path: request.path_info,
|
174
|
+
query: request.query.query,
|
175
|
+
body: request.body
|
176
|
+
})
|
177
|
+
return nil unless handshake.finished? and handshake.valid?
|
178
|
+
|
179
|
+
handshake
|
180
|
+
end
|
181
|
+
|
182
|
+
# Close the socket
|
183
|
+
# @return [void]
|
184
|
+
def close
|
185
|
+
_close
|
186
|
+
@writable = false
|
187
|
+
@readable = false
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
# Event reading loop
|
193
|
+
# @return [void]
|
194
|
+
def _loop
|
195
|
+
@thread = Thread.new do
|
196
|
+
loop do
|
197
|
+
msg = _read
|
198
|
+
if msg and [:text, :binary].include? msg.type
|
199
|
+
_emit :message, msg
|
200
|
+
elsif msg and msg.type == :close
|
201
|
+
_emit :__close, msg
|
202
|
+
break
|
203
|
+
end
|
204
|
+
end
|
205
|
+
rescue IOError => e
|
206
|
+
@writable = false
|
207
|
+
_emit :error, e
|
208
|
+
close
|
209
|
+
ensure
|
210
|
+
close_read
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Receive data through websocket
|
215
|
+
# @return [String] output from frame
|
216
|
+
def _read
|
217
|
+
while (char = @io.getc)
|
218
|
+
@frame_parser << char
|
219
|
+
frame = @frame_parser.next
|
220
|
+
return frame if frame
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Close the websocket
|
225
|
+
# @return [void]
|
226
|
+
def _close
|
227
|
+
frame = ::WebSocket::Frame::Outgoing::Server.new(
|
228
|
+
version: @version,
|
229
|
+
type: :close
|
230
|
+
)
|
231
|
+
@io.write(frame.to_s) if @writable
|
232
|
+
sleep 0.1
|
233
|
+
@io.close
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
module Landline
|
240
|
+
module Handlers
|
241
|
+
# Web socket server handler
|
242
|
+
class WebSockServer < Landline::Probe
|
243
|
+
# @param path [Object]
|
244
|
+
# @param parent [Landline::Node]
|
245
|
+
# @param params [Hash] options hash
|
246
|
+
# @param callback [#call] callback to process request within
|
247
|
+
# @option params [Integer] :version protocol version
|
248
|
+
# @option params [Array<String>] :protocols array of supported sub-protocols
|
249
|
+
# @option params [Boolean] :secure true if the server will use wss:// protocol
|
250
|
+
def initialize(path, parent:, **params, &callback)
|
251
|
+
nodeparams = params.dup
|
252
|
+
nodeparams.delete(:version)
|
253
|
+
nodeparams.delete(:protocols)
|
254
|
+
nodeparams.delete(:secure)
|
255
|
+
super(path, parent: parent, **nodeparams)
|
256
|
+
@callback = callback
|
257
|
+
@params = params
|
258
|
+
end
|
259
|
+
|
260
|
+
# Method callback on successful request navigation
|
261
|
+
# Creates a websocket from a given request
|
262
|
+
# @param request [Landline::Request]
|
263
|
+
def process(request)
|
264
|
+
@callback.call(
|
265
|
+
Landline::WebSocket::WSockWrapper.handshake(
|
266
|
+
request,
|
267
|
+
**@params
|
268
|
+
)
|
269
|
+
)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
module DSL
|
275
|
+
module PathConstructors
|
276
|
+
# (in Landline::Path context)
|
277
|
+
# Create a new websocket handler
|
278
|
+
def websocket(path, **args, &setup)
|
279
|
+
register(Landline::Handlers::WebSockServer.new(path,
|
280
|
+
parent: @origin,
|
281
|
+
**args,
|
282
|
+
&setup))
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
data/lib/landline/path.rb
CHANGED
@@ -4,23 +4,9 @@ require_relative 'pattern_matching'
|
|
4
4
|
require_relative 'node'
|
5
5
|
require_relative 'dsl/constructors_path'
|
6
6
|
require_relative 'dsl/methods_path'
|
7
|
-
require_relative 'dsl/methods_common'
|
8
|
-
require_relative 'dsl/methods_probe'
|
9
|
-
require_relative 'dsl/constructors_probe'
|
10
7
|
require_relative 'util/lookup'
|
11
8
|
|
12
9
|
module Landline
|
13
|
-
# Execution context for filters and preprocessors.
|
14
|
-
class ProcessorContext
|
15
|
-
include Landline::DSL::CommonMethods
|
16
|
-
include Landline::DSL::ProbeMethods
|
17
|
-
include Landline::DSL::ProbeConstructors
|
18
|
-
|
19
|
-
def initialize(path)
|
20
|
-
@origin = path
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
10
|
# Execution context for path setup block.
|
25
11
|
class PathContext
|
26
12
|
include Landline::DSL::PathConstructors
|
@@ -33,7 +19,6 @@ module Landline
|
|
33
19
|
|
34
20
|
# Primary building block of request navigation.
|
35
21
|
class Path < Landline::Node
|
36
|
-
ProcContext = Landline::ProcessorContext
|
37
22
|
Context = Landline::PathContext
|
38
23
|
|
39
24
|
# @param path [Object] Object to generate {Landline::Pattern} from
|
@@ -50,7 +35,6 @@ module Landline
|
|
50
35
|
# Contexts setup
|
51
36
|
context = self.class::Context.new(self)
|
52
37
|
context.instance_exec(&setup)
|
53
|
-
@proccontext = self.class::ProcContext.new(self)
|
54
38
|
end
|
55
39
|
|
56
40
|
# Method callback on successful request navigation.
|
@@ -89,18 +73,28 @@ module Landline
|
|
89
73
|
@filters.append(block)
|
90
74
|
end
|
91
75
|
|
92
|
-
attr_reader :children, :properties
|
76
|
+
attr_reader :children, :properties
|
93
77
|
|
94
78
|
attr_accessor :bounce, :pipeline
|
95
79
|
|
96
80
|
private
|
97
81
|
|
82
|
+
# Create an execution context for in-path processing blocks
|
83
|
+
def get_context(request)
|
84
|
+
request.context.origin.properties.lookup = @properties
|
85
|
+
request.context
|
86
|
+
end
|
87
|
+
|
98
88
|
# Sequentially run through all filters and drop request if one is false
|
99
89
|
# @param request [Landline::Request]
|
100
90
|
# @return [Boolean] true if request passed all filters
|
101
91
|
def run_filters(request)
|
92
|
+
proccontext = get_context(request)
|
102
93
|
@filters.each do |filter|
|
103
|
-
|
94
|
+
output = catch(:break) do
|
95
|
+
proccontext.instance_exec(request, &filter)
|
96
|
+
end
|
97
|
+
return false unless output
|
104
98
|
end
|
105
99
|
true
|
106
100
|
end
|
@@ -108,9 +102,15 @@ module Landline
|
|
108
102
|
# Sequentially run all preprocessors on a request
|
109
103
|
# @param request [Landline::Request]
|
110
104
|
def run_preprocessors(request)
|
105
|
+
proccontext = get_context(request)
|
111
106
|
@preprocessors.each do |preproc|
|
112
|
-
|
107
|
+
output = catch(:break) do
|
108
|
+
proccontext.instance_exec(request, &preproc)
|
109
|
+
true
|
110
|
+
end
|
111
|
+
return false unless output
|
113
112
|
end
|
113
|
+
true
|
114
114
|
end
|
115
115
|
|
116
116
|
# Append postprocessors to request
|
@@ -125,23 +125,33 @@ module Landline
|
|
125
125
|
# @return [Boolean] true if further navigation will be done
|
126
126
|
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
127
127
|
def process_wrapped(request)
|
128
|
-
@request = request
|
129
128
|
return false unless run_filters(request)
|
130
129
|
|
131
|
-
run_preprocessors(request)
|
130
|
+
return false unless run_preprocessors(request)
|
131
|
+
|
132
132
|
enqueue_postprocessors(request)
|
133
133
|
@children.each do |x|
|
134
134
|
value = x.go(request)
|
135
|
-
return value if value
|
135
|
+
return exit_stack(request, value) if value
|
136
136
|
end
|
137
137
|
value = index(request)
|
138
|
-
return value if value
|
138
|
+
return exit_stack(request, value) if value
|
139
139
|
|
140
|
-
|
140
|
+
notfound(request)
|
141
141
|
rescue StandardError => e
|
142
|
-
_die(500, backtrace: [e.to_s] + e.backtrace)
|
143
|
-
|
144
|
-
|
142
|
+
_die(request, 500, backtrace: [e.to_s] + e.backtrace)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Run enqueued postprocessors on navigation failure
|
146
|
+
# @param request [Landline::Request]
|
147
|
+
def exit_stack(request, response = nil)
|
148
|
+
request.run_postprocessors(response)
|
149
|
+
response
|
150
|
+
end
|
151
|
+
|
152
|
+
# Exit with failure or throw 404
|
153
|
+
def notfound(request)
|
154
|
+
@bounce ? exit_stack(request) : _die(request, 404)
|
145
155
|
end
|
146
156
|
|
147
157
|
# Try to perform indexing on the path if possible
|
@@ -165,9 +175,10 @@ module Landline
|
|
165
175
|
# @param errorcode [Integer]
|
166
176
|
# @param backtrace [Array(String), nil]
|
167
177
|
# @raise [UncaughtThrowError] throws :finish to stop processing
|
168
|
-
def _die(errorcode, backtrace: nil)
|
178
|
+
def _die(request, errorcode, backtrace: nil)
|
179
|
+
proccontext = get_context(request)
|
169
180
|
throw :finish, [errorcode].append(
|
170
|
-
|
181
|
+
*proccontext.instance_exec(
|
171
182
|
errorcode,
|
172
183
|
backtrace: backtrace,
|
173
184
|
&(@properties["handle.#{errorcode}"] or
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../probe"
|
4
|
+
|
5
|
+
module Landline
|
6
|
+
module Handlers
|
7
|
+
# Probe that sends files from a location
|
8
|
+
class Link < Landline::Probe
|
9
|
+
# @param path [Object]
|
10
|
+
# @param parent [Landline::Node]
|
11
|
+
def initialize(path, application, parent:)
|
12
|
+
@application = application
|
13
|
+
super(path, parent: parent, filepath: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Method callback on successful request navigation.
|
17
|
+
# Sends request over to another rack app, stripping the part of the path that was not navigated
|
18
|
+
# @param request [Landline::Request]
|
19
|
+
# @return [Array(Integer, Host{String => Object}, Object)]
|
20
|
+
def process(request)
|
21
|
+
throw :finish, @application.call(request.env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -12,13 +12,8 @@ module Landline
|
|
12
12
|
def initialize(path, **args, &exec)
|
13
13
|
super(path, **args)
|
14
14
|
@callback = exec
|
15
|
-
@context = Landline::ProbeContext.new(self)
|
16
|
-
@response = nil
|
17
15
|
end
|
18
16
|
|
19
|
-
attr_accessor :response
|
20
|
-
attr_reader :request
|
21
|
-
|
22
17
|
# Method callback on successful request navigation.
|
23
18
|
# Runs block supplied with object initialization.
|
24
19
|
# Request's #splat and #param are passed to block.
|
@@ -34,23 +29,32 @@ module Landline
|
|
34
29
|
# @return [Boolean] true if further navigation is possible
|
35
30
|
# @raise [UncaughtThrowError] may raise if die() is called.
|
36
31
|
def process(request)
|
37
|
-
|
32
|
+
origin, context = get_context(request)
|
33
|
+
|
38
34
|
return reject(request) unless request.path.match?(/^\/?$/)
|
39
|
-
|
40
|
-
@request = request
|
35
|
+
|
41
36
|
response = catch(:break) do
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
context.instance_exec(*request.splat,
|
38
|
+
**request.param,
|
39
|
+
&@callback)
|
45
40
|
end
|
46
41
|
return false unless response
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
|
43
|
+
oresponse = origin.response
|
44
|
+
if oresponse and [String, File, IO].include? response.class
|
45
|
+
oresponse.body = response
|
46
|
+
throw :finish, oresponse
|
51
47
|
end
|
52
48
|
throw :finish, response
|
53
49
|
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Create a context to run handler in
|
54
|
+
def get_context(request)
|
55
|
+
request.context.origin.properties.lookup = @properties
|
56
|
+
[request.context.origin, request.context]
|
57
|
+
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
data/lib/landline/probe.rb
CHANGED
@@ -21,17 +21,7 @@ module Landline
|
|
21
21
|
autoload :TRACE, "landline/probe/http_method"
|
22
22
|
autoload :PATCH, "landline/probe/http_method"
|
23
23
|
autoload :Serve, "landline/probe/serve_handler"
|
24
|
-
|
25
|
-
|
26
|
-
# Context that provides execution context for Probes.
|
27
|
-
class ProbeContext
|
28
|
-
include Landline::DSL::ProbeConstructors
|
29
|
-
include Landline::DSL::ProbeMethods
|
30
|
-
include Landline::DSL::CommonMethods
|
31
|
-
|
32
|
-
def initialize(origin)
|
33
|
-
@origin = origin
|
34
|
-
end
|
24
|
+
autoload :Link, "landline/probe/crosscall_handler"
|
35
25
|
end
|
36
26
|
|
37
27
|
# Test probe. Also base for all "reactive" nodes.
|