mizuno 0.4.1 → 0.5.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.
- data/.gitignore +7 -0
- data/Rakefile +19 -0
- data/bin/mizuno +3 -4
- data/lib/java/log4j-1.2.16.jar +0 -0
- data/lib/java/rewindable-input-stream.jar +0 -0
- data/lib/java/slf4j-api-1.6.4.jar +0 -0
- data/lib/java/slf4j-log4j12-1.6.4.jar +0 -0
- data/lib/mizuno.rb +14 -4
- data/lib/mizuno/choices.rb +158 -0
- data/lib/mizuno/http_server.rb +102 -18
- data/lib/mizuno/java_logger.rb +37 -0
- data/lib/mizuno/rack/chunked.rb +19 -0
- data/lib/mizuno/rack_servlet.rb +65 -43
- data/lib/mizuno/runner.rb +244 -0
- data/lib/mizuno/version.rb +3 -0
- data/mizuno.gemspec +14 -4
- metadata +55 -15
data/.gitignore
ADDED
data/Rakefile
CHANGED
@@ -78,3 +78,22 @@ namespace :jetty do
|
|
78
78
|
puts "Update complete."
|
79
79
|
end
|
80
80
|
end
|
81
|
+
|
82
|
+
namespace :java do
|
83
|
+
desc "Build bundled Java source files."
|
84
|
+
task :build do
|
85
|
+
system(<<-END)
|
86
|
+
javac -classpath lib/java/servlet-api-3.0.jar \
|
87
|
+
src/org/jruby/rack/servlet/RewindableInputStream.java
|
88
|
+
jar cf lib/java/rewindable-input-stream.jar -C src/ \
|
89
|
+
org/jruby/rack/servlet/RewindableInputStream.class
|
90
|
+
END
|
91
|
+
end
|
92
|
+
|
93
|
+
desc "Clean up after building."
|
94
|
+
task :clean do
|
95
|
+
system(<<-END)
|
96
|
+
rm src/org/jruby/rack/servlet/RewindableInputStream.class
|
97
|
+
END
|
98
|
+
end
|
99
|
+
end
|
data/bin/mizuno
CHANGED
@@ -2,9 +2,8 @@
|
|
2
2
|
|
3
3
|
raise("Mizuno only runs on JRuby.") unless (RUBY_PLATFORM =~ /java/)
|
4
4
|
|
5
|
-
require '
|
5
|
+
require 'rubygems'
|
6
6
|
require 'mizuno'
|
7
|
+
require 'mizuno/runner'
|
7
8
|
|
8
|
-
|
9
|
-
server.options[:server] = 'mizuno'
|
10
|
-
server.start
|
9
|
+
Mizuno::Runner.start!
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/mizuno.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
1
|
#
|
2
|
-
# A Rack handler for Jetty
|
2
|
+
# A Rack handler for Jetty 8.
|
3
3
|
#
|
4
4
|
# Written by Don Werve <don@madwombat.com>
|
5
5
|
#
|
6
6
|
|
7
|
-
#
|
8
|
-
|
7
|
+
# Save our launch environment for spawning children later.
|
8
|
+
module Mizuno
|
9
|
+
LAUNCH_ENV = $LOAD_PATH.map { |i| "-I#{i}" }.push($0)
|
10
|
+
end
|
9
11
|
|
10
|
-
# Load
|
12
|
+
# Load up Java dependencies.
|
13
|
+
require 'java'
|
11
14
|
jars = File.join(File.dirname(__FILE__), 'java', '*.jar')
|
12
15
|
Dir[jars].each { |j| require j }
|
13
16
|
|
17
|
+
# Tell log4j not to complain to the console about a missing
|
18
|
+
# log4j.properties file, as we configure it programmatically in
|
19
|
+
# Mizuno::HttpServer (http://stackoverflow.com/questions/6849887)
|
20
|
+
Java.org.apache.log4j.Logger.getRootLogger.setLevel( \
|
21
|
+
Java.org.apache.log4j.Level::INFO)
|
22
|
+
|
14
23
|
require 'rack'
|
24
|
+
require 'mizuno/rack/chunked'
|
15
25
|
require 'mizuno/rack_servlet'
|
16
26
|
require 'mizuno/http_server'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
Choice.options do
|
2
|
+
separator ''
|
3
|
+
separator 'Ruby options: '
|
4
|
+
|
5
|
+
option :eval do
|
6
|
+
short '-e'
|
7
|
+
long '--eval'
|
8
|
+
desc 'evaluate a line of code'
|
9
|
+
default nil
|
10
|
+
end
|
11
|
+
|
12
|
+
option :debug do
|
13
|
+
short '-d'
|
14
|
+
long '--debug'
|
15
|
+
desc 'set debugging flags (set $DEBUG to true)'
|
16
|
+
default false
|
17
|
+
end
|
18
|
+
|
19
|
+
option :warn do
|
20
|
+
short '-w'
|
21
|
+
long '--warn'
|
22
|
+
desc 'turn warnings on for your script'
|
23
|
+
default false
|
24
|
+
end
|
25
|
+
|
26
|
+
option :include do
|
27
|
+
short '-I'
|
28
|
+
long '--include *PATHS'
|
29
|
+
desc 'specify $LOAD_PATH (may be used more than once)'
|
30
|
+
default []
|
31
|
+
end
|
32
|
+
|
33
|
+
option :require do
|
34
|
+
short '-r'
|
35
|
+
long '--require *GEMS'
|
36
|
+
desc 'require a gem (may be used more than once)'
|
37
|
+
default []
|
38
|
+
end
|
39
|
+
|
40
|
+
separator ''
|
41
|
+
separator 'Rack options: '
|
42
|
+
|
43
|
+
option :host do
|
44
|
+
short '-o'
|
45
|
+
long '--host'
|
46
|
+
desc 'the address to listen on'
|
47
|
+
default '0.0.0.0'
|
48
|
+
end
|
49
|
+
|
50
|
+
option :port do
|
51
|
+
short '-p'
|
52
|
+
long '--port'
|
53
|
+
desc 'the port to listen on'
|
54
|
+
cast Integer
|
55
|
+
default 9292
|
56
|
+
end
|
57
|
+
|
58
|
+
option :env do
|
59
|
+
short '-E'
|
60
|
+
long '--env'
|
61
|
+
desc 'application environment'
|
62
|
+
default 'development'
|
63
|
+
end
|
64
|
+
|
65
|
+
option :threads do
|
66
|
+
long '--threads'
|
67
|
+
desc 'maximum size of the thread pool'
|
68
|
+
cast Integer
|
69
|
+
default 50
|
70
|
+
end
|
71
|
+
|
72
|
+
option :log do
|
73
|
+
long '--log'
|
74
|
+
desc 'logfile (defaults to stderr)'
|
75
|
+
default nil
|
76
|
+
end
|
77
|
+
|
78
|
+
separator ''
|
79
|
+
separator 'Daemonization: '
|
80
|
+
|
81
|
+
option :daemonize do
|
82
|
+
short '-D'
|
83
|
+
long '--start'
|
84
|
+
desc 'detach and run as a daemon'
|
85
|
+
default false
|
86
|
+
end
|
87
|
+
|
88
|
+
option :stop do
|
89
|
+
long '--stop'
|
90
|
+
desc 'stop a running daemon'
|
91
|
+
default false
|
92
|
+
end
|
93
|
+
|
94
|
+
option :kill do
|
95
|
+
long '--stop'
|
96
|
+
desc 'stop a running daemon'
|
97
|
+
default false
|
98
|
+
end
|
99
|
+
|
100
|
+
option :status do
|
101
|
+
long '--status'
|
102
|
+
desc 'get the status of a running daemon'
|
103
|
+
default false
|
104
|
+
end
|
105
|
+
|
106
|
+
option :reload do
|
107
|
+
long '--reload'
|
108
|
+
desc 'reload a running daemon'
|
109
|
+
default false
|
110
|
+
end
|
111
|
+
|
112
|
+
option :pidfile do
|
113
|
+
short '-P'
|
114
|
+
long '--pidfile'
|
115
|
+
desc 'pidfile for when running as a daemon'
|
116
|
+
default 'mizuno.pid'
|
117
|
+
end
|
118
|
+
|
119
|
+
option :user do
|
120
|
+
long '--user'
|
121
|
+
desc 'user to run as'
|
122
|
+
default nil
|
123
|
+
end
|
124
|
+
|
125
|
+
option :group do
|
126
|
+
long '--group'
|
127
|
+
desc 'group to run as'
|
128
|
+
default nil
|
129
|
+
end
|
130
|
+
|
131
|
+
option :root do
|
132
|
+
long '--root'
|
133
|
+
desc 'app root (defaults to the current directory)'
|
134
|
+
default Dir.pwd
|
135
|
+
end
|
136
|
+
|
137
|
+
separator ''
|
138
|
+
separator 'Common options: '
|
139
|
+
|
140
|
+
option :help do
|
141
|
+
short '-h'
|
142
|
+
long '--help'
|
143
|
+
desc 'Show this message'
|
144
|
+
action { Choice.help }
|
145
|
+
end
|
146
|
+
|
147
|
+
option :version do
|
148
|
+
short '-v'
|
149
|
+
long '--version'
|
150
|
+
desc 'Show version'
|
151
|
+
action do
|
152
|
+
$stderr.puts Mizuno::HttpServer.version
|
153
|
+
exit
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
data/lib/mizuno/http_server.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
"org.eclipse.jetty.util.log.StdErrLog")
|
1
|
+
require 'mizuno/version'
|
2
|
+
require 'mizuno/java_logger'
|
4
3
|
|
5
4
|
module Mizuno
|
6
5
|
class HttpServer
|
6
|
+
include_class 'java.util.Properties'
|
7
|
+
include_class 'java.io.ByteArrayInputStream'
|
8
|
+
include_class 'org.apache.log4j.PropertyConfigurator'
|
7
9
|
include_class 'org.eclipse.jetty.server.Server'
|
8
10
|
include_class 'org.eclipse.jetty.servlet.ServletContextHandler'
|
9
11
|
include_class 'org.eclipse.jetty.servlet.ServletHolder'
|
10
12
|
include_class 'org.eclipse.jetty.server.nio.SelectChannelConnector'
|
11
13
|
include_class 'org.eclipse.jetty.util.thread.QueuedThreadPool'
|
12
|
-
include_class 'org.eclipse.jetty.servlet.DefaultServlet'
|
14
|
+
# include_class 'org.eclipse.jetty.servlet.DefaultServlet'
|
15
|
+
# include_class 'org.eclipse.jetty.server.handler.HandlerCollection'
|
16
|
+
# include_class 'org.eclipse.jetty.server.handler.RequestLogHandler'
|
17
|
+
# include_class 'org.eclipse.jetty.server.NCSARequestLog'
|
13
18
|
|
14
19
|
#
|
15
20
|
# Provide accessors so we can set a custom logger and a location
|
@@ -32,19 +37,26 @@ module Mizuno
|
|
32
37
|
# String or integer with the port to bind to; defaults
|
33
38
|
# to 9292.
|
34
39
|
#
|
35
|
-
#
|
40
|
+
# http://wiki.eclipse.org/Jetty/Tutorial/RequestLog
|
36
41
|
#
|
37
|
-
|
42
|
+
# FIXME: Add SSL suport.
|
43
|
+
#
|
44
|
+
def HttpServer.run(app, options = {})
|
45
|
+
# Symbolize and downcase keys.
|
46
|
+
@options = options = Hash[options.map { |k, v|
|
47
|
+
[ k.to_s.downcase.to_sym, v ] }]
|
48
|
+
options[:quiet] ||= true if options[:embedded]
|
49
|
+
|
38
50
|
# The Jetty server
|
51
|
+
configure_logging(options)
|
39
52
|
@server = Server.new
|
40
|
-
|
41
|
-
options = Hash[options.map { |o|
|
42
|
-
[ o[0].to_s.downcase.to_sym, o[1] ] }]
|
53
|
+
@server.setSendServerVersion(false)
|
43
54
|
|
44
55
|
# Thread pool
|
56
|
+
threads = options[:threads] || 50
|
45
57
|
thread_pool = QueuedThreadPool.new
|
46
|
-
thread_pool.min_threads = 5
|
47
|
-
thread_pool.max_threads =
|
58
|
+
thread_pool.min_threads = [ threads.to_i / 10, 5 ].max
|
59
|
+
thread_pool.max_threads = [ threads.to_i, 10 ].max
|
48
60
|
@server.set_thread_pool(thread_pool)
|
49
61
|
|
50
62
|
# Connector
|
@@ -53,20 +65,36 @@ module Mizuno
|
|
53
65
|
connector.setHost(options[:host])
|
54
66
|
@server.addConnector(connector)
|
55
67
|
|
56
|
-
#
|
57
|
-
|
68
|
+
# Switch to a different user or group if we were asked to.
|
69
|
+
Runner.setgid(options) if options[:group]
|
70
|
+
Runner.setuid(options) if options[:user]
|
71
|
+
|
72
|
+
# Servlet handler.
|
73
|
+
app_handler = ServletContextHandler.new(nil, "/",
|
58
74
|
ServletContextHandler::NO_SESSIONS)
|
59
75
|
|
60
76
|
# The servlet itself.
|
61
77
|
rack_servlet = RackServlet.new
|
62
78
|
rack_servlet.rackup(app)
|
63
79
|
holder = ServletHolder.new(rack_servlet)
|
64
|
-
|
80
|
+
app_handler.addServlet(holder, "/")
|
81
|
+
|
82
|
+
# # Our request log.
|
83
|
+
# request_log = NCSARequestLog.new
|
84
|
+
# request_log.setLogTimeZone("GMT")
|
85
|
+
# request_log_handler = RequestLogHandler.new
|
86
|
+
# request_log_handler.setRequestLog(request_log)
|
87
|
+
#
|
88
|
+
# # Add handlers in order.
|
89
|
+
# handlers = HandlerCollection.new
|
90
|
+
# handlers.addHandler(request_log_handler)
|
91
|
+
# handlers.addHandler(app_handler)
|
65
92
|
|
66
93
|
# Add the context to the server and start.
|
67
|
-
@server.set_handler(
|
68
|
-
puts "Listening on #{connector.getHost}:#{connector.getPort}"
|
94
|
+
@server.set_handler(app_handler)
|
69
95
|
@server.start
|
96
|
+
$stderr.printf("%s listening on %s:%s\n", version,
|
97
|
+
connector.host, connector.port) unless options[:quiet]
|
70
98
|
|
71
99
|
# If we're embeded, we're done.
|
72
100
|
return if options[:embedded]
|
@@ -83,10 +111,66 @@ module Mizuno
|
|
83
111
|
#
|
84
112
|
# Shuts down an embedded Jetty instance.
|
85
113
|
#
|
86
|
-
def
|
114
|
+
def HttpServer.stop
|
87
115
|
return unless @server
|
88
|
-
|
116
|
+
$stderr.print "Stopping Jetty..." unless @options[:quiet]
|
89
117
|
@server.stop
|
118
|
+
$stderr.puts "done." unless @options[:quiet]
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Returns the full version string.
|
123
|
+
#
|
124
|
+
def HttpServer.version
|
125
|
+
"Mizuno #{Mizuno::VERSION} (Jetty #{Server.getVersion})"
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Configure Log4J.
|
130
|
+
#
|
131
|
+
def HttpServer.configure_logging(options)
|
132
|
+
return if @logger
|
133
|
+
|
134
|
+
# Default logging threshold.
|
135
|
+
limit = options[:warn] ? "WARN" : "ERROR"
|
136
|
+
limit = "DEBUG" if ($DEBUG or options[:debug])
|
137
|
+
|
138
|
+
# Base logging configuration.
|
139
|
+
config = <<-END
|
140
|
+
log4j.rootCategory = #{limit}, default
|
141
|
+
log4j.logger.org.eclipse.jetty.util.log = #{limit}, default
|
142
|
+
log4j.logger.ruby = INFO, ruby
|
143
|
+
log4j.appender.default.Threshold = #{limit}
|
144
|
+
log4j.appender.default.layout = org.apache.log4j.PatternLayout
|
145
|
+
log4j.appender.default.layout.ConversionPattern = %d %p %m
|
146
|
+
log4j.appender.ruby.Threshold = INFO
|
147
|
+
log4j.appender.ruby.layout = org.apache.log4j.PatternLayout
|
148
|
+
log4j.appender.ruby.layout.ConversionPattern = %m
|
149
|
+
END
|
150
|
+
|
151
|
+
# Should we log to the console?
|
152
|
+
config.concat(<<-END) unless options[:log]
|
153
|
+
log4j.appender.default = org.apache.log4j.ConsoleAppender
|
154
|
+
log4j.appender.ruby = org.apache.log4j.ConsoleAppender
|
155
|
+
END
|
156
|
+
|
157
|
+
# Are we logging to a file?
|
158
|
+
config.concat(<<-END) if options[:log]
|
159
|
+
log4j.appender.default = org.apache.log4j.FileAppender
|
160
|
+
log4j.appender.default.File = #{options[:log]}
|
161
|
+
log4j.appender.default.Append = true
|
162
|
+
log4j.appender.ruby = org.apache.log4j.FileAppender
|
163
|
+
log4j.appender.ruby.File = #{options[:log]}
|
164
|
+
log4j.appender.ruby.Append = true
|
165
|
+
END
|
166
|
+
|
167
|
+
# Set up Log4J via Properties.
|
168
|
+
properties = Properties.new
|
169
|
+
properties.load(ByteArrayInputStream.new(config.to_java_bytes))
|
170
|
+
PropertyConfigurator.configure(properties)
|
171
|
+
|
172
|
+
# Use log4j for our logging as well.
|
173
|
+
@logger = JavaLogger.new
|
90
174
|
end
|
91
175
|
end
|
92
176
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Mizuno
|
4
|
+
class JavaLogger < Logger
|
5
|
+
LEVELS = {
|
6
|
+
Logger::DEBUG => Java.org.apache.log4j.Level::DEBUG,
|
7
|
+
Logger::INFO => Java.org.apache.log4j.Level::INFO,
|
8
|
+
Logger::WARN => Java.org.apache.log4j.Level::WARN,
|
9
|
+
Logger::ERROR => Java.org.apache.log4j.Level::ERROR,
|
10
|
+
Logger::FATAL => Java.org.apache.log4j.Level::FATAL }
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@log4j = Java.org.apache.log4j.Logger.getLogger('ruby')
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(severity, message = nil, progname = nil)
|
17
|
+
content = (message or (block_given? and yield) or progname)
|
18
|
+
@log4j.log(LEVELS[severity], content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def puts(message)
|
22
|
+
write(message.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(message)
|
26
|
+
add(INFO, message)
|
27
|
+
end
|
28
|
+
|
29
|
+
def flush
|
30
|
+
# No-op.
|
31
|
+
end
|
32
|
+
|
33
|
+
def close
|
34
|
+
# No-op.
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
#
|
4
|
+
# We replace the default Rack::Chunked implementation with a non-op
|
5
|
+
# version, as Jetty handles chunking for us.
|
6
|
+
#
|
7
|
+
module Rack
|
8
|
+
class Chunked
|
9
|
+
include Rack::Utils
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/mizuno/rack_servlet.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
require 'rack/response'
|
2
3
|
|
3
4
|
#
|
4
5
|
# Wraps a Rack application in a Java servlet.
|
@@ -15,6 +16,7 @@ module Mizuno
|
|
15
16
|
class RackServlet < HttpServlet
|
16
17
|
include_class java.io.FileInputStream
|
17
18
|
include_class org.eclipse.jetty.continuation.ContinuationSupport
|
19
|
+
include_class org.jruby.rack.servlet.RewindableInputStream
|
18
20
|
|
19
21
|
#
|
20
22
|
# Sets the Rack application that handles requests sent to this
|
@@ -40,47 +42,49 @@ module Mizuno
|
|
40
42
|
# empty body, we declare the async response finished.
|
41
43
|
#
|
42
44
|
def service(request, response)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
45
|
+
handle_exceptions(response) do
|
46
|
+
# Turn the ServletRequest into a Rack env hash
|
47
|
+
env = servlet_to_rack(request)
|
48
|
+
|
49
|
+
# Handle asynchronous responses via Servlet continuations.
|
50
|
+
continuation = ContinuationSupport.getContinuation(request)
|
51
|
+
|
52
|
+
# If this is an expired connection, do nothing.
|
53
|
+
return if continuation.isExpired
|
54
|
+
|
55
|
+
# We should never be re-dispatched.
|
56
|
+
raise("Request re-dispatched.") unless continuation.isInitial
|
57
|
+
|
58
|
+
# Add our own special bits to the rack environment so that
|
59
|
+
# Rack middleware can have access to the Java internals.
|
60
|
+
env['rack.java.servlet'] = true
|
61
|
+
env['rack.java.servlet.request'] = request
|
62
|
+
env['rack.java.servlet.response'] = response
|
63
|
+
env['rack.java.servlet.continuation'] = continuation
|
64
|
+
|
65
|
+
# Add an callback that can be used to add results to the
|
66
|
+
# response asynchronously.
|
67
|
+
env['async.callback'] = lambda do |rack_response|
|
68
|
+
servlet_response = continuation.getServletResponse
|
69
|
+
rack_to_servlet(rack_response, servlet_response) \
|
70
|
+
and continuation.complete
|
71
|
+
end
|
69
72
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
# Execute the Rack request.
|
74
|
+
catch(:async) do
|
75
|
+
rack_response = @app.call(env)
|
76
|
+
|
77
|
+
# For apps that don't throw :async.
|
78
|
+
unless(rack_response[0] == -1)
|
79
|
+
# Nope, nothing asynchronous here.
|
80
|
+
rack_to_servlet(rack_response, response)
|
81
|
+
return
|
82
|
+
end
|
79
83
|
end
|
80
|
-
end
|
81
84
|
|
82
|
-
|
83
|
-
|
85
|
+
# If we got here, this is a continuation.
|
86
|
+
continuation.suspend(response)
|
87
|
+
end
|
84
88
|
end
|
85
89
|
|
86
90
|
private
|
@@ -123,12 +127,12 @@ module Mizuno
|
|
123
127
|
env['rack.run_once'] = false
|
124
128
|
|
125
129
|
# The input stream is a wrapper around the Java InputStream.
|
126
|
-
env['rack.input'] =
|
130
|
+
env['rack.input'] = RewindableInputStream.new( \
|
131
|
+
request.getInputStream).to_io.binmode
|
127
132
|
|
128
133
|
# Force encoding if we're on Ruby 1.9
|
129
134
|
env['rack.input'].set_encoding(Encoding.find("ASCII-8BIT")) \
|
130
135
|
if env['rack.input'].respond_to?(:set_encoding)
|
131
|
-
# puts "**** rack.input.encoding: #{env['rack.input'].encoding}"
|
132
136
|
|
133
137
|
# Populate the HTTP headers.
|
134
138
|
request.getHeaderNames.each do |header_name|
|
@@ -143,8 +147,9 @@ module Mizuno
|
|
143
147
|
env["CONTENT_LENGTH"] = env.delete("HTTP_CONTENT_LENGTH") \
|
144
148
|
if env["HTTP_CONTENT_LENGTH"]
|
145
149
|
|
146
|
-
#
|
147
|
-
env['rack.errors'] ||=
|
150
|
+
# Route errors through the logger.
|
151
|
+
env['rack.errors'] ||= HttpServer.logger
|
152
|
+
env['rack.logger'] ||= HttpServer.logger
|
148
153
|
|
149
154
|
# All done, hand back the Rack request.
|
150
155
|
return(env)
|
@@ -179,7 +184,7 @@ module Mizuno
|
|
179
184
|
# data out on an async request.
|
180
185
|
unless(response.isCommitted)
|
181
186
|
# Set the HTTP status code.
|
182
|
-
response.setStatus(status)
|
187
|
+
response.setStatus(status.to_i)
|
183
188
|
|
184
189
|
# Did we get a Content-Length header?
|
185
190
|
content_length = headers.delete('Content-Length')
|
@@ -222,5 +227,22 @@ module Mizuno
|
|
222
227
|
# All done.
|
223
228
|
output.flush
|
224
229
|
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# Handle exceptions, returning a generic 500 error response.
|
233
|
+
#
|
234
|
+
def handle_exceptions(response)
|
235
|
+
begin
|
236
|
+
yield
|
237
|
+
rescue => error
|
238
|
+
message = "Exception: #{error}"
|
239
|
+
message << "\n#{error.backtrace.join("\n")}" \
|
240
|
+
if (error.respond_to?(:backtrace))
|
241
|
+
HttpServer.logger.error(message)
|
242
|
+
return if response.isCommitted
|
243
|
+
response.reset
|
244
|
+
response.setStatus(500)
|
245
|
+
end
|
246
|
+
end
|
225
247
|
end
|
226
248
|
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'net/http'
|
3
|
+
require 'choice'
|
4
|
+
require 'mizuno/choices'
|
5
|
+
require 'childprocess'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'etc'
|
8
|
+
require 'rack'
|
9
|
+
|
10
|
+
module Mizuno
|
11
|
+
#
|
12
|
+
# Launches Mizuno when called from the command-line, and handles
|
13
|
+
# damonization via FFI.
|
14
|
+
#
|
15
|
+
# Daemonization code based on Spoon.
|
16
|
+
#
|
17
|
+
class Runner
|
18
|
+
extend FFI::Library
|
19
|
+
|
20
|
+
ffi_lib 'c'
|
21
|
+
|
22
|
+
attach_function :_setuid, :setuid, [ :uint ], :int
|
23
|
+
|
24
|
+
attach_function :_setgid, :setgid, [ :uint ], :int
|
25
|
+
|
26
|
+
#
|
27
|
+
# Switch the process over to a new user id; will abort the
|
28
|
+
# process if it fails. _options_ is the full list of options
|
29
|
+
# passed to a server.
|
30
|
+
#
|
31
|
+
def Runner.setuid(options)
|
32
|
+
entry = Etc.getpwnam(options[:user])
|
33
|
+
die("Can't find --user named '#{options[:user]}'") unless entry
|
34
|
+
return unless (_setuid(entry.uid) != 0)
|
35
|
+
die("Can't switch to user '#{options[:user]}'")
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Like setuid, but for groups.
|
40
|
+
#
|
41
|
+
def Runner.setgid(options)
|
42
|
+
entry = Etc.getgrnam(options[:group])
|
43
|
+
die("Can't find --group named '#{options[:group]}'") unless entry
|
44
|
+
return unless (_setgid(entry.gid) != 0)
|
45
|
+
die("Can't switch to group '#{options[:group]}'")
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Launch Jetty, optionally as a daemon.
|
50
|
+
#
|
51
|
+
def Runner.start!
|
52
|
+
# Default rackup is in config.ru
|
53
|
+
config = (Choice.rest.first or "config.ru")
|
54
|
+
|
55
|
+
# Create an options hash with only symbols.
|
56
|
+
choices = Choice.choices.merge(:config => config)
|
57
|
+
options = Hash[choices.map { |k, v| [ k.to_sym, v ] }]
|
58
|
+
|
59
|
+
# Resolve relative paths to the logfile, etc.
|
60
|
+
root = options[:root]
|
61
|
+
options[:pidfile] = Runner.resolve_path(root, options[:pidfile])
|
62
|
+
options[:log] = Runner.resolve_path(root, options[:log])
|
63
|
+
options[:public] = Runner.resolve_path(root, options[:public])
|
64
|
+
|
65
|
+
# Require multiple libraries.
|
66
|
+
options.delete(:require).each { |r| require r }
|
67
|
+
|
68
|
+
# Handle daemon-related commands.
|
69
|
+
Runner.status(options) if options.delete(:status)
|
70
|
+
Runner.reload(options) if options.delete(:reload)
|
71
|
+
Runner.stop(options) if options.delete(:stop)
|
72
|
+
Runner.kill(options) if options.delete(:kill)
|
73
|
+
Runner.daemonize(options) if options.delete(:daemonize)
|
74
|
+
|
75
|
+
# Fire up Mizuno as if it was called from Rackup.
|
76
|
+
Dir.chdir(options[:root])
|
77
|
+
HttpServer.configure_logging(options)
|
78
|
+
server = Rack::Server.new
|
79
|
+
server.options = options.merge(:server => 'mizuno',
|
80
|
+
:environment => options[:env])
|
81
|
+
server.start
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Relaunch as a daemon.
|
86
|
+
#
|
87
|
+
def Runner.daemonize(options)
|
88
|
+
# Ensure that Mizuno isn't running.
|
89
|
+
Runner.pid(options) and die("Mizuno is already running.")
|
90
|
+
|
91
|
+
# Build a command line that should launch JRuby with the
|
92
|
+
# appropriate options; this depends on the proper jruby
|
93
|
+
# being in the $PATH
|
94
|
+
config = options.delete(:config)
|
95
|
+
args = Mizuno::LAUNCH_ENV.concat(options.map { |k, v|
|
96
|
+
(v.to_s.empty?) ? nil : [ "--#{k}", v.to_s ] }.compact.flatten)
|
97
|
+
args.push(config)
|
98
|
+
args.unshift('jruby')
|
99
|
+
|
100
|
+
# Launch a detached child process.
|
101
|
+
child = ChildProcess.build(*args)
|
102
|
+
child.io.inherit!
|
103
|
+
child.detach = true
|
104
|
+
child.start
|
105
|
+
File.open(options[:pidfile], 'w') { |f| f.puts(child.pid) }
|
106
|
+
|
107
|
+
# Wait until the server starts or we time out waiting for it.
|
108
|
+
exit if wait_for_server(options)
|
109
|
+
child.stop
|
110
|
+
die("Failed to start Mizuno.")
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Return the status of a running daemon.
|
115
|
+
#
|
116
|
+
def Runner.status(options)
|
117
|
+
die("Mizuno doesn't appear to be running.") \
|
118
|
+
unless (pid = Runner.pid(options))
|
119
|
+
die("Mizuno is running, but not online.") \
|
120
|
+
unless(wait_for_server(options))
|
121
|
+
die("Mizuno is running.", true)
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Reload a running daemon by SIGHUPing it.
|
126
|
+
#
|
127
|
+
def Runner.reload(options)
|
128
|
+
pid = Runner.pid(options) or die("Mizuno isn't running.")
|
129
|
+
Process.kill("HUP", pid)
|
130
|
+
die("Mizuno signaled to reload app.", true)
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Stop a running daemon (SIGKILL)
|
135
|
+
#
|
136
|
+
def Runner.stop(options)
|
137
|
+
pid = Runner.pid(options) or die("Mizuno isn't running.")
|
138
|
+
print "Stopping Mizuno..."
|
139
|
+
Process.kill("KILL", pid)
|
140
|
+
die("failed") unless wait_for_server_to_die(options)
|
141
|
+
FileUtils.rm(options[:pidfile])
|
142
|
+
die("stopped", true)
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Really stop a running daemon (SIGTERM)
|
147
|
+
#
|
148
|
+
def Runner.kill(options)
|
149
|
+
pid = Runner.pid(options) or die("Mizuno isn't running.")
|
150
|
+
$stderr.puts "Terminating Mizuno with extreme prejudice..."
|
151
|
+
Process.kill("TERM", pid)
|
152
|
+
die("failed") unless wait_for_server_to_die(options)
|
153
|
+
FileUtils.rm(options[:pidfile])
|
154
|
+
die("stopped", true)
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# Transform a relative path to an absolute path.
|
159
|
+
#
|
160
|
+
def Runner.resolve_path(root, path)
|
161
|
+
return(path) unless path.is_a?(String)
|
162
|
+
return(path) if (path =~ /^\//)
|
163
|
+
File.expand_path(File.join(root, path))
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# Fetches the PID from the :pidfile.
|
168
|
+
#
|
169
|
+
def Runner.pid(options)
|
170
|
+
options[:pidfile] or die("Speficy a --pidfile to daemonize.")
|
171
|
+
return unless File.exists?(options[:pidfile])
|
172
|
+
pid = File.read(options[:pidfile]).to_i
|
173
|
+
|
174
|
+
# FIXME: This is a hacky way to get the process list, but I
|
175
|
+
# haven't found a good cross-platform solution yet; this
|
176
|
+
# should work on MacOS and Linux, possibly Solaris and BSD,
|
177
|
+
# and almost definitely not on Windows.
|
178
|
+
process = `ps ax`.lines.select { |l| l =~ /^\s*#{pid}\s*/ }
|
179
|
+
return(pid) if (process.join =~ /\bmizuno\b/)
|
180
|
+
|
181
|
+
# Stale pidfile; remove.
|
182
|
+
$stderr.puts("Removing stale pidfile '#{options[:pidfile]}'")
|
183
|
+
FileUtils.rm(options[:pidfile])
|
184
|
+
return(nil)
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# Wait until _timeout_ seconds for a successful http connection;
|
189
|
+
# returns true if we could connect and didn't get a server
|
190
|
+
# error, false otherwise.
|
191
|
+
#
|
192
|
+
def Runner.wait_for_server(options, timeout = 10)
|
193
|
+
begin
|
194
|
+
Net::HTTP.start(options[:host], options[:port]) do |http|
|
195
|
+
http.read_timeout = timeout
|
196
|
+
response = http.get("/")
|
197
|
+
return(response.code.to_i < 500)
|
198
|
+
end
|
199
|
+
rescue Errno::ECONNREFUSED => error
|
200
|
+
return(false) unless ((timeout -= 0.5) > 0)
|
201
|
+
sleep(0.5)
|
202
|
+
retry
|
203
|
+
rescue => error
|
204
|
+
puts "HTTP Error '#{error}'"
|
205
|
+
return(false)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Like wait_for_server, but returns true when the server goes
|
211
|
+
# offline. If we hit _timeout_ seconds and the server is still
|
212
|
+
# responding, returns false.
|
213
|
+
#
|
214
|
+
def Runner.wait_for_server_to_die(options, timeout = 10)
|
215
|
+
begin
|
216
|
+
while(timeout > 0)
|
217
|
+
Net::HTTP.start(options[:host], options[:port]) do |http|
|
218
|
+
http.read_timeout = timeout
|
219
|
+
response = http.get("/")
|
220
|
+
puts "**** (die) response: #{response}"
|
221
|
+
end
|
222
|
+
timeout -= 0.5
|
223
|
+
sleep(0.5)
|
224
|
+
end
|
225
|
+
return(false)
|
226
|
+
rescue Errno::ECONNREFUSED => error
|
227
|
+
return(true)
|
228
|
+
rescue => error
|
229
|
+
puts "**** http error: #{error}"
|
230
|
+
return(true)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
#
|
235
|
+
# Exit with a message and a status value.
|
236
|
+
#
|
237
|
+
# FIXME: Dump these in the logfile if called from HttpServer?
|
238
|
+
#
|
239
|
+
def Runner.die(message, success = false)
|
240
|
+
$stderr.puts(message)
|
241
|
+
exit(success ? 0 : 1)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
data/mizuno.gemspec
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
require 'lib/mizuno/version'
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
4
|
spec.name = "mizuno"
|
3
|
-
spec.version =
|
5
|
+
spec.version = Mizuno::VERSION
|
4
6
|
spec.required_rubygems_version = Gem::Requirement.new(">= 1.2") \
|
5
7
|
if spec.respond_to?(:required_rubygems_version=)
|
6
8
|
spec.authors = [ "Don Werve" ]
|
7
9
|
spec.description = 'Jetty-powered running shoes for JRuby/Rack.'
|
8
|
-
spec.summary = 'Rack handler for Jetty
|
10
|
+
spec.summary = 'Rack handler for Jetty 8 on JRuby. Features multithreading, event-driven I/O, and async support.'
|
9
11
|
spec.email = 'don@madwombat.com'
|
10
|
-
# FIXME: We're not getting put in bin/
|
11
12
|
spec.executables = [ "mizuno" ]
|
13
|
+
# FIXME: Use Dir.glob for this
|
12
14
|
spec.files = %w(.gitignore
|
13
15
|
README.markdown
|
14
16
|
LICENSE
|
@@ -16,8 +18,13 @@ Gem::Specification.new do |spec|
|
|
16
18
|
Gemfile
|
17
19
|
mizuno.gemspec
|
18
20
|
tmp/.gitkeep
|
21
|
+
lib/mizuno/choices.rb
|
19
22
|
lib/mizuno/http_server.rb
|
23
|
+
lib/mizuno/java_logger.rb
|
24
|
+
lib/mizuno/rack/chunked.rb
|
20
25
|
lib/mizuno/rack_servlet.rb
|
26
|
+
lib/mizuno/runner.rb
|
27
|
+
lib/mizuno/version.rb
|
21
28
|
lib/mizuno.rb
|
22
29
|
bin/mizuno)
|
23
30
|
jars = Dir.entries("lib/java").grep(/\.jar$/)
|
@@ -27,8 +34,11 @@ Gem::Specification.new do |spec|
|
|
27
34
|
spec.require_paths = [ "lib" ]
|
28
35
|
spec.rubygems_version = '1.3.6'
|
29
36
|
spec.add_dependency('rack', '>= 1.0.0')
|
37
|
+
spec.add_dependency('ffi', '>= 1.0.0')
|
38
|
+
spec.add_dependency('choice', '>= 0.1.0')
|
39
|
+
spec.add_dependency('childprocess', '>= 0.2.6')
|
30
40
|
spec.add_development_dependency('rspec', '>= 2.7.0')
|
31
41
|
spec.add_development_dependency('rspec-core', '>= 2.7.0')
|
32
|
-
spec.add_development_dependency('json_pure', '>= 1.6.
|
42
|
+
spec.add_development_dependency('json_pure', '>= 1.6.0')
|
33
43
|
spec.add_development_dependency('nokogiri')
|
34
44
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: mizuno
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Don Werve
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
14
|
-
default_executable:
|
13
|
+
date: 2012-01-08 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: rack
|
@@ -25,49 +24,82 @@ dependencies:
|
|
25
24
|
type: :runtime
|
26
25
|
version_requirements: *id001
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
27
|
+
name: ffi
|
29
28
|
prerelease: false
|
30
29
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: choice
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: childprocess
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.2.6
|
57
|
+
type: :runtime
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: rspec
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
31
63
|
none: false
|
32
64
|
requirements:
|
33
65
|
- - ">="
|
34
66
|
- !ruby/object:Gem::Version
|
35
67
|
version: 2.7.0
|
36
68
|
type: :development
|
37
|
-
version_requirements: *
|
69
|
+
version_requirements: *id005
|
38
70
|
- !ruby/object:Gem::Dependency
|
39
71
|
name: rspec-core
|
40
72
|
prerelease: false
|
41
|
-
requirement: &
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
42
74
|
none: false
|
43
75
|
requirements:
|
44
76
|
- - ">="
|
45
77
|
- !ruby/object:Gem::Version
|
46
78
|
version: 2.7.0
|
47
79
|
type: :development
|
48
|
-
version_requirements: *
|
80
|
+
version_requirements: *id006
|
49
81
|
- !ruby/object:Gem::Dependency
|
50
82
|
name: json_pure
|
51
83
|
prerelease: false
|
52
|
-
requirement: &
|
84
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
53
85
|
none: false
|
54
86
|
requirements:
|
55
87
|
- - ">="
|
56
88
|
- !ruby/object:Gem::Version
|
57
|
-
version: 1.6.
|
89
|
+
version: 1.6.0
|
58
90
|
type: :development
|
59
|
-
version_requirements: *
|
91
|
+
version_requirements: *id007
|
60
92
|
- !ruby/object:Gem::Dependency
|
61
93
|
name: nokogiri
|
62
94
|
prerelease: false
|
63
|
-
requirement: &
|
95
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
64
96
|
none: false
|
65
97
|
requirements:
|
66
98
|
- - ">="
|
67
99
|
- !ruby/object:Gem::Version
|
68
100
|
version: "0"
|
69
101
|
type: :development
|
70
|
-
version_requirements: *
|
102
|
+
version_requirements: *id008
|
71
103
|
description: Jetty-powered running shoes for JRuby/Rack.
|
72
104
|
email: don@madwombat.com
|
73
105
|
executables:
|
@@ -84,8 +116,13 @@ files:
|
|
84
116
|
- Gemfile
|
85
117
|
- mizuno.gemspec
|
86
118
|
- tmp/.gitkeep
|
119
|
+
- lib/mizuno/choices.rb
|
87
120
|
- lib/mizuno/http_server.rb
|
121
|
+
- lib/mizuno/java_logger.rb
|
122
|
+
- lib/mizuno/rack/chunked.rb
|
88
123
|
- lib/mizuno/rack_servlet.rb
|
124
|
+
- lib/mizuno/runner.rb
|
125
|
+
- lib/mizuno/version.rb
|
89
126
|
- lib/mizuno.rb
|
90
127
|
- bin/mizuno
|
91
128
|
- lib/java/jetty-continuation-8.0.4.v20111024.jar
|
@@ -97,8 +134,11 @@ files:
|
|
97
134
|
- lib/java/jetty-servlet-8.0.4.v20111024.jar
|
98
135
|
- lib/java/jetty-servlets-8.0.4.v20111024.jar
|
99
136
|
- lib/java/jetty-util-8.0.4.v20111024.jar
|
137
|
+
- lib/java/log4j-1.2.16.jar
|
138
|
+
- lib/java/rewindable-input-stream.jar
|
100
139
|
- lib/java/servlet-api-3.0.jar
|
101
|
-
|
140
|
+
- lib/java/slf4j-api-1.6.4.jar
|
141
|
+
- lib/java/slf4j-log4j12-1.6.4.jar
|
102
142
|
homepage: http://github.com/matadon/mizuno
|
103
143
|
licenses: []
|
104
144
|
|
@@ -122,9 +162,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
162
|
requirements: []
|
123
163
|
|
124
164
|
rubyforge_project:
|
125
|
-
rubygems_version: 1.
|
165
|
+
rubygems_version: 1.8.9
|
126
166
|
signing_key:
|
127
167
|
specification_version: 3
|
128
|
-
summary: Rack handler for Jetty
|
168
|
+
summary: Rack handler for Jetty 8 on JRuby. Features multithreading, event-driven I/O, and async support.
|
129
169
|
test_files: []
|
130
170
|
|