Class | Bcat::Server |
In: |
lib/bcat/server.rb
|
Parent: | Object |
Simple Rack handler based largely on Scott Chacon‘s kidgloves library: github.com/schacon/kidgloves
app | [RW] |
# File lib/bcat/server.rb, line 15 15: def initialize(app, options={}) 16: @app = app 17: @host = options[:Host] || '0.0.0.0' 18: @port = options[:Port] || 8089 19: end
# File lib/bcat/server.rb, line 11 11: def self.run(app, options={}, &block) 12: new(app, options).listen(&block) 13: end
# File lib/bcat/server.rb, line 21 21: def bind(host, port) 22: TCPServer.new(host, port) 23: rescue Errno::EADDRINUSE 24: port += 1 25: retry 26: end
# File lib/bcat/server.rb, line 28 28: def listen 29: server = TCPServer.new(@host, @port) 30: 31: yield server if block_given? 32: 33: loop do 34: socket = server.accept 35: socket.sync = true 36: log "#{socket.peeraddr[2]} (#{socket.peeraddr[3]})" 37: begin 38: req = {} 39: 40: # parse the request line 41: request = socket.gets 42: method, path, version = request.split(" ", 3) 43: req["REQUEST_METHOD"] = method 44: info, query = path.split("?") 45: req["PATH_INFO"] = info 46: req["QUERY_STRING"] = query 47: 48: # parse the headers 49: while (line = socket.gets) 50: line.strip! 51: break if line.size == 0 52: key, val = line.split(": ") 53: key = key.upcase.gsub('-', '_') 54: key = "HTTP_#{key}" if !%w[CONTENT_TYPE CONTENT_LENGTH].include?(key) 55: req[key] = val 56: end 57: 58: # parse the body 59: body = 60: if len = req['CONTENT_LENGTH'] 61: socket.read(len.to_i) 62: else 63: '' 64: end 65: 66: # process the request 67: process_request(req, body, socket) 68: ensure 69: socket.close if not socket.closed? 70: end 71: end 72: end
# File lib/bcat/server.rb, line 82 82: def process_request(request, input_body, socket) 83: env = {}.replace(request) 84: env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] 85: env["QUERY_STRING"] ||= "" 86: env["SCRIPT_NAME"] = "" 87: 88: rack_input = StringIO.new(input_body) 89: rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding) 90: 91: env.update( 92: "rack.version" => [1,0], 93: "rack.input" => rack_input, 94: "rack.errors" => $stderr, 95: "rack.multithread" => true, 96: "rack.multiprocess" => true, 97: "rack.run_once" => false, 98: "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" 99: ) 100: status, headers, body = app.call(env) 101: begin 102: socket.write("HTTP/1.1 #{status} #{status_message(status)}\r\n") 103: headers.each do |k, vs| 104: vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} 105: end 106: socket.write("\r\n") 107: body.each { |s| socket.write(s) } 108: ensure 109: body.close if body.respond_to? :close 110: end 111: end