sanford 0.10.1 → 0.11.0

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.
Files changed (73) hide show
  1. data/Gemfile +1 -1
  2. data/README.md +41 -56
  3. data/Rakefile +0 -1
  4. data/bench/client.rb +8 -3
  5. data/bench/{services.rb → config.sanford} +11 -6
  6. data/bench/{runner.rb → report.rb} +2 -2
  7. data/bench/report.txt +32 -32
  8. data/lib/sanford/cli.rb +42 -28
  9. data/lib/sanford/config_file.rb +79 -0
  10. data/lib/sanford/{worker.rb → connection_handler.rb} +28 -20
  11. data/lib/sanford/error_handler.rb +7 -7
  12. data/lib/sanford/pid_file.rb +42 -0
  13. data/lib/sanford/process.rb +136 -0
  14. data/lib/sanford/process_signal.rb +20 -0
  15. data/lib/sanford/route.rb +48 -0
  16. data/lib/sanford/router.rb +36 -0
  17. data/lib/sanford/runner.rb +30 -58
  18. data/lib/sanford/sanford_runner.rb +19 -9
  19. data/lib/sanford/server.rb +211 -42
  20. data/lib/sanford/server_data.rb +47 -0
  21. data/lib/sanford/service_handler.rb +8 -46
  22. data/lib/sanford/template_source.rb +19 -2
  23. data/lib/sanford/test_runner.rb +27 -28
  24. data/lib/sanford/version.rb +1 -1
  25. data/lib/sanford.rb +1 -23
  26. data/sanford.gemspec +4 -5
  27. data/test/helper.rb +3 -20
  28. data/test/support/app_server.rb +142 -0
  29. data/test/support/config.sanford +7 -0
  30. data/test/support/config_invalid_run.sanford +3 -0
  31. data/test/support/config_no_run.sanford +0 -0
  32. data/test/support/fake_server_connection.rb +58 -0
  33. data/test/support/pid_file_spy.rb +19 -0
  34. data/test/support/template.erb +1 -0
  35. data/test/system/server_tests.rb +378 -0
  36. data/test/system/service_handler_tests.rb +224 -0
  37. data/test/unit/cli_tests.rb +187 -0
  38. data/test/unit/config_file_tests.rb +59 -0
  39. data/test/unit/connection_handler_tests.rb +254 -0
  40. data/test/unit/error_handler_tests.rb +30 -35
  41. data/test/unit/pid_file_tests.rb +70 -0
  42. data/test/unit/process_signal_tests.rb +61 -0
  43. data/test/unit/process_tests.rb +428 -0
  44. data/test/unit/route_tests.rb +92 -0
  45. data/test/unit/router_tests.rb +65 -0
  46. data/test/unit/runner_tests.rb +61 -15
  47. data/test/unit/sanford_runner_tests.rb +162 -28
  48. data/test/unit/sanford_tests.rb +0 -8
  49. data/test/unit/server_data_tests.rb +87 -0
  50. data/test/unit/server_tests.rb +502 -21
  51. data/test/unit/service_handler_tests.rb +114 -219
  52. data/test/unit/template_engine_tests.rb +1 -1
  53. data/test/unit/template_source_tests.rb +56 -16
  54. data/test/unit/test_runner_tests.rb +206 -0
  55. metadata +67 -67
  56. data/bench/tasks.rb +0 -41
  57. data/lib/sanford/config.rb +0 -28
  58. data/lib/sanford/host.rb +0 -129
  59. data/lib/sanford/host_data.rb +0 -65
  60. data/lib/sanford/hosts.rb +0 -38
  61. data/lib/sanford/manager.rb +0 -275
  62. data/test/support/fake_connection.rb +0 -36
  63. data/test/support/helpers.rb +0 -17
  64. data/test/support/service_handlers.rb +0 -154
  65. data/test/support/services.rb +0 -123
  66. data/test/support/simple_client.rb +0 -62
  67. data/test/system/request_handling_tests.rb +0 -306
  68. data/test/unit/config_tests.rb +0 -56
  69. data/test/unit/host_data_tests.rb +0 -71
  70. data/test/unit/host_tests.rb +0 -141
  71. data/test/unit/hosts_tests.rb +0 -50
  72. data/test/unit/manager_tests.rb +0 -195
  73. data/test/unit/worker_tests.rb +0 -24
data/Gemfile CHANGED
@@ -3,6 +3,6 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  gem 'rake'
6
- gem 'pry'
6
+ gem 'pry', "~> 0.9.0"
7
7
 
8
8
  gem 'bson_ext'
data/README.md CHANGED
@@ -1,28 +1,28 @@
1
1
  # Sanford
2
2
 
3
- Sanford TCP protocol server for hosting services. Define hosts for versioned services. Setup handlers for the services. Run the host as a daemon.
3
+ Sanford TCP protocol server for hosting services. Define servers for services. Setup handlers for the services. Run the server as a daemon.
4
4
 
5
5
  Sanford uses [Sanford::Protocol](https://github.com/redding/sanford-protocol) to communicate with clients. Check out [AndSon](https://github.com/redding/and-son) for a Ruby Sanford protocol client.
6
6
 
7
7
  ## Usage
8
8
 
9
9
  ```ruby
10
- # define a host
11
- class MyHost
12
- include Sanford::Host
10
+ # define a server
11
+ class MyServer
12
+ include Sanford::Server
13
13
 
14
14
  port 8000
15
- pid_file '/path/to/host.pid'
15
+ pid_file '/path/to/server.pid'
16
16
 
17
17
  # define some services
18
- version 'v1' do
19
- service 'get_user', 'MyHost::V1Services::GetUser'
18
+ router do
19
+ service 'get_user', 'MyServer::GetUser'
20
20
  end
21
21
 
22
22
  end
23
23
 
24
24
  # define handlers for the services
25
- class MyHost::V1Services::GetUser
25
+ class MyServer::GetUser
26
26
  include Sanford::ServiceHandler
27
27
 
28
28
  def run!
@@ -33,47 +33,43 @@ end
33
33
 
34
34
  ```
35
35
 
36
- ## Hosts
36
+ ## Servers
37
37
 
38
- To define a Sanford host, include the mixin `Sanford::Host` on a class and use the DSL to configure it. A few options can be set:
38
+ To define a Sanford server, include the mixin `Sanford::Server` on a class and use the DSL to configure it. A few options can be set:
39
39
 
40
+ * `name` - (string) A name for the server, this is used to set the process name
41
+ and in logging.
40
42
  * `ip` - (string) A hostname or IP address for the server to bind to; default: `'0.0.0.0'`.
41
43
  * `port` - (integer) The port number for the server to bind to.
42
44
  * `pid_file` - (string) Path to where you want the pid file to be written.
43
45
  * `logger`- (logger) A logger for Sanford to use when handling requests; default: `Logger.new`.
44
46
 
45
- Any values specified using the DSL act as defaults for instances of the host. You can overwritten when creating new instances:
46
-
47
- ```ruby
48
- host = MyHost.new({ :port => 12000 })
49
- ```
50
-
51
47
  ## Services
52
48
 
53
49
  ```ruby
54
- class MyHost
55
- include Sanford::Host
50
+ class MyServer
51
+ include Sanford::Server
56
52
 
57
- version 'v1' do
58
- service 'get_user', 'MyHost::ServicesV1::GetUser'
53
+ router do
54
+ service 'get_user', 'MyServer::GetUser'
59
55
  end
60
56
  end
61
57
  ```
62
58
 
63
- Services are defined on hosts by version. Each named service maps to a 'service handler' class. The version and service name are used to 'route' requests to handler classes.
59
+ Services are defined on servers via a router block. Each named service maps to a 'service handler' class. The service name is used to 'route' requests to handler classes.
64
60
 
65
61
  When defining services handlers, it's typical to organize them all under a common namespace. Use `service_handler_ns` to define a default namespace for all handler classes under the version:
66
62
 
67
63
  ```ruby
68
- class MyHost
69
- include Sanford::Host
64
+ class MyServer
65
+ include Sanford::Server
70
66
 
71
- version 'v1' do
72
- service_handler_ns 'MyHost::ServicesV1'
67
+ router do
68
+ service_handler_ns 'MyServer'
73
69
 
74
70
  service 'get_user', 'GetUser'
75
71
  service 'get_article', 'GetArticle'
76
- service 'get_comments', '::MyHost::OtherServices::GetComments'
72
+ service 'get_comments', '::OtherServices::GetComments'
77
73
  end
78
74
  end
79
75
  ```
@@ -83,7 +79,7 @@ end
83
79
  Define handlers by mixing in `Sanford::ServiceHandler` on a class and defining a `run!` method:
84
80
 
85
81
  ```ruby
86
- class MyHost::Services::GetUser
82
+ class MyServer::GetUser
87
83
  include Sanford::ServiceHandler
88
84
 
89
85
  def run!
@@ -103,7 +99,7 @@ In addition to these, there are some helpers methods that can be used in your `r
103
99
  * `halt`: stop processing and return response data with a status code and message
104
100
 
105
101
  ```ruby
106
- class MyHost::Services::GetUser
102
+ class MyServer::GetUser
107
103
  include Sanford::ServiceHandler
108
104
 
109
105
  def run!
@@ -116,43 +112,32 @@ class MyHost::Services::GetUser
116
112
  end
117
113
  ```
118
114
 
119
- ## Running Host Daemons
120
-
121
- Sanford comes with a CLI for running hosts:
115
+ ## Running Servers
122
116
 
123
- * `sanford start` - spin up a background process running the host daemon.
124
- * `sanford stop` - shutdown the background process running the host gracefully.
125
- * `sanford restart` - "hot restart" the process running the host.
126
- * `sanford run` - starts the server, but doesn't daemonize it (runs in the current ruby process). Convenient when using the server in a development environment.
117
+ To run a server, Sanford needs a config file to be defined:
127
118
 
128
- The basic commands are useful if your application only has one host defined and if you only want to run the host on a single port. In the case you have multiple hosts defined or you want to run a single host on multiple ports, use environment variables to set custom configurations.
129
-
130
- ```bash
131
- sanford start # starts the first defined host
132
- SANFORD_HOST=AnotherHost SANFORD_PORT=13001 sanford start # choose a specific host and port to run on with ENV vars
119
+ ```ruby
120
+ require 'my_server'
121
+ run MyServer.new
133
122
  ```
134
123
 
135
- The CLI allow using environment variables for specifying which host to run the command against and for overriding the host's configuration. They recognize the a number of environment variables, but the main ones are: `SANFORD_HOST`, `SANFORD_IP`, and `SANFORD_PORT`.
136
-
137
- Define a `name` on a Host to set a string name for your host that can be used to reference a host when using the CLI. If no name is set, Sanford will use the host's class name.
124
+ This file works like a rackup file. You require in your server and call `run`
125
+ passing an instance of the server. To use these files, Sanford comes with a CLI:
138
126
 
139
- Alternatively, the CLI supports passing switches to override the host's configuration as well. Use `sanford --help` to see the options that are available.
127
+ * `sanford CONFIG_FILE start` - spin up a background process running the server.
128
+ * `sanford CONFIG_FILE stop` - shutdown the background process running the server gracefully.
129
+ * `sanford CONFIG_FILE restart` - "hot restart" the process running the server.
130
+ * `sanford CONFIG_FILE run` - starts the server, but doesn't daemonize it (runs in the current ruby process). Convenient when using the server in a development environment.
140
131
 
141
- ### Loading An Application
132
+ Sanford will use the configuration of your server to either start a process or manage an existing one. A servers ip and port can be overwritten using environment variables:
142
133
 
143
- Typically, a Sanford host is part of a larger application and parts of the application need to be initialized or loaded when you start your Sanford server. To support this, Sanford provides an `init` hook for hosts. The proc that is defined will be called before the Sanford server is started, properly running the server in your application's environment:
144
-
145
- ```ruby
146
- class MyHost
147
- include Sanford::Host
148
-
149
- init do
150
- require File.expand_path("../config/environment", __FILE__)
151
- end
152
-
153
- end
134
+ ```bash
135
+ sanford my_server.sanford start # starts a process for `MyServer`
136
+ SANFORD_IP="1.2.3.4" SANFORD_PORT=13001 sanford my_server.sanford start # run the same server on a custom ip and port
154
137
  ```
155
138
 
139
+ This allows running multiple instances of the same server on ips and ports that are different than its configuration if needed.
140
+
156
141
  ## Contributing
157
142
 
158
143
  1. Fork it
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
1
  require "bundler/gem_tasks"
2
- require 'bench/tasks'
data/bench/client.rb CHANGED
@@ -10,11 +10,16 @@ module Bench
10
10
  @host, @port = [ host, port ]
11
11
  end
12
12
 
13
- def call(version, name, params)
13
+ # TCP_NODELAY is set to disable buffering. In the case of Sanford
14
+ # communication, we have all the information we need to send up front and
15
+ # are closing the connection, so it doesn't need to buffer.
16
+ # See http://linux.die.net/man/7/tcp
17
+
18
+ def call(name, params)
14
19
  socket = TCPSocket.open(@host, @port)
15
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) # TODO - explain
20
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
16
21
  connection = Sanford::Protocol::Connection.new(socket)
17
- request = Sanford::Protocol::Request.new(version, name, params)
22
+ request = Sanford::Protocol::Request.new(name, params)
18
23
  connection.write(request.to_hash)
19
24
  connection.close_write
20
25
  if IO.select([ socket ], nil, nil, 10)
@@ -1,13 +1,16 @@
1
- class BenchHost
2
- include Sanford::Host
1
+ class BenchServer
2
+ include Sanford::Server
3
3
 
4
+ name 'bench'
4
5
  port 59284
5
- pid_file File.expand_path("../../tmp/bench_host.pid", __FILE__)
6
+ pid_file File.expand_path("../../tmp/bench_server.pid", __FILE__)
6
7
 
7
- logger Logger.new(STDOUT)
8
- verbose_logging false
8
+ logger Logger.new(STDOUT)
9
+ verbose_logging false
9
10
 
10
- service 'simple', 'BenchHost::Simple'
11
+ router do
12
+ service 'simple', 'BenchServer::Simple'
13
+ end
11
14
 
12
15
  class Simple
13
16
  include Sanford::ServiceHandler
@@ -22,3 +25,5 @@ class BenchHost
22
25
  end
23
26
 
24
27
  end
28
+
29
+ run BenchServer.new
@@ -1,6 +1,4 @@
1
- Bundler.setup(:benchmark)
2
1
  require 'benchmark'
3
-
4
2
  require 'bench/client'
5
3
 
6
4
  module Bench
@@ -222,3 +220,5 @@ module Bench
222
220
  end
223
221
 
224
222
  end
223
+
224
+ Bench::Runner.new.build_report
data/bench/report.txt CHANGED
@@ -2,40 +2,40 @@ Running benchmark report...
2
2
 
3
3
  Hitting "simple" service with {}, 10000 times
4
4
  ....................................................................................................
5
- Total Time: 8970.7162ms
6
- Average Time: 0.8970ms
7
- Min Time: 0.5419ms
8
- Max Time: 108.7779ms
5
+ Total Time: 5192.7267ms
6
+ Average Time: 0.5192ms
7
+ Min Time: 0.3479ms
8
+ Max Time: 56.1919ms
9
9
 
10
10
  Distribution (number of requests):
11
- 0ms: 9634
12
- 0.5ms: 2063
13
- 0.6ms: 5113
14
- 0.7ms: 1448
15
- 0.8ms: 675
16
- 0.9ms: 335
17
- 1ms: 319
18
- 1.0ms: 129
19
- 1.1ms: 67
20
- 1.2ms: 44
21
- 1.3ms: 20
22
- 1.4ms: 23
23
- 1.5ms: 11
24
- 1.6ms: 13
25
- 1.7ms: 1
26
- 1.8ms: 6
27
- 1.9ms: 5
28
- 2ms: 11
29
- 3ms: 9
30
- 4ms: 4
11
+ 0ms: 9929
12
+ 0.3ms: 6583
13
+ 0.4ms: 2845
14
+ 0.5ms: 289
15
+ 0.6ms: 130
16
+ 0.7ms: 47
17
+ 0.8ms: 23
18
+ 0.9ms: 12
19
+ 1ms: 31
20
+ 1.0ms: 10
21
+ 1.1ms: 6
22
+ 1.2ms: 5
23
+ 1.3ms: 5
24
+ 1.4ms: 1
25
+ 1.5ms: 2
26
+ 1.8ms: 2
27
+ 2ms: 1
31
28
  5ms: 1
32
- 21ms: 1
33
- 87ms: 1
34
- 90ms: 1
35
- 97ms: 5
36
- 98ms: 9
37
- 99ms: 2
38
- 100ms: 2
39
- 108ms: 1
29
+ 7ms: 1
30
+ 16ms: 1
31
+ 24ms: 1
32
+ 28ms: 5
33
+ 29ms: 13
34
+ 30ms: 4
35
+ 31ms: 2
36
+ 32ms: 5
37
+ 33ms: 4
38
+ 34ms: 1
39
+ 56ms: 1
40
40
 
41
41
  Done running benchmark report
data/lib/sanford/cli.rb CHANGED
@@ -1,51 +1,65 @@
1
1
  require 'sanford'
2
+ require 'sanford/config_file'
3
+ require 'sanford/process'
4
+ require 'sanford/process_signal'
2
5
  require 'sanford/version'
3
6
 
4
7
  module Sanford
5
8
 
6
9
  class CLI
7
10
 
8
- def self.run(*args)
11
+ def self.run(args)
9
12
  self.new.run(*args)
10
13
  end
11
14
 
12
- def initialize
13
- @cli = CLIRB.new do
14
- option :host, "Name of the Host configuration", :value => String
15
- option :ip, "IP address to bind to", :value => String
16
- option :port, "Port number to bind to", :value => Integer
17
- option :config, "File defining the configured Hosts", :value => String
18
- end
15
+ def initialize(kernel = nil)
16
+ @kernel = kernel || Kernel
17
+ @cli = CLIRB.new
19
18
  end
20
19
 
21
20
  def run(*args)
22
21
  begin
23
- @cli.parse!(*args)
24
- @command = @cli.args.first || 'run'
25
- Sanford.config.services_file = @cli.opts['config'] if @cli.opts['config']
26
- Sanford.init
27
- require 'sanford/manager'
28
- Sanford::Manager.call(@command, @cli.opts)
22
+ run!(*args)
29
23
  rescue CLIRB::HelpExit
30
- puts help
24
+ @kernel.puts help
31
25
  rescue CLIRB::VersionExit
32
- puts Sanford::VERSION
33
- rescue CLIRB::Error => exception
34
- puts "#{exception.message}\n\n"
35
- puts help
36
- exit(1)
37
- rescue SystemExit
38
- rescue Exception => exception
39
- puts "#{exception.class}: #{exception.message}"
40
- puts exception.backtrace.join("\n") if ENV['DEBUG']
41
- exit(1)
26
+ @kernel.puts "sanford #{Sanford::VERSION}"
27
+ rescue CLIRB::Error, Sanford::ConfigFile::InvalidError => exception
28
+ @kernel.puts "#{exception.message}\n\n"
29
+ @kernel.puts help
30
+ @kernel.exit 1
31
+ rescue StandardError => exception
32
+ @kernel.puts "#{exception.class}: #{exception.message}"
33
+ @kernel.puts exception.backtrace.join("\n")
34
+ @kernel.exit 1
35
+ end
36
+ @kernel.exit 0
37
+ end
38
+
39
+ private
40
+
41
+ def run!(*args)
42
+ @cli.parse!(args)
43
+ command = @cli.args.pop || 'run'
44
+ config_file_path = @cli.args.pop || 'config.sanford'
45
+ server = Sanford::ConfigFile.new(config_file_path).server
46
+ case(command)
47
+ when 'run'
48
+ Sanford::Process.new(server, :daemonize => false).run
49
+ when 'start'
50
+ Sanford::Process.new(server, :daemonize => true).run
51
+ when 'stop'
52
+ Sanford::ProcessSignal.new(server, 'TERM').send
53
+ when 'restart'
54
+ Sanford::ProcessSignal.new(server, 'USR2').send
55
+ else
56
+ raise CLIRB::Error, "#{command.inspect} is not a valid command"
42
57
  end
43
- exit(0)
44
58
  end
45
59
 
46
60
  def help
47
- "Usage: sanford <command> <options> \n" \
48
- "Commands: run, start, stop, restart \n" \
61
+ "Usage: sanford [CONFIG_FILE] [COMMAND]\n" \
62
+ "Commands: run, start, stop, restart" \
49
63
  "#{@cli}"
50
64
  end
51
65
 
@@ -0,0 +1,79 @@
1
+ require 'sanford/server'
2
+
3
+ module Sanford
4
+
5
+ class ConfigFile
6
+
7
+ attr_reader :server
8
+
9
+ def initialize(file_path)
10
+ @file_path = build_file_path(file_path)
11
+ @server = nil
12
+ evaluate_file(@file_path)
13
+ validate!
14
+ end
15
+
16
+ def run(server)
17
+ @server = server
18
+ end
19
+
20
+ private
21
+
22
+ def validate!
23
+ if !@server.kind_of?(Sanford::Server)
24
+ raise NoServerError.new(@server, @file_path)
25
+ end
26
+ end
27
+
28
+ def build_file_path(path)
29
+ full_path = File.expand_path(path)
30
+ raise NoConfigFileError.new(full_path) unless File.exists?(full_path)
31
+ full_path
32
+ rescue NoConfigFileError
33
+ full_path_with_sanford = "#{full_path}.sanford"
34
+ raise unless File.exists?(full_path_with_sanford)
35
+ full_path_with_sanford
36
+ end
37
+
38
+ # This evaluates the file and creates a proc using it's contents. This is
39
+ # a trick borrowed from Rack. It is essentially converting a file into a
40
+ # proc and then instance eval'ing it. This has a couple benefits:
41
+ # * The obvious benefit is the file is evaluated in the context of this
42
+ # class. This allows the file to call `run`, setting the server that
43
+ # will be used.
44
+ # * The other benefit is that the file's contents behave like they were a
45
+ # proc defined by the user. Instance eval'ing the file directly, makes
46
+ # any constants (modules/classes) defined in it namespaced by the
47
+ # instance of the config (not namespaced by `Sanford::ConfigFile`,
48
+ # they are actually namespaced by an instance of this class, its like
49
+ # accessing it via `ConfigFile.new::MyServer`), which is very confusing.
50
+ # Thus, the proc is created and eval'd using the `TOPLEVEL_BINDING`,
51
+ # which defines the constants at the top-level, as would be expected.
52
+ def evaluate_file(file_path)
53
+ config_file_code = "proc{ #{File.read(file_path)} }"
54
+ config_file_proc = eval(config_file_code, TOPLEVEL_BINDING, file_path, 0)
55
+ self.instance_eval(&config_file_proc)
56
+ end
57
+
58
+ InvalidError = Class.new(StandardError)
59
+
60
+ class NoConfigFileError < InvalidError
61
+ def initialize(path)
62
+ super "A configuration file couldn't be found at: #{path.to_s.inspect}"
63
+ end
64
+ end
65
+
66
+ class NoServerError < InvalidError
67
+ def initialize(server, path)
68
+ prefix = "Configuration file #{path.to_s.inspect}"
69
+ if server
70
+ super "#{prefix} called `run` without a Sanford::Server"
71
+ else
72
+ super "#{prefix} didn't call `run` with a Sanford::Server"
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -1,24 +1,27 @@
1
1
  require 'benchmark'
2
2
  require 'sanford-protocol'
3
-
4
3
  require 'sanford/error_handler'
5
4
  require 'sanford/logger'
6
5
  require 'sanford/runner'
7
6
 
8
7
  module Sanford
9
8
 
10
- class Worker
9
+ class ConnectionHandler
11
10
 
12
- ProcessedService = Struct.new(*[
11
+ ProcessedService = Struct.new(
13
12
  :request, :handler_class, :response, :exception, :time_taken
14
- ])
13
+ )
15
14
 
15
+ attr_reader :server_data, :connection
16
16
  attr_reader :logger
17
17
 
18
- def initialize(host_data, connection)
19
- @host_data, @connection = host_data, connection
20
-
21
- @logger = Sanford::Logger.new(@host_data.logger, @host_data.verbose)
18
+ def initialize(server_data, connection)
19
+ @server_data = server_data
20
+ @connection = connection
21
+ @logger = Sanford::Logger.new(
22
+ @server_data.logger,
23
+ @server_data.verbose_logging
24
+ )
22
25
  end
23
26
 
24
27
  def run
@@ -42,14 +45,14 @@ module Sanford
42
45
  self.log_request(request)
43
46
  service.request = request
44
47
 
45
- handler_class = @host_data.handler_class_for(request.name)
46
- self.log_handler_class(handler_class)
47
- service.handler_class = handler_class
48
+ route = @server_data.route_for(request.name)
49
+ self.log_handler_class(route.handler_class)
50
+ service.handler_class = route.handler_class
48
51
 
49
- response = @host_data.run(handler_class, request)
52
+ response = route.run(request, @server_data)
50
53
  service.response = response
51
- rescue Exception => exception
52
- self.handle_exception(service, exception, @host_data)
54
+ rescue StandardError => exception
55
+ self.handle_exception(service, exception, @server_data)
53
56
  ensure
54
57
  self.write_response(service)
55
58
  end
@@ -59,7 +62,7 @@ module Sanford
59
62
  def write_response(service)
60
63
  begin
61
64
  @connection.write_data service.response.to_hash
62
- rescue Exception => exception
65
+ rescue StandardError => exception
63
66
  service = self.handle_exception(service, exception)
64
67
  @connection.write_data service.response.to_hash
65
68
  end
@@ -67,8 +70,12 @@ module Sanford
67
70
  service
68
71
  end
69
72
 
70
- def handle_exception(service, exception, host_data = nil)
71
- error_handler = Sanford::ErrorHandler.new(exception, host_data, service.request)
73
+ def handle_exception(service, exception, server_data = nil)
74
+ error_handler = Sanford::ErrorHandler.new(
75
+ exception,
76
+ server_data,
77
+ service.request
78
+ )
72
79
  service.response = error_handler.run
73
80
  service.exception = error_handler.exception
74
81
  self.log_exception(service.exception)
@@ -110,8 +117,9 @@ module Sanford
110
117
  end
111
118
 
112
119
  def log_exception(exception)
113
- log_verbose("#{exception.class}: #{exception.message}", :error)
114
- log_verbose(exception.backtrace.join("\n"), :error)
120
+ backtrace = exception.backtrace.join("\n")
121
+ message = "#{exception.class}: #{exception.message}\n#{backtrace}"
122
+ log_verbose(message, :error)
115
123
  end
116
124
 
117
125
  def log_verbose(message, level = :info)
@@ -132,7 +140,7 @@ module Sanford
132
140
 
133
141
  module SummaryLine
134
142
  def self.new(line_attrs)
135
- attr_keys = %w{time status handler version service params}
143
+ attr_keys = %w{time status handler service params}
136
144
  attr_keys.map{ |k| "#{k}=#{line_attrs[k].inspect}" }.join(' ')
137
145
  end
138
146
  end
@@ -5,12 +5,12 @@ module Sanford
5
5
 
6
6
  class ErrorHandler
7
7
 
8
- attr_reader :exception, :host_data, :request
8
+ attr_reader :exception, :server_data, :request
9
+ attr_reader :error_procs
9
10
 
10
- def initialize(exception, host_data = nil, request = nil)
11
- @exception, @host_data, @request = exception, host_data, request
12
- @keep_alive = @host_data ? @host_data.keep_alive : false
13
- @error_procs = @host_data ? @host_data.error_procs.reverse : []
11
+ def initialize(exception, server_data = nil, request = nil)
12
+ @exception, @server_data, @request = exception, server_data, request
13
+ @error_procs = @server_data ? @server_data.error_procs.reverse : []
14
14
  end
15
15
 
16
16
  # The exception that we are generating a response for can change in the case
@@ -24,8 +24,8 @@ module Sanford
24
24
  @error_procs.each do |error_proc|
25
25
  result = nil
26
26
  begin
27
- result = error_proc.call(@exception, @host_data, @request)
28
- rescue Exception => proc_exception
27
+ result = error_proc.call(@exception, @server_data, @request)
28
+ rescue StandardError => proc_exception
29
29
  @exception = proc_exception
30
30
  end
31
31
  response ||= self.response_from_proc(result)
@@ -0,0 +1,42 @@
1
+ require 'fileutils'
2
+
3
+ module Sanford
4
+
5
+ class PIDFile
6
+ attr_reader :path
7
+
8
+ def initialize(path)
9
+ @path = (path || '/dev/null').to_s
10
+ end
11
+
12
+ def pid
13
+ pid = File.read(@path).strip
14
+ pid && !pid.empty? ? pid.to_i : raise('no pid in file')
15
+ rescue StandardError => exception
16
+ error = InvalidError.new("A PID couldn't be read from #{@path.inspect}")
17
+ error.set_backtrace(exception.backtrace)
18
+ raise error
19
+ end
20
+
21
+ def write
22
+ FileUtils.mkdir_p(File.dirname(@path))
23
+ File.open(@path, 'w'){ |f| f.puts ::Process.pid }
24
+ rescue StandardError => exception
25
+ error = InvalidError.new("Can't write pid to file #{@path.inspect}")
26
+ error.set_backtrace(exception.backtrace)
27
+ raise error
28
+ end
29
+
30
+ def remove
31
+ FileUtils.rm_f(@path)
32
+ end
33
+
34
+ def to_s
35
+ @path
36
+ end
37
+
38
+ InvalidError = Class.new(RuntimeError)
39
+
40
+ end
41
+
42
+ end