Class: Bcat::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/bcat/server.rb

Overview

Simple Rack handler based largely on Scott Chacon’s kidgloves library: github.com/schacon/kidgloves

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Server

Returns a new instance of Server.



15
16
17
18
19
# File 'lib/bcat/server.rb', line 15

def initialize(app, options={})
  @app = app
  @host = options[:Host] || '0.0.0.0'
  @port = options[:Port] || 8089
end

Instance Attribute Details

#appObject

Returns the value of attribute app.



9
10
11
# File 'lib/bcat/server.rb', line 9

def app
  @app
end

Class Method Details

.run(app, options = {}, &block) ⇒ Object



11
12
13
# File 'lib/bcat/server.rb', line 11

def self.run(app, options={}, &block)
  new(app, options).listen(&block)
end

Instance Method Details

#bind(host, port) ⇒ Object



21
22
23
24
25
26
# File 'lib/bcat/server.rb', line 21

def bind(host, port)
  TCPServer.new(host, port)
rescue Errno::EADDRINUSE
  port += 1
  retry
end

#listen {|server| ... } ⇒ Object

Yields:

  • (server)


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
69
70
71
72
# File 'lib/bcat/server.rb', line 28

def listen
  server = TCPServer.new(@host, @port)

  yield server if block_given?

  loop do
    socket = server.accept
    socket.sync = true
    log "#{socket.peeraddr[2]} (#{socket.peeraddr[3]})"
    begin
      req = {}

      # parse the request line
      request = socket.gets
      method, path, version = request.split(" ", 3)
      req["REQUEST_METHOD"] = method
      info, query = path.split("?")
      req["PATH_INFO"] = info
      req["QUERY_STRING"] = query

      # parse the headers
      while (line = socket.gets)
        line.strip!
        break if line.size == 0
        key, val = line.split(": ")
        key = key.upcase.gsub('-', '_')
        key = "HTTP_#{key}" if !%w[CONTENT_TYPE CONTENT_LENGTH].include?(key)
        req[key] = val
      end

      # parse the body
      body =
        if len = req['CONTENT_LENGTH']
          socket.read(len.to_i)
        else
          ''
        end

      # process the request
      process_request(req, body, socket)
    ensure
      socket.close if not socket.closed?
    end
  end
end

#log(message) ⇒ Object



74
75
76
# File 'lib/bcat/server.rb', line 74

def log(message)
  # $stderr.puts message
end

#process_request(request, input_body, socket) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/bcat/server.rb', line 82

def process_request(request, input_body, socket)
  env = {}.replace(request)
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
  env["QUERY_STRING"] ||= ""
  env["SCRIPT_NAME"] = ""

  rack_input = StringIO.new(input_body)
  rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)

  env.update(
    "rack.version"      => [1,0],
    "rack.input"        => rack_input,
    "rack.errors"       => $stderr,
    "rack.multithread"  => true,
    "rack.multiprocess" => true,
    "rack.run_once"     => false,
    "rack.url_scheme"   => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
  )
  status, headers, body = app.call(env)
  begin
    socket.write("HTTP/1.1 #{status} #{status_message(status)}\r\n")
    headers.each do |k, vs|
      vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")}
    end
    socket.write("\r\n")
    body.each { |s| socket.write(s) }
  ensure
    body.close if body.respond_to? :close
  end
end

#status_message(code) ⇒ Object



78
79
80
# File 'lib/bcat/server.rb', line 78

def status_message(code)
  Rack::Utils::HTTP_STATUS_CODES[code]
end