From 3fa9a5693d7417d75a8957cc0c8ff41347714fd1 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 22 Oct 2015 09:23:45 +0900 Subject: initial commit --- lib/plum/rack/server.rb | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 lib/plum/rack/server.rb (limited to 'lib/plum/rack/server.rb') diff --git a/lib/plum/rack/server.rb b/lib/plum/rack/server.rb new file mode 100644 index 0000000..15cb10a --- /dev/null +++ b/lib/plum/rack/server.rb @@ -0,0 +1,55 @@ +module Plum + module Rack + class Server + def initialize(app, config) + @state = :null + @app = app + @logger = Logger.new(config[:log] || $stdout).tap { |l| + l.level = config[:debug] ? Logger::DEBUG : Logger::INFO + } + @listeners = config[:listeners].map { |lc| + lc[:listener].new(lc) + } + + @logger.info("Plum::Rack #{::Plum::Rack::VERSION} (Plum #{::Plum::VERSION})") + @logger.info("Config: #{config}") + end + + def start + @state = :running + while @state == :running + break if @listeners.empty? + begin + if ss = IO.select(@listeners, nil, nil, 2.0) + ss[0].each { |svr| + new_con(svr) + } + end + rescue Errno::EBADF, Errno::ENOTSOCK, IOError => e # closed + @logger.debug("socket closed?: #{e}") + rescue StandardError => e + @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") + end + end + end + + def stop + @state = :stop + @listeners.map(&:stop) + end + + private + def new_con(svr) + sock = svr.accept + @logger.debug("accept: #{sock}") + + con = Connection.new(@app, sock, @logger) + con.start + rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed + @logger.debug("connection closed while accepting: #{e}") + rescue StandardError => e + @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") + end + end + end +end -- cgit v1.2.3 From b579a2e34e8cc2bb26250d954af6ed24fadeda45 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 22 Oct 2015 20:44:42 +0900 Subject: server: wrap the passed app with Rack::CommonLogger if debug mode --- lib/plum/rack/server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/plum/rack/server.rb') diff --git a/lib/plum/rack/server.rb b/lib/plum/rack/server.rb index 15cb10a..1e444c0 100644 --- a/lib/plum/rack/server.rb +++ b/lib/plum/rack/server.rb @@ -3,7 +3,7 @@ module Plum class Server def initialize(app, config) @state = :null - @app = app + @app = config[:debug] ? ::Rack::CommonLogger.new(app) : app @logger = Logger.new(config[:log] || $stdout).tap { |l| l.level = config[:debug] ? Logger::DEBUG : Logger::INFO } -- cgit v1.2.3 From c35bd14ca3d8d892d1c7509c57a2f429acce9e23 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 22 Oct 2015 20:44:52 +0900 Subject: reduce debug logging --- lib/plum/rack/connection.rb | 3 --- lib/plum/rack/server.rb | 2 -- 2 files changed, 5 deletions(-) (limited to 'lib/plum/rack/server.rb') diff --git a/lib/plum/rack/connection.rb b/lib/plum/rack/connection.rb index cae7080..f84ec5b 100644 --- a/lib/plum/rack/connection.rb +++ b/lib/plum/rack/connection.rb @@ -19,7 +19,6 @@ module Plum @plum = setup_plum @plum.run rescue Errno::EPIPE, Errno::ECONNRESET => e - @logger.debug("connection closed: #{e}") rescue StandardError => e @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") end @@ -41,12 +40,10 @@ module Plum } stream.on(:headers) { |h| - @logger.debug("headers: " + h.map {|name, value| "#{name}: #{value}" }.join(" // ")) headers = h } stream.on(:data) { |d| - @logger.debug("data: #{d.bytesize}") data << d # TODO: store to file? } diff --git a/lib/plum/rack/server.rb b/lib/plum/rack/server.rb index 1e444c0..f942df4 100644 --- a/lib/plum/rack/server.rb +++ b/lib/plum/rack/server.rb @@ -26,7 +26,6 @@ module Plum } end rescue Errno::EBADF, Errno::ENOTSOCK, IOError => e # closed - @logger.debug("socket closed?: #{e}") rescue StandardError => e @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") end @@ -46,7 +45,6 @@ module Plum con = Connection.new(@app, sock, @logger) con.start rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed - @logger.debug("connection closed while accepting: #{e}") rescue StandardError => e @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") end -- cgit v1.2.3 From b6de76aa184bae4637fa4c1beb6bf296d97c7679 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 23 Oct 2015 09:59:39 +0900 Subject: set start_immediately=false for SSLServer --- lib/plum/rack/connection.rb | 46 ++++++++++++++++++++++----------------------- lib/plum/rack/listener.rb | 30 ++++++++--------------------- lib/plum/rack/server.rb | 19 +++++++++++++++---- lib/rack/handler/plum.rb | 5 +++-- 4 files changed, 49 insertions(+), 51 deletions(-) (limited to 'lib/plum/rack/server.rb') diff --git a/lib/plum/rack/connection.rb b/lib/plum/rack/connection.rb index 23314bd..52e7117 100644 --- a/lib/plum/rack/connection.rb +++ b/lib/plum/rack/connection.rb @@ -1,37 +1,35 @@ module Plum module Rack class Connection - attr_reader :app, :listener, :plum + attr_reader :app, :sock, :plum - def initialize(app, listener, logger) + def initialize(app, plum, logger) @app = app - @listener = listener + @plum = plum @logger = logger + + setup_plum end def stop - @listener.close # TODO: gracefully shutdown + @plum.stop end - def start - Thread.new { - begin - @plum = setup_plum - @plum.run - rescue Errno::EPIPE, Errno::ECONNRESET => e - @logger.debug("connection closed: #{e}") - rescue StandardError => e - @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") - end - } + def run + begin + @plum.run + rescue Errno::EPIPE, Errno::ECONNRESET => e + @logger.debug("connection closed: #{e}") + rescue StandardError => e + @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") + end end private def setup_plum - plum = @listener.plum - plum.on(:connection_error) { |ex| @logger.error(ex) } + @plum.on(:connection_error) { |ex| @logger.error(ex) } - plum.on(:stream) do |stream| + @plum.on(:stream) do |stream| stream.on(:stream_error) { |ex| @logger.error(ex) } headers = data = nil @@ -56,17 +54,19 @@ module Plum if r_body.is_a?(::Rack::BodyProxy) stream.respond(r_headers, end_stream: false) - r_body.each { |part| - stream.send_data(part, end_stream: false) - } + begin + r_body.each { |part| + stream.send_data(part, end_stream: false) + } + ensure + r_body.close + end stream.send_data(nil) else stream.respond(r_headers, r_body) end } end - - plum end def new_env(h, data) diff --git a/lib/plum/rack/listener.rb b/lib/plum/rack/listener.rb index d39aad1..87d6179 100644 --- a/lib/plum/rack/listener.rb +++ b/lib/plum/rack/listener.rb @@ -9,17 +9,8 @@ module Plum raise "not implemented" end - def accept - @sock = to_io.accept - self - end - def method_missing(name, *args) - if @sock - @sock.__send__(name, *args) - else - @server.__send__(name, *args) - end + @server.__send__(name, *args) end end @@ -32,8 +23,8 @@ module Plum @server.to_io end - def plum - ::Plum::HTTPConnection.new(self) + def plum(sock) + ::Plum::HTTPConnection.new(sock) end end @@ -56,20 +47,15 @@ module Plum ctx.key = OpenSSL::PKey::RSA.new(key) tcp_server = ::TCPServer.new(lc[:hostname], lc[:port]) @server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx) - @server.start_immediately = true # TODO + @server.start_immediately = false end def to_io @server.to_io end - def accept - @sock = @server.accept - self - end - - def plum - ::Plum::HTTPSConnection.new(self) + def plum(sock) + ::Plum::HTTPSConnection.new(sock) end private @@ -111,8 +97,8 @@ module Plum @server.to_io end - def plum - ::Plum::HTTPConnection.new(self) + def plum(sock) + ::Plum::HTTPSConnection.new(sock) end end end diff --git a/lib/plum/rack/server.rb b/lib/plum/rack/server.rb index 15cb10a..2a72d6e 100644 --- a/lib/plum/rack/server.rb +++ b/lib/plum/rack/server.rb @@ -36,17 +36,28 @@ module Plum def stop @state = :stop @listeners.map(&:stop) + # TODO: gracefully shutdown connections end private def new_con(svr) sock = svr.accept - @logger.debug("accept: #{sock}") + Thread.new { + begin + sock = sock.accept if sock.respond_to?(:accept) + plum = svr.plum(sock) + @logger.debug("accept: #{plum}") - con = Connection.new(@app, sock, @logger) - con.start + con = Connection.new(@app, plum, @logger) + con.run + rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed + sock.close if sock + rescue StandardError => e + @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") + sock.close if sock + end + } rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed - @logger.debug("connection closed while accepting: #{e}") rescue StandardError => e @logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}") end diff --git a/lib/rack/handler/plum.rb b/lib/rack/handler/plum.rb index 1052592..c3ca543 100644 --- a/lib/rack/handler/plum.rb +++ b/lib/rack/handler/plum.rb @@ -31,10 +31,11 @@ module Rack private def self.default_options rack_env = ENV["RACK_ENV"] || "development" + dev = rack_env == "development" default_options = { - Host: rack_env == "development" ? "localhost" : "0.0.0.0", + Host: dev ? "localhost" : "0.0.0.0", Port: 8080, - Debug: true, + Debug: dev, } end end -- cgit v1.2.3 From 7e998a7ae6826ebf2a50aa763b4d84f0082917ec Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 25 Oct 2015 18:47:37 +0900 Subject: better performance --- examples/rack.ru | 2 +- lib/plum/rack/connection.rb | 88 ++++++++++++++++++++++----------------------- lib/plum/rack/server.rb | 8 +++-- 3 files changed, 49 insertions(+), 49 deletions(-) (limited to 'lib/plum/rack/server.rb') diff --git a/examples/rack.ru b/examples/rack.ru index 7558c80..67462e9 100644 --- a/examples/rack.ru +++ b/examples/rack.ru @@ -7,7 +7,7 @@ class App2 [ 200, { "Content-Type" => "text/html" }, - ["Hello World!"] + ["*10 bytes*"*400] ] else [ diff --git a/lib/plum/rack/connection.rb b/lib/plum/rack/connection.rb index 400a098..45cde79 100644 --- a/lib/plum/rack/connection.rb +++ b/lib/plum/rack/connection.rb @@ -1,7 +1,7 @@ module Plum module Rack class Connection - attr_reader :app, :sock, :plum + attr_reader :app, :plum def initialize(app, plum, logger) @app = app @@ -12,7 +12,7 @@ module Plum end def stop - @plum.stop + @plum.close end def run @@ -28,47 +28,43 @@ module Plum def setup_plum @plum.on(:connection_error) { |ex| @logger.error(ex) } - @plum.on(:stream) do |stream| - stream.on(:stream_error) { |ex| @logger.error(ex) } + # @plum.on(:stream) { |stream| @logger.debug("new stream: #{stream}") } + @plum.on(:stream_error) { |stream, ex| @logger.error(ex) } - headers = data = nil - stream.on(:open) { - headers = nil - data = "".force_encoding(Encoding::BINARY) - } - - stream.on(:headers) { |h| - headers = h - } + reqs = {} + @plum.on(:headers) { |stream, h| + reqs[stream] = { headers: h, data: "".force_encoding(Encoding::BINARY) } + } - stream.on(:data) { |d| - data << d # TODO: store to file? - } + @plum.on(:data) { |stream, d| + reqs[stream][:data] << d # TODO: store to file? + } - stream.on(:end_stream) { - handle_request(stream, headers, data) - } - end + @plum.on(:end_stream) { |stream| + handle_request(stream, reqs[stream][:headers], reqs[stream][:data]) + } end def send_body(stream, body) - if body.is_a?(::Rack::BodyProxy) - begin - body.each { |part| - stream.send_data(part, end_stream: false) + begin + if body.is_a?(Array) + last = body.size - 1 + body.each_with_index { |part, i| + stream.send_data(part, end_stream: last == i) } - ensure - body.close + elsif body.is_a?(IO) + stream.send_data(body, end_stream: true) + else + body.each { |part| stream.send_data(part, end_stream: false) } + stream.send_data(nil, end_stream: true) end - stream.send_data(nil, end_stream: true) - else - stream.send_data(body, end_stream: true) + ensure + body.close if body.respond_to?(:close) end end - def extract_push(r_rawheaders) - _, pushs = r_rawheaders.find { |k, v| k == "plum.serverpush" } - if pushs + def extract_push(r_extheaders) + if pushs = r_extheaders["plum.serverpush"] pushs.split(";").map { |push| push.split(" ", 2) } else [] @@ -78,8 +74,8 @@ module Plum def handle_request(stream, headers, data) env = new_env(headers, data) r_status, r_rawheaders, r_body = @app.call(env) - r_headers = extract_headers(r_status, r_rawheaders) - r_topushs = extract_push(r_rawheaders) + r_headers, r_extheaders = extract_headers(r_status, r_rawheaders) + r_topushs = extract_push(r_extheaders) stream.send_headers(r_headers, end_stream: false) r_pushstreams = r_topushs.map { |method, path| @@ -132,7 +128,7 @@ module Plum if k.start_with?(":") # unknown HTTP/2 pseudo-headers else - if "cookie" == k && headers["HTTP_COOKIE"] + if "cookie" == k && ebase["HTTP_COOKIE"] ebase["HTTP_COOKIE"] << "; " << v else ebase["HTTP_" << k.tr("-", "_").upcase!] = v @@ -149,22 +145,24 @@ module Plum ":status" => r_status, "server" => "plum/#{::Plum::VERSION}", } + rext = {} r_h.each do |key, v_| - if key.start_with?("rack.") - next - end - - key = key.downcase - if "set-cookie".freeze == key - rbase[key] = v_.gsub("\n", "; ") # RFC 7540 8.1.2.5 + if key.include?(".") + rext[key] = v_ else - key = key.byteshift(2) if key.start_with?("x-") - rbase[key] = v_.tr("\n", ",") # RFC 7230 7 + key = key.downcase + + if "set-cookie" == key + rbase[key] = v_.gsub("\n", "; ") # RFC 7540 8.1.2.5 + else + key = key.byteshift(2) if key.start_with?("x-") + rbase[key] = v_.tr("\n", ",") # RFC 7230 7 + end end end - rbase + [rbase, rext] end end end diff --git a/lib/plum/rack/server.rb b/lib/plum/rack/server.rb index 496a375..8dda95e 100644 --- a/lib/plum/rack/server.rb +++ b/lib/plum/rack/server.rb @@ -45,10 +45,12 @@ module Plum begin sock = sock.accept if sock.respond_to?(:accept) plum = svr.plum(sock) - @logger.debug("accept: #{plum}") - con = Connection.new(@app, plum, @logger) - con.run + #require "lineprof" + #Lineprof.profile(/plum/) { + con = Connection.new(@app, plum, @logger) + con.run + #} rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed sock.close if sock rescue StandardError => e -- cgit v1.2.3