1 # Ruby Binding to the Ebb Web Server
2 # Copyright (c) 2008 Ry Dahl. This software is released under the MIT License.
3 # See README file for details.
5 LIBDIR = File.dirname(__FILE__)
6 require Ebb::LIBDIR + '/../src/ebb_ext'
7 autoload :Runner, LIBDIR + '/ebb/runner'
9 def self.start_server(app, options={})
10 port = (options[:port] || 4001).to_i
11 if options.has_key?(:threaded_processing)
12 threaded_processing = options[:threaded_processing] ? true : false
14 threaded_processing = true
17 Client::BASE_ENV['rack.multithread'] = threaded_processing
19 FFI::server_listen_on_port(port)
21 puts "Ebb listening at http://0.0.0.0:#{port}/ (#{threaded_processing ? 'threaded' : 'sequential'} processing)"
22 trap('INT') { @running = false }
26 FFI::server_process_connections()
27 while client = FFI::waiting_clients.shift
28 if threaded_processing
29 Thread.new(client) { |c| c.process(app) }
36 puts "Ebb unlistening"
37 FFI::server_unlisten()
40 # This array is created and manipulated in the C extension.
41 def FFI.waiting_clients
47 'SERVER_NAME' => '0.0.0.0',
49 'SERVER_SOFTWARE' => "Ebb #{Ebb::VERSION}",
50 'SERVER_PROTOCOL' => 'HTTP/1.1',
51 'rack.version' => [0, 1],
52 'rack.errors' => STDERR,
53 'rack.url_scheme' => 'http',
54 'rack.multiprocess' => false,
55 'rack.run_once' => false
60 status, headers, body = app.call(env)
64 headers = {'Content-Type' => 'text/plain'}
65 body = "Internal Server Error\n"
69 FFI::client_write_status(self, status, HTTP_STATUS_CODES[status])
71 if headers.respond_to?(:[]=) and body.respond_to?(:length) and status != 304
72 headers['Connection'] = 'close'
73 headers['Content-Length'] = body.length.to_s
76 headers.each { |field, value| write_header(field, value) }
79 if body.kind_of?(String)
85 body.each { |p| write(p) }
89 puts "Error! #{e.class} #{e.message}"
91 FFI::client_release(self)
97 env = FFI::client_env(self).update(BASE_ENV)
98 env['rack.input'] = RequestBody.new(self)
103 FFI::client_write(self, data)
106 def write_header(field, value)
107 value.send(value.is_a?(String) ? :each_line : :each) do |v|
108 FFI::client_write_header(self, field, v.chomp)
113 FFI::client_set_body_written(self, true)
116 def begin_transmission
117 FFI::client_begin_transmission(self)
122 def initialize(client)
127 FFI::client_read_input(@client, len)
131 raise NotImplementedError
135 raise NotImplementedError
139 HTTP_STATUS_CODES = {
141 101 => 'Switching Protocols',
145 203 => 'Non-Authoritative Information',
147 205 => 'Reset Content',
148 206 => 'Partial Content',
149 300 => 'Multiple Choices',
150 301 => 'Moved Permanently',
151 302 => 'Moved Temporarily',
153 304 => 'Not Modified',
155 400 => 'Bad Request',
156 401 => 'Unauthorized',
157 402 => 'Payment Required',
160 405 => 'Method Not Allowed',
161 406 => 'Not Acceptable',
162 407 => 'Proxy Authentication Required',
163 408 => 'Request Time-out',
166 411 => 'Length Required',
167 412 => 'Precondition Failed',
168 413 => 'Request Entity Too Large',
169 414 => 'Request-URI Too Large',
170 415 => 'Unsupported Media Type',
171 500 => 'Internal Server Error',
172 501 => 'Not Implemented',
173 502 => 'Bad Gateway',
174 503 => 'Service Unavailable',
175 504 => 'Gateway Time-out',
176 505 => 'HTTP Version not supported'