class Soba::Server
Attributes
app[R]
host[R]
logger[R]
port[R]
Public Class Methods
new(app, host:, port:, **options)
click to toggle source
# File lib/soba/server.rb, line 16 def initialize(app, host:, port:, **options) @app = app @host, @port = host, port @server = nil @logger = Logger.new(STDOUT, level: is_true?(options[:debug]) ? Logger::DEBUG : Logger::INFO) @options = options end
Public Instance Methods
error_stream()
click to toggle source
# File lib/soba/server.rb, line 10 def error_stream STDERR end
process_request(socket)
click to toggle source
# File lib/soba/server.rb, line 55 def process_request(socket) parser = Parser.new(socket) env = parser.env.merge(rack_env) env["rack.url_scheme"] = parser.request_schema env["rack.input"] = parser.body env["rack.errors"] = error_stream env["SERVER_NAME"] = env[HTTP_HOST].split(":")[0] keep_alive = env[HTTP_CONNECTION].to_s.downcase == KEEP_ALIVE begin status, headers, res_body = app.call(env) logger.debug("response: #{status} #{headers.inspect} #{res_body.inspect}") rescue Exception => e status = 500 headers = {'Content-Type' => 'text/plain'} logger.error("Internal Server Error: #{e}:\n#{e.backtrace.join("\n")}") end nobody = parser.env[REQUEST_METHOD] == HEAD || STATUS_WITH_NO_ENTITY_BODY[status] outbuf = '' status_line = "#{parser.server_protocol} #{status} #{status_text(status)}\n" outbuf << status_line set_content_length = false headers.each do |header, vs| case header.downcase when CONTENT_LENGTH2, CONTENT_LENGTH set_content_length = true end outbuf << "#{header}#{COLON}#{vs}#{LINE_END}" end chunked = !set_content_length outbuf << TRANSFER_ENCODING_CHUNKED if chunked connection_header = keep_alive ? CONNECTION_KEEP_ALIVE : CONNECTION_CLOSE outbuf << connection_header outbuf << "\n" if nobody socket << outbuf socket.flush return end res_body.each do |part| if chunked next if part.bytesize.zero? outbuf << part.bytesize.to_s(16) outbuf << LINE_END outbuf << part outbuf << LINE_END else outbuf << part end end if chunked outbuf << CLOSE_CHUNKED end # write is very slow, don't know why socket << outbuf socket.flush res_body.close if res_body.respond_to?(:close) ensure socket.close unless socket.closed? || keep_alive end
rack_env()
click to toggle source
# File lib/soba/server.rb, line 41 def rack_env @rack_env ||= { "rack.version" => Rack::VERSION, "rack.multithread" => true, "rack.multiprocess" => false, "rack.run_once" => false, "rack.hijack?" => false, "rack.hijack" => nil, "rack.hijack_io" => nil, "rack.logger" => logger, "SERVER_PORT" => port.to_s, } end
run()
click to toggle source
# File lib/soba/server.rb, line 24 def run setup while (socket = @server.accept) _, port, host = socket.peeraddr logger.debug "accept connection from #{host}:#{port}" LightIO::Beam.new(socket) do |socket| begin process_request(socket) until socket.closed? rescue StandardError => e logger.info("Exception: #{e}") raise end end end end
Private Instance Methods
is_true?(v)
click to toggle source
# File lib/soba/server.rb, line 145 def is_true?(v) %w{true on 1}.include?(v.to_s) end
setup()
click to toggle source
# File lib/soba/server.rb, line 130 def setup monkey_patch = LightIO::Monkey.patched?(IO) logger.info "Soba #{Soba::VERSION}" logger.info "ruby #{RUBY_VERSION}" if monkey_patch logger.info "Run in Green thread(monkey patch) mode, engine: #{NIO.engine}, see https://github.com/socketry/lightio" logger.info "Current backend: #{LightIO::IOloop.current.backend}, available backends: #{NIO::Selector.backends} (set `LIGHTIO_BACKEND` env to choose)" else logger.info "Run in normal mode" end logger.info "Server start listen #{host}:#{port}" @server = LightIO::TCPServer.new(host, port) end
status_text(status)
click to toggle source
# File lib/soba/server.rb, line 126 def status_text(status) HTTP_STATUS_CODES[status.to_i] end