Permalink
Browse files

Refactor come parts between normal and cluster mode

  • Loading branch information...
evanphx committed Aug 2, 2012
1 parent dad69f5 commit 508b235f543db59fbace59d822861cce62edc1c0
Showing with 339 additions and 263 deletions.
  1. +23 −0 lib/puma/accept_nonblock.rb
  2. +234 −0 lib/puma/binder.rb
  3. +13 −104 lib/puma/cli.rb
  4. +43 −51 lib/puma/cluster_cli.rb
  5. +11 −0 lib/puma/delegation.rb
  6. +15 −108 lib/puma/server.rb
@@ -0,0 +1,23 @@
+require 'openssl'
+
+module OpenSSL
+ module SSL
+ if RUBY_VERSION < "1.9"
+ class SSLServer
+ def accept_nonblock
+ sock = @svr.accept_nonblock
+
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
+ ssl.sync_close = true
+ ssl.accept if @start_immediately
+ ssl
+ rescue SSLError => ex
+ sock.close
+ raise ex
+ end
+ end
+ end
+ end
+ end
+end
View
@@ -0,0 +1,234 @@
+require 'puma/const'
+
+module Puma
+ class Binder
+ include Puma::Const
+
+ def initialize(events)
+ @events = events
+ @listeners = []
+ @inherited_fds = {}
+ @unix_paths = []
+
+ @proto_env = {
+ "rack.version".freeze => Rack::VERSION,
+ "rack.errors".freeze => events.stderr,
+ "rack.multithread".freeze => true,
+ "rack.multiprocess".freeze => false,
+ "rack.run_once".freeze => true,
+ "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
+
+ # Rack blows up if this is an empty string, and Rack::Lint
+ # blows up if it's nil. So 'text/plain' seems like the most
+ # sensible default value.
+ "CONTENT_TYPE".freeze => "text/plain",
+
+ "QUERY_STRING".freeze => "",
+ SERVER_PROTOCOL => HTTP_11,
+ SERVER_SOFTWARE => PUMA_VERSION,
+ GATEWAY_INTERFACE => CGI_VER
+ }
+
+ @envs = {}
+ @ios = []
+ end
+
+ attr_reader :listeners, :ios
+
+ def env(sock)
+ @envs.fetch(sock, @proto_env)
+ end
+
+ def close
+ @ios.each { |i| i.close }
+ @unix_paths.each { |i| File.unlink i }
+ end
+
+ def import_from_env
+ remove = []
+
+ ENV.each do |k,v|
+ if k =~ /PUMA_INHERIT_\d+/
+ fd, url = v.split(":", 2)
+ @inherited_fds[url] = fd.to_i
+ remove << k
+ end
+ end
+
+ remove.each do |k|
+ ENV.delete k
+ end
+ end
+
+ def parse(binds, logger)
+ binds.each do |str|
+ uri = URI.parse str
+ case uri.scheme
+ when "tcp"
+ if fd = @inherited_fds.delete(str)
+ logger.log "* Inherited #{str}"
+ io = inherit_tcp_listener uri.host, uri.port, fd
+ else
+ logger.log "* Listening on #{str}"
+ io = add_tcp_listener uri.host, uri.port
+ end
+
+ @listeners << [str, io]
+ when "unix"
+ if fd = @inherited_fds.delete(str)
+ logger.log "* Inherited #{str}"
+ io = inherit_unix_listener uri.path, fd
+ else
+ logger.log "* Listening on #{str}"
+ path = "#{uri.host}#{uri.path}"
+
+ umask = nil
+
+ if uri.query
+ params = Rack::Utils.parse_query uri.query
+ if u = params['umask']
+ # Use Integer() to respect the 0 prefix as octal
+ umask = Integer(u)
+ end
+ end
+
+ io = add_unix_listener path, umask
+ end
+
+ @listeners << [str, io]
+ when "ssl"
+ params = Rack::Utils.parse_query uri.query
+ require 'openssl'
+
+ ctx = OpenSSL::SSL::SSLContext.new
+ unless params['key']
+ logger.error "Please specify the SSL key via 'key='"
+ end
+
+ ctx.key = OpenSSL::PKey::RSA.new File.read(params['key'])
+
+ unless params['cert']
+ logger.error "Please specify the SSL cert via 'cert='"
+ end
+
+ ctx.cert = OpenSSL::X509::Certificate.new File.read(params['cert'])
+
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+
+ if fd = @inherited_fds.delete(str)
+ logger.log "* Inherited #{str}"
+ io = inherited_ssl_listener fd, ctx
+ else
+ logger.log "* Listening on #{str}"
+ io = add_ssl_listener uri.host, uri.port, ctx
+ end
+
+ @listeners << [str, io]
+ else
+ logger.error "Invalid URI: #{str}"
+ end
+ end
+
+ # If we inherited fds but didn't use them (because of a
+ # configuration change), then be sure to close them.
+ @inherited_fds.each do |str, fd|
+ logger.log "* Closing unused inherited connection: #{str}"
+
+ begin
+ IO.for_fd(fd).close
+ rescue SystemCallError
+ end
+
+ # We have to unlink a unix socket path that's not being used
+ uri = URI.parse str
+ if uri.scheme == "unix"
+ path = "#{uri.host}#{uri.path}"
+ File.unlink path
+ end
+ end
+
+ end
+
+ # Tell the server to listen on host +host+, port +port+.
+ # If +optimize_for_latency+ is true (the default) then clients connecting
+ # will be optimized for latency over throughput.
+ #
+ # +backlog+ indicates how many unaccepted connections the kernel should
+ # allow to accumulate before returning connection refused.
+ #
+ def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
+ s = TCPServer.new(host, port)
+ if optimize_for_latency
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ end
+ s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
+ s.listen backlog
+ @ios << s
+ s
+ end
+
+ def inherit_tcp_listener(host, port, fd)
+ if fd.kind_of? TCPServer
+ s = fd
+ else
+ s = TCPServer.for_fd(fd)
+ end
+
+ @ios << s
+ s
+ end
+
+ def add_ssl_listener(host, port, ctx,
+ optimize_for_latency=true, backlog=1024)
+ s = TCPServer.new(host, port)
+ if optimize_for_latency
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ end
+ s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
+ s.listen backlog
+
+ ssl = OpenSSL::SSL::SSLServer.new(s, ctx)
+ env = @proto_env.dup
+ env[HTTPS_KEY] = HTTPS
+ @envs[ssl] = env
+
+ @ios << ssl
+ s
+ end
+
+ def inherited_ssl_listener(fd, ctx)
+ s = TCPServer.for_fd(fd)
+ @ios << OpenSSL::SSL::SSLServer.new(s, ctx)
+ s
+ end
+
+ # Tell the server to listen on +path+ as a UNIX domain socket.
+ #
+ def add_unix_listener(path, umask=nil)
+ @unix_paths << path
+
+ # Let anyone connect by default
+ umask ||= 0
+
+ begin
+ old_mask = File.umask(umask)
+ s = UNIXServer.new(path)
+ @ios << s
+ ensure
+ File.umask old_mask
+ end
+
+ s
+ end
+
+ def inherit_unix_listener(path, fd)
+ @unix_paths << path
+
+ s = UNIXServer.for_fd fd
+ @ios << s
+
+ s
+ end
+
+ end
+end
Oops, something went wrong.

0 comments on commit 508b235

Please sign in to comment.