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 VERSION = File.read(LIBDIR + "/../VERSION").gsub(/\s/,'')
7 require Ebb::LIBDIR + '/../src/ebb_ext'
8 autoload :Runner, LIBDIR + '/ebb/runner'
10 def self.start_server(app, options={})
11 port = (options[:port] || 4001).to_i
12 if options.has_key?(:threaded_processing)
13 threaded_processing = options[:threaded_processing] ? true : false
15 threaded_processing = true
18 Client::BASE_ENV['rack.multithread'] = threaded_processing
20 FFI::server_listen_on_port(port)
22 puts "Ebb listening at http://0.0.0.0:#{port}/ (#{threaded_processing ? 'threaded' : 'sequential'} processing)"
23 trap('INT') { @running = false }
27 FFI::server_process_connections()
28 while client = FFI::waiting_clients.shift
29 if threaded_processing
30 Thread.new(client) { |c| c.process(app) }
37 puts "Ebb unlistening"
38 FFI::server_unlisten()
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.multithread' => true,
55 'rack.multiprocess' => false,
56 'rack.run_once' => false
60 #puts "Request: #{client.inspect}\n"
62 status, headers, body = app.call(env)
66 headers = {'Content-Type' => 'text/plain'}
67 body = "Internal Server Error\n"
70 FFI::client_write_status(self, status.to_i, HTTP_STATUS_CODES[status.to_i])
72 if body.respond_to? :length and status != 304
73 headers['Connection'] = 'close'
74 headers['Content-Length'] = body.length
77 headers.each { |k, v| write_header(k,v) }
81 # Not many apps use streaming yet so i'll hold off on that feature
82 # until the rest of ebb is more developed.
83 if body.kind_of?(String)
85 FFI::client_set_body_written(self, true)
86 FFI::client_begin_transmission(self)
88 FFI::client_begin_transmission(self)
89 body.each { |p| write(p) }
90 FFI::client_set_body_written(self, true)
93 FFI::client_release(self)
99 env = FFI::client_env(self).update(BASE_ENV)
100 env['rack.input'] = RequestBody.new(self)
105 FFI::client_write(self, data)
108 def write_header(field, value)
109 FFI::client_write_header(self, field.to_s, value.to_s)
114 def initialize(client)
119 FFI::client_read_input(@client, len)
123 raise NotImplementedError
127 raise NotImplementedError
131 HTTP_STATUS_CODES = {
133 101 => 'Switching Protocols',
137 203 => 'Non-Authoritative Information',
139 205 => 'Reset Content',
140 206 => 'Partial Content',
141 300 => 'Multiple Choices',
142 301 => 'Moved Permanently',
143 302 => 'Moved Temporarily',
145 304 => 'Not Modified',
147 400 => 'Bad Request',
148 401 => 'Unauthorized',
149 402 => 'Payment Required',
152 405 => 'Method Not Allowed',
153 406 => 'Not Acceptable',
154 407 => 'Proxy Authentication Required',
155 408 => 'Request Time-out',
158 411 => 'Length Required',
159 412 => 'Precondition Failed',
160 413 => 'Request Entity Too Large',
161 414 => 'Request-URI Too Large',
162 415 => 'Unsupported Media Type',
163 500 => 'Internal Server Error',
164 501 => 'Not Implemented',
165 502 => 'Bad Gateway',
166 503 => 'Service Unavailable',
167 504 => 'Gateway Time-out',
168 505 => 'HTTP Version not supported'