sanford 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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