sanford 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/bench/report.rb +2 -0
- data/bench/report.txt +28 -37
- data/lib/sanford/connection_handler.rb +10 -3
- data/lib/sanford/error_handler.rb +4 -4
- data/lib/sanford/io_pipe.rb +40 -0
- data/lib/sanford/process.rb +108 -28
- data/lib/sanford/router.rb +5 -0
- data/lib/sanford/server.rb +133 -95
- data/lib/sanford/server_data.rb +27 -28
- data/lib/sanford/template_source.rb +2 -2
- data/lib/sanford/version.rb +1 -1
- data/sanford.gemspec +4 -5
- data/test/helper.rb +11 -0
- data/test/support/app_server.rb +6 -3
- data/test/support/factory.rb +7 -0
- data/test/system/server_tests.rb +21 -3
- data/test/system/service_handler_tests.rb +4 -4
- data/test/unit/cli_tests.rb +2 -2
- data/test/unit/connection_handler_tests.rb +107 -47
- data/test/unit/error_handler_tests.rb +11 -11
- data/test/unit/io_pipe_tests.rb +84 -0
- data/test/unit/process_tests.rb +266 -131
- data/test/unit/router_tests.rb +25 -7
- data/test/unit/server_data_tests.rb +68 -46
- data/test/unit/server_tests.rb +197 -240
- data/test/unit/template_engine_tests.rb +1 -1
- data/test/unit/template_source_tests.rb +6 -0
- data/test/unit/test_runner_tests.rb +0 -19
- metadata +12 -19
data/lib/sanford/server_data.rb
CHANGED
@@ -2,20 +2,17 @@ module Sanford
|
|
2
2
|
|
3
3
|
class ServerData
|
4
4
|
|
5
|
-
# The server uses this to "compile"
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
attr_reader :name
|
11
|
-
attr_reader :pid_file
|
12
|
-
attr_reader :receives_keep_alive
|
13
|
-
attr_reader :worker_class, :worker_params, :num_workers
|
14
|
-
attr_reader :debug, :logger, :dtcp_logger, :verbose_logging
|
15
|
-
attr_reader :template_source, :shutdown_timeout
|
16
|
-
attr_reader :init_procs, :error_procs
|
17
|
-
attr_reader :router, :routes
|
5
|
+
# The server uses this to "compile" the common configuration data used
|
6
|
+
# by the server instances, error handlers and routes. The goal here is
|
7
|
+
# to provide these with a simplified interface with the minimal data needed
|
8
|
+
# and to decouple the configuration from each thing that needs its data.
|
9
|
+
|
18
10
|
attr_accessor :ip, :port
|
11
|
+
attr_reader :name, :pid_file, :shutdown_timeout
|
12
|
+
attr_reader :worker_class, :worker_params, :num_workers
|
13
|
+
attr_reader :error_procs, :template_source, :logger, :router
|
14
|
+
attr_reader :receives_keep_alive, :verbose_logging
|
15
|
+
attr_reader :debug, :dtcp_logger, :routes, :process_label
|
19
16
|
|
20
17
|
def initialize(args = nil)
|
21
18
|
args ||= {}
|
@@ -24,26 +21,28 @@ module Sanford
|
|
24
21
|
@port = !(v = ENV['SANFORD_PORT'].to_s).empty? ? v.to_i : args[:port]
|
25
22
|
@pid_file = args[:pid_file]
|
26
23
|
|
27
|
-
@
|
28
|
-
|
29
|
-
@worker_class = args[:worker_class]
|
30
|
-
@worker_params = args[:worker_params] || {}
|
31
|
-
@num_workers = args[:num_workers]
|
32
|
-
|
33
|
-
@debug = !ENV['SANFORD_DEBUG'].to_s.empty?
|
34
|
-
@logger = args[:logger]
|
35
|
-
@dtcp_logger = @logger if @debug
|
36
|
-
@verbose_logging = !!args[:verbose_logging]
|
24
|
+
@shutdown_timeout = args[:shutdown_timeout]
|
37
25
|
|
26
|
+
@worker_class = args[:worker_class]
|
27
|
+
@worker_params = args[:worker_params] || {}
|
28
|
+
@num_workers = args[:num_workers]
|
29
|
+
@error_procs = args[:error_procs] || []
|
38
30
|
@template_source = args[:template_source]
|
31
|
+
@logger = args[:logger]
|
32
|
+
@router = args[:router]
|
39
33
|
|
40
|
-
@
|
34
|
+
@receives_keep_alive = !!args[:receives_keep_alive]
|
35
|
+
@verbose_logging = !!args[:verbose_logging]
|
41
36
|
|
42
|
-
@
|
43
|
-
@
|
37
|
+
@debug = !ENV['SANFORD_DEBUG'].to_s.empty?
|
38
|
+
@dtcp_logger = @logger if @debug
|
39
|
+
@routes = build_routes(args[:routes] || [])
|
44
40
|
|
45
|
-
@
|
46
|
-
|
41
|
+
@process_label = if (label = ENV['SANFORD_PROCESS_LABEL'].to_s).empty?
|
42
|
+
"#{@name}-#{@ip}-#{@port}"
|
43
|
+
else
|
44
|
+
label
|
45
|
+
end
|
47
46
|
end
|
48
47
|
|
49
48
|
def route_for(name)
|
data/lib/sanford/version.rb
CHANGED
data/sanford.gemspec
CHANGED
@@ -18,10 +18,9 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_development_dependency("assert", ["~> 2.
|
21
|
+
gem.add_development_dependency("assert", ["~> 2.16.1"])
|
22
22
|
|
23
|
-
gem.add_dependency("dat-tcp", ["~> 0.8.
|
24
|
-
gem.add_dependency("much-plugin", ["~> 0.
|
25
|
-
gem.add_dependency("
|
26
|
-
gem.add_dependency("sanford-protocol", ["~> 0.11.0"])
|
23
|
+
gem.add_dependency("dat-tcp", ["~> 0.8.1"])
|
24
|
+
gem.add_dependency("much-plugin", ["~> 0.2.0"])
|
25
|
+
gem.add_dependency("sanford-protocol", ["~> 0.12.0"])
|
27
26
|
end
|
data/test/helper.rb
CHANGED
@@ -12,3 +12,14 @@ require 'pathname'
|
|
12
12
|
ROOT_PATH = Pathname.new(File.expand_path('../..', __FILE__))
|
13
13
|
|
14
14
|
require 'test/support/factory'
|
15
|
+
|
16
|
+
JOIN_SECONDS = 0.1
|
17
|
+
|
18
|
+
# 1.8.7 backfills
|
19
|
+
|
20
|
+
# Array#sample
|
21
|
+
if !(a = Array.new).respond_to?(:sample) && a.respond_to?(:choice)
|
22
|
+
class Array
|
23
|
+
alias_method :sample, :choice
|
24
|
+
end
|
25
|
+
end
|
data/test/support/app_server.rb
CHANGED
@@ -10,13 +10,16 @@ LOGGER = Logger.new(ROOT_PATH.join('log/app_server.log').to_s)
|
|
10
10
|
LOGGER.datetime_format = "" # turn off the datetime in the logs
|
11
11
|
|
12
12
|
class AppERBEngine < Sanford::TemplateEngine
|
13
|
-
RenderScope = Struct.new(:view)
|
13
|
+
RenderScope = Class.new(Struct.new(:view)) do
|
14
|
+
def get_binding; binding; end
|
15
|
+
end
|
14
16
|
|
15
17
|
def render(path, service_handler, locals)
|
16
18
|
require 'erb'
|
17
19
|
full_path = ROOT_PATH.join("test/support/#{path}.erb")
|
18
|
-
|
19
|
-
|
20
|
+
|
21
|
+
b = RenderScope.new(service_handler).get_binding
|
22
|
+
ERB.new(File.read(full_path)).result(b)
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
data/test/support/factory.rb
CHANGED
data/test/system/server_tests.rb
CHANGED
@@ -308,9 +308,9 @@ module Sanford::Server
|
|
308
308
|
should "return a response generated by the error handler" do
|
309
309
|
assert_equal 200, subject.code
|
310
310
|
assert_nil subject.status.message
|
311
|
-
|
312
|
-
|
313
|
-
assert_equal
|
311
|
+
exp = "The server on #{AppServer.ip}:#{AppServer.port} " \
|
312
|
+
"threw a StandardError."
|
313
|
+
assert_equal exp, subject.data
|
314
314
|
end
|
315
315
|
|
316
316
|
end
|
@@ -346,6 +346,24 @@ module Sanford::Server
|
|
346
346
|
|
347
347
|
end
|
348
348
|
|
349
|
+
class WithEnvProcessLabelTests < SystemTests
|
350
|
+
desc "with a process label env var"
|
351
|
+
setup do
|
352
|
+
ENV['SANFORD_PROCESS_LABEL'] = Factory.string
|
353
|
+
|
354
|
+
@server = AppServer.new
|
355
|
+
end
|
356
|
+
teardown do
|
357
|
+
ENV.delete('SANFORD_PROCESS_LABEL')
|
358
|
+
end
|
359
|
+
subject{ @server }
|
360
|
+
|
361
|
+
should "set the daemons process label to the env var" do
|
362
|
+
assert_equal ENV['SANFORD_PROCESS_LABEL'], subject.process_label
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
349
367
|
class TestClient
|
350
368
|
attr_accessor :delay, :bytes
|
351
369
|
|
@@ -62,8 +62,8 @@ module Sanford::ServiceHandler
|
|
62
62
|
setup do
|
63
63
|
@params = { 'message' => Factory.text }
|
64
64
|
@runner = test_runner(AppHandlers::Template, {
|
65
|
-
:params
|
66
|
-
:template_source => AppServer.
|
65
|
+
:params => @params,
|
66
|
+
:template_source => AppServer.config.template_source
|
67
67
|
})
|
68
68
|
@handler = @runner.handler
|
69
69
|
end
|
@@ -78,8 +78,8 @@ module Sanford::ServiceHandler
|
|
78
78
|
should "return a 200 response and render the template when run" do
|
79
79
|
response = @runner.run
|
80
80
|
assert_equal 200, response.code
|
81
|
-
|
82
|
-
assert_equal
|
81
|
+
exp = "ERB Template Message: #{@params['message']}\n"
|
82
|
+
assert_equal exp, response.data
|
83
83
|
end
|
84
84
|
|
85
85
|
end
|
data/test/unit/cli_tests.rb
CHANGED
@@ -129,8 +129,8 @@ class Sanford::CLI
|
|
129
129
|
end
|
130
130
|
|
131
131
|
should "output the error with the help" do
|
132
|
-
|
133
|
-
assert_includes
|
132
|
+
exp = "#{@command.inspect} is not a valid command"
|
133
|
+
assert_includes exp, @kernel_spy.output
|
134
134
|
assert_includes "Usage: sanford", @kernel_spy.output
|
135
135
|
end
|
136
136
|
|
@@ -84,59 +84,99 @@ class Sanford::ConnectionHandler
|
|
84
84
|
|
85
85
|
end
|
86
86
|
|
87
|
-
class
|
88
|
-
desc "and run with a route that throws an exception"
|
87
|
+
class RunWithExceptionSetupTests < InitTests
|
89
88
|
setup do
|
90
|
-
|
89
|
+
@route_exception = Factory.exception
|
90
|
+
Assert.stub(@route, :run){ raise @route_exception }
|
91
|
+
Assert.stub(Sanford::ErrorHandler, :new) do |*args|
|
92
|
+
@error_handler_spy = ErrorHandlerSpy.new(*args)
|
93
|
+
end
|
94
|
+
end
|
91
95
|
|
92
|
-
|
93
|
-
:server_data => @server_data,
|
94
|
-
:request => @request
|
95
|
-
})
|
96
|
-
@expected_response = error_handler.run
|
97
|
-
@expected_exception = error_handler.exception
|
96
|
+
end
|
98
97
|
|
98
|
+
class RunWithExceptionTests < RunWithExceptionSetupTests
|
99
|
+
desc "and run with an exception"
|
100
|
+
setup do
|
99
101
|
@processed_service = @connection_handler.run
|
100
102
|
end
|
101
|
-
subject{ @processed_service }
|
102
103
|
|
103
|
-
should "
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
should "run an error handler" do
|
105
|
+
assert_equal @route_exception, @error_handler_spy.passed_exception
|
106
|
+
exp = {
|
107
|
+
:server_data => @server_data,
|
108
|
+
:request => @processed_service.request,
|
109
|
+
:handler_class => @processed_service.handler_class,
|
110
|
+
:response => nil
|
111
|
+
}
|
112
|
+
assert_equal exp, @error_handler_spy.context_hash
|
113
|
+
assert_true @error_handler_spy.run_called
|
107
114
|
end
|
108
115
|
|
109
|
-
should "
|
110
|
-
assert_equal @
|
116
|
+
should "store the error handler response and exception on the processed service" do
|
117
|
+
assert_equal @error_handler_spy.response, @processed_service.response
|
118
|
+
assert_equal @error_handler_spy.exception, @processed_service.exception
|
119
|
+
end
|
120
|
+
|
121
|
+
should "write the error response to the connection" do
|
122
|
+
assert_equal @error_handler_spy.response, @connection.response
|
111
123
|
assert_true @connection.write_closed
|
112
124
|
end
|
113
125
|
|
114
126
|
end
|
115
127
|
|
116
|
-
class
|
128
|
+
class RunWithShutdownErrorTests < RunWithExceptionSetupTests
|
129
|
+
desc "and run with a dat worker pool shutdown error"
|
130
|
+
setup do
|
131
|
+
@shutdown_error = DatWorkerPool::ShutdownError.new(Factory.text)
|
132
|
+
Assert.stub(@route, :run){ raise @shutdown_error }
|
133
|
+
end
|
134
|
+
|
135
|
+
should "run an error handler" do
|
136
|
+
assert_raises{ @connection_handler.run }
|
137
|
+
|
138
|
+
passed_exception = @error_handler_spy.passed_exception
|
139
|
+
assert_instance_of Sanford::ShutdownError, passed_exception
|
140
|
+
assert_equal @shutdown_error.message, passed_exception.message
|
141
|
+
assert_equal @shutdown_error.backtrace, passed_exception.backtrace
|
142
|
+
assert_true @error_handler_spy.run_called
|
143
|
+
end
|
144
|
+
|
145
|
+
should "raise the shutdown error" do
|
146
|
+
assert_raises(@shutdown_error.class){ @connection_handler.run }
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
class RunWithExceptionWhileWritingTests < RunWithExceptionSetupTests
|
117
152
|
desc "and run with an exception thrown while writing the response"
|
118
153
|
setup do
|
154
|
+
Assert.stub(@route, :run){ @response }
|
119
155
|
@connection.raise_on_write = true
|
120
156
|
|
121
|
-
error_handler = Sanford::ErrorHandler.new(@connection.write_exception, {
|
122
|
-
:server_data => @server_data,
|
123
|
-
:request => @request
|
124
|
-
})
|
125
|
-
@expected_response = error_handler.run
|
126
|
-
@expected_exception = error_handler.exception
|
127
|
-
|
128
157
|
@processed_service = @connection_handler.run
|
129
158
|
end
|
130
159
|
subject{ @processed_service }
|
131
160
|
|
132
|
-
should "
|
133
|
-
|
134
|
-
|
135
|
-
|
161
|
+
should "run an error handler" do
|
162
|
+
assert_equal @connection.write_exception, @error_handler_spy.passed_exception
|
163
|
+
exp = {
|
164
|
+
:server_data => @server_data,
|
165
|
+
:request => @processed_service.request,
|
166
|
+
:handler_class => @processed_service.handler_class,
|
167
|
+
:response => @response
|
168
|
+
}
|
169
|
+
assert_equal exp, @error_handler_spy.context_hash
|
170
|
+
assert_true @error_handler_spy.run_called
|
171
|
+
end
|
172
|
+
|
173
|
+
should "store the error handler response and exception on the processed service" do
|
174
|
+
assert_equal @error_handler_spy.response, @processed_service.response
|
175
|
+
assert_equal @error_handler_spy.exception, @processed_service.exception
|
136
176
|
end
|
137
177
|
|
138
|
-
should "
|
139
|
-
assert_equal @
|
178
|
+
should "write the error response to the connection" do
|
179
|
+
assert_equal @error_handler_spy.response, @connection.response
|
140
180
|
assert_true @connection.write_closed
|
141
181
|
end
|
142
182
|
|
@@ -161,19 +201,21 @@ class Sanford::ConnectionHandler
|
|
161
201
|
should "have logged the service" do
|
162
202
|
time_taken = @processed_service.time_taken
|
163
203
|
status = @processed_service.response.status.to_s
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
assert_equal
|
204
|
+
exp = "[Sanford] ===== Received request =====" \
|
205
|
+
"[Sanford] Service: #{@request.name.inspect}" \
|
206
|
+
"[Sanford] Params: #{@request.params.inspect}" \
|
207
|
+
"[Sanford] Handler: #{@route.handler_class}" \
|
208
|
+
"[Sanford] ===== Completed in #{time_taken}ms #{status} ====="
|
209
|
+
assert_equal exp, subject.info_logged.join
|
170
210
|
end
|
171
211
|
|
172
212
|
should "log an exception when one is thrown" do
|
173
213
|
err = @processed_service.exception
|
174
|
-
|
175
|
-
|
176
|
-
|
214
|
+
exp = "[Sanford] #{err.class}: #{err.message}"
|
215
|
+
assert_equal exp, subject.error_logged.first
|
216
|
+
err.backtrace.each do |l|
|
217
|
+
assert_includes "[Sanford] #{l}", subject.error_logged
|
218
|
+
end
|
177
219
|
end
|
178
220
|
|
179
221
|
end
|
@@ -198,14 +240,14 @@ class Sanford::ConnectionHandler
|
|
198
240
|
time_taken = @processed_service.time_taken
|
199
241
|
status = @processed_service.response.status.to_i
|
200
242
|
exception_msg = "#{@exception.class}: #{@exception.message}"
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
assert_equal
|
243
|
+
exp = "[Sanford] " \
|
244
|
+
"time=#{time_taken} " \
|
245
|
+
"status=#{status} " \
|
246
|
+
"handler=#{@route.handler_class} " \
|
247
|
+
"service=#{@request.name.inspect} " \
|
248
|
+
"params=#{@request.params.inspect} " \
|
249
|
+
"error=#{exception_msg.inspect}"
|
250
|
+
assert_equal exp, subject.info_logged.join
|
209
251
|
end
|
210
252
|
|
211
253
|
should "not have logged the exception" do
|
@@ -216,6 +258,24 @@ class Sanford::ConnectionHandler
|
|
216
258
|
|
217
259
|
TestHandler = Class.new
|
218
260
|
|
261
|
+
class ErrorHandlerSpy
|
262
|
+
attr_reader :passed_exception, :context_hash, :exception, :response
|
263
|
+
attr_reader :run_called
|
264
|
+
|
265
|
+
def initialize(exception, context_hash)
|
266
|
+
@passed_exception = exception
|
267
|
+
@context_hash = context_hash
|
268
|
+
@exception = Factory.exception
|
269
|
+
@response = Factory.protocol_response
|
270
|
+
@run_called = false
|
271
|
+
end
|
272
|
+
|
273
|
+
def run
|
274
|
+
@run_called = true
|
275
|
+
@response
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
219
279
|
class SpyLogger
|
220
280
|
attr_reader :info_logged, :error_logged
|
221
281
|
|
@@ -100,7 +100,7 @@ class Sanford::ErrorHandler
|
|
100
100
|
subject{ @response }
|
101
101
|
|
102
102
|
should "return a bad request response" do
|
103
|
-
exp = Sanford::Protocol::Response.new([
|
103
|
+
exp = Sanford::Protocol::Response.new([400, @exception.message])
|
104
104
|
assert_equal exp, subject
|
105
105
|
end
|
106
106
|
|
@@ -117,7 +117,7 @@ class Sanford::ErrorHandler
|
|
117
117
|
subject{ @response }
|
118
118
|
|
119
119
|
should "return a bad request response" do
|
120
|
-
exp = Sanford::Protocol::Response.new([
|
120
|
+
exp = Sanford::Protocol::Response.new([400, @exception.message])
|
121
121
|
assert_equal exp, subject
|
122
122
|
end
|
123
123
|
|
@@ -134,7 +134,7 @@ class Sanford::ErrorHandler
|
|
134
134
|
subject{ @response }
|
135
135
|
|
136
136
|
should "return a not found response" do
|
137
|
-
exp = Sanford::Protocol::Response.new(
|
137
|
+
exp = Sanford::Protocol::Response.new(404)
|
138
138
|
assert_equal exp, subject
|
139
139
|
end
|
140
140
|
|
@@ -151,7 +151,7 @@ class Sanford::ErrorHandler
|
|
151
151
|
subject{ @response }
|
152
152
|
|
153
153
|
should "return a timeout response" do
|
154
|
-
exp = Sanford::Protocol::Response.new(
|
154
|
+
exp = Sanford::Protocol::Response.new(408)
|
155
155
|
assert_equal exp, subject
|
156
156
|
end
|
157
157
|
|
@@ -168,7 +168,7 @@ class Sanford::ErrorHandler
|
|
168
168
|
subject{ @response }
|
169
169
|
|
170
170
|
should "return an error response" do
|
171
|
-
exp = Sanford::Protocol::Response.new([
|
171
|
+
exp = Sanford::Protocol::Response.new([500, "An unexpected error occurred."])
|
172
172
|
assert_equal exp, subject
|
173
173
|
end
|
174
174
|
|
@@ -198,7 +198,7 @@ class Sanford::ErrorHandler
|
|
198
198
|
end
|
199
199
|
|
200
200
|
should "return an error response" do
|
201
|
-
exp = Sanford::Protocol::Response.new([
|
201
|
+
exp = Sanford::Protocol::Response.new([500, "An unexpected error occurred."])
|
202
202
|
assert_equal exp, @response
|
203
203
|
end
|
204
204
|
|
@@ -208,7 +208,7 @@ class Sanford::ErrorHandler
|
|
208
208
|
desc "with a protocol response returned from an error proc"
|
209
209
|
setup do
|
210
210
|
@proc_response = Sanford::Protocol::Response.new(Factory.integer)
|
211
|
-
@error_proc_spies.
|
211
|
+
@error_proc_spies.sample.response = @proc_response
|
212
212
|
|
213
213
|
@response = @handler.run
|
214
214
|
end
|
@@ -224,7 +224,7 @@ class Sanford::ErrorHandler
|
|
224
224
|
desc "with a response code returned from an error proc"
|
225
225
|
setup do
|
226
226
|
@response_code = Factory.integer
|
227
|
-
@error_proc_spies.
|
227
|
+
@error_proc_spies.sample.response = @response_code
|
228
228
|
|
229
229
|
@response = @handler.run
|
230
230
|
end
|
@@ -240,15 +240,15 @@ class Sanford::ErrorHandler
|
|
240
240
|
class RunWithSymbolFromErrorProcTests < RunSetupTests
|
241
241
|
desc "with a response symbol returned from an error proc"
|
242
242
|
setup do
|
243
|
-
@
|
244
|
-
@error_proc_spies.
|
243
|
+
@response_code = [400, 404, 500].sample
|
244
|
+
@error_proc_spies.sample.response = @response_code
|
245
245
|
|
246
246
|
@response = @handler.run
|
247
247
|
end
|
248
248
|
subject{ @response }
|
249
249
|
|
250
250
|
should "use the response symbol to build a response and return it" do
|
251
|
-
exp = Sanford::Protocol::Response.new(@
|
251
|
+
exp = Sanford::Protocol::Response.new(@response_code)
|
252
252
|
assert_equal exp, subject
|
253
253
|
end
|
254
254
|
|