ebb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +119 -0
- data/VERSION +1 -0
- data/benchmark/application.rb +84 -0
- data/benchmark/bench_results.rb +45 -0
- data/benchmark/server_test.rb +152 -0
- data/benchmark/test.rb +141 -0
- data/bin/ebb_rails +88 -0
- data/libev/ev.c +2440 -0
- data/libev/ev.h +551 -0
- data/libev/ev_epoll.c +174 -0
- data/libev/ev_kqueue.c +186 -0
- data/libev/ev_poll.c +127 -0
- data/libev/ev_port.c +155 -0
- data/libev/ev_select.c +236 -0
- data/libev/ev_vars.h +108 -0
- data/libev/ev_win32.c +117 -0
- data/libev/ev_wrap.h +132 -0
- data/ruby_lib/daemonizable.rb +134 -0
- data/ruby_lib/ebb.rb +179 -0
- data/ruby_lib/rack/adapter/rails.rb +152 -0
- data/src/ebb.c +709 -0
- data/src/ebb.h +102 -0
- data/src/ebb_ruby.c +256 -0
- data/src/extconf.rb +44 -0
- data/src/parser.c +1584 -0
- data/src/parser.h +50 -0
- metadata +80 -0
data/README
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# A Web Server Called *Ebb*
|
2
|
+
|
3
|
+
Ebb aims to be a small and fast web server specifically for hosting
|
4
|
+
web frameworks like Rails, Merb, and in the future Django.
|
5
|
+
|
6
|
+
It is not meant to be a full featured web server like Lighttpd, Apache, or
|
7
|
+
Nginx. Rather it should be used in multiplicity behind a
|
8
|
+
[load balancer](http://brainspl.at/articles/2007/11/09/a-fair-proxy-balancer-for-nginx-and-mongrel)
|
9
|
+
and a front-end server. It is not meant to serve static files in production.
|
10
|
+
|
11
|
+
## Design
|
12
|
+
|
13
|
+
The design is similar to the
|
14
|
+
[Evented Mongrel](http://swiftiply.swiftcore.org/mongrel.html) web server;
|
15
|
+
except instead of using EventMachine (a ruby binding to libevent), the Ebb
|
16
|
+
web server is written in C and uses the
|
17
|
+
[libev](http://software.schmorp.de/pkg/libev.html) event loop library.
|
18
|
+
|
19
|
+
Connections are processed as follows:
|
20
|
+
|
21
|
+
1. libev loops and waits for incoming connections.
|
22
|
+
|
23
|
+
2. When Ebb receives a connection, it passes the request into the
|
24
|
+
[mongrel state machine](http://mongrel.rubyforge.org/browser/tags/rel_1-0-1/ext/http11/http11_parser.rl)
|
25
|
+
which securely parses the headers.
|
26
|
+
|
27
|
+
3. When the request is complete, Ebb passes the information to a user
|
28
|
+
supplied callback.
|
29
|
+
|
30
|
+
4. The Ruby binding supplying this callback transforms the
|
31
|
+
request into a [Rack](http://rack.rubyforge.org/) compatible `env` hash
|
32
|
+
and passes it on a Rack adapter.
|
33
|
+
|
34
|
+
Because Ebb is written mostly in C, other language bindings can be added to
|
35
|
+
make it useful to Non-Ruby frameworks. For example, a Python WSGI interface is
|
36
|
+
forthcoming.
|
37
|
+
|
38
|
+
## Download
|
39
|
+
|
40
|
+
The Ruby binding is available as a Ruby Gem. It can be install by executing
|
41
|
+
|
42
|
+
`gem install ebb`
|
43
|
+
|
44
|
+
Ebb depends on having glib2 headers and libraries installed. (Easily available
|
45
|
+
on any UNIX system.) A manual downloads can be found at
|
46
|
+
the [RubyForge project page](http://rubyforge.org/frs/?group_id=5640).
|
47
|
+
|
48
|
+
## Why?
|
49
|
+
|
50
|
+
Because by building the server in C one is able to side-step the
|
51
|
+
limitations on speed of many scripting languages. Inefficiencies are okay
|
52
|
+
for quick and beautiful code, but for production web servers that might handle
|
53
|
+
thousands of requests a second, an attempt should be made to be as efficient
|
54
|
+
as possible in processing connections.
|
55
|
+
|
56
|
+
Following are some benchmarks. Please take these measurements with a grain
|
57
|
+
of salt. Benchmarks like these are notorious for presenting an inaccurate
|
58
|
+
or highly slanted view of how software performs.
|
59
|
+
The code for these can be found in the `benchmark` directory.
|
60
|
+
|
61
|
+
![Response Size](http://s3.amazonaws.com/four.livejournal/20080227/response_size.png)
|
62
|
+
|
63
|
+
This shows how the web servers perform with respect to throughput (using a
|
64
|
+
simple Rack application). Concurrency is at 50 clients.
|
65
|
+
|
66
|
+
![Concurrency](http://s3.amazonaws.com/four.livejournal/20080227/concurrency.png)
|
67
|
+
|
68
|
+
A simple concurrent clients benchmark serving a *hello world* page.
|
69
|
+
|
70
|
+
![Uploads](http://s3.amazonaws.com/four.livejournal/20080227/post_size.png)
|
71
|
+
|
72
|
+
Ebb processes uploads before handing it over to the web application. This
|
73
|
+
allows Ebb to continue to process other clients while the upload is in
|
74
|
+
progress. The cliff at 40k here is because Ebb's internal request
|
75
|
+
buffer is set at 40 kilobytes before it writes to file.
|
76
|
+
|
77
|
+
|
78
|
+
## Contributions
|
79
|
+
|
80
|
+
|
81
|
+
Contributions (patches, criticism, advice) are very welcome! The source code
|
82
|
+
is hosted at [repo.or.cz](http://repo.or.cz/w/ebb.git). It can be retrieved
|
83
|
+
by executing
|
84
|
+
|
85
|
+
`git clone http://repo.or.cz/r/ebb.git`
|
86
|
+
|
87
|
+
I intend to keep the C code base very small, so do email me before writing any
|
88
|
+
large additions. Here are some features that I would like to add:
|
89
|
+
|
90
|
+
* Multipart parser
|
91
|
+
* Streaming responses
|
92
|
+
* Optimize and clean up upload handling
|
93
|
+
* Option to listen on unix sockets instead of TCP
|
94
|
+
* Python binding
|
95
|
+
|
96
|
+
## (The MIT) License
|
97
|
+
|
98
|
+
Copyright © 2008 [Ry Dahl](http://tinyclouds.org) (ry at tiny clouds dot org)
|
99
|
+
|
100
|
+
<div id="license">
|
101
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
102
|
+
a copy of this software and associated documentation files (the
|
103
|
+
"Software"), to deal in the Software without restriction, including
|
104
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
105
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
106
|
+
permit persons to whom the Software is furnished to do so, subject to
|
107
|
+
the following conditions:
|
108
|
+
|
109
|
+
The above copyright notice and this permission notice shall be
|
110
|
+
included in all copies or substantial portions of the Software.
|
111
|
+
|
112
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
113
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
114
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
115
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
116
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
117
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
118
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
119
|
+
</div>
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,84 @@
|
|
1
|
+
DIR = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
def fib(n)
|
4
|
+
return 1 if n <= 1
|
5
|
+
fib(n-1) + fib(n-2)
|
6
|
+
end
|
7
|
+
|
8
|
+
def wait(seconds)
|
9
|
+
n = (seconds / 0.01).to_i
|
10
|
+
n.times do
|
11
|
+
sleep(0.01)
|
12
|
+
#File.read(DIR + '/yahoo.html')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class SimpleApp
|
17
|
+
@@responses = {}
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@count = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
commands = env['PATH_INFO'].split('/')
|
25
|
+
|
26
|
+
@count += 1
|
27
|
+
if commands.include?('periodical_activity') and @count % 10 != 1
|
28
|
+
return [200, {'Content-Type'=>'text/plain'}, "quick response!\r\n"]
|
29
|
+
end
|
30
|
+
|
31
|
+
if commands.include?('fibonacci')
|
32
|
+
n = commands.last.to_i
|
33
|
+
raise "fibonacci called with n <= 0" if n <= 0
|
34
|
+
body = (1..n).to_a.map { |i| fib(i).to_s }.join(' ')
|
35
|
+
status = 200
|
36
|
+
|
37
|
+
elsif commands.include?('wait')
|
38
|
+
n = commands.last.to_i
|
39
|
+
raise "wait called with n <= 0" if n <= 0
|
40
|
+
wait(n)
|
41
|
+
body = "waited about #{n} seconds"
|
42
|
+
status = 200
|
43
|
+
|
44
|
+
elsif commands.include?('bytes')
|
45
|
+
n = commands.last.to_i
|
46
|
+
raise "bytes called with n <= 0" if n <= 0
|
47
|
+
body = @@responses[n] || "C"*n
|
48
|
+
status = 200
|
49
|
+
|
50
|
+
elsif commands.include?('test_post_length')
|
51
|
+
input_body = ""
|
52
|
+
while chunk = env['rack.input'].read(512)
|
53
|
+
input_body << chunk
|
54
|
+
end
|
55
|
+
if env['HTTP_CONTENT_LENGTH'].to_i == input_body.length
|
56
|
+
body = "Content-Length matches input length"
|
57
|
+
status = 200
|
58
|
+
else
|
59
|
+
body = "Content-Length doesn't matches input length!
|
60
|
+
content_length = #{env['HTTP_CONTENT_LENGTH'].to_i}
|
61
|
+
input_body.length = #{input_body.length}"
|
62
|
+
status = 500
|
63
|
+
end
|
64
|
+
|
65
|
+
else
|
66
|
+
status = 404
|
67
|
+
body = "Undefined url"
|
68
|
+
end
|
69
|
+
|
70
|
+
[status, {'Content-Type' => 'text/plain'}, body + "\r\n\r\n"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
if $0 == __FILE__
|
76
|
+
require DIR + '/../ruby_lib/ebb'
|
77
|
+
require 'rubygems'
|
78
|
+
require 'ruby-debug'
|
79
|
+
Debugger.start
|
80
|
+
|
81
|
+
server = Ebb::Server.new(SimpleApp.new, :port => 4001)
|
82
|
+
puts "Ebb started on http://0.0.0.0:4001/"
|
83
|
+
server.start
|
84
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# supply the benchmark dump file as an argumetn to this program
|
2
|
+
require 'rubygems'
|
3
|
+
require 'google_chart'
|
4
|
+
require 'server_test'
|
5
|
+
|
6
|
+
class Array
|
7
|
+
def avg
|
8
|
+
sum.to_f / length
|
9
|
+
end
|
10
|
+
def sum
|
11
|
+
inject(0) { |i, s| s += i }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
colors = %w{F74343 444130 7DA478 E4AC3D}
|
18
|
+
max_x = 0
|
19
|
+
max_y = 0
|
20
|
+
results = ServerTestResults.open(ARGV[0])
|
21
|
+
all_m = []
|
22
|
+
response_chart = GoogleChart::LineChart.new('400x300', Time.now.strftime('%Y.%m.%d'), true)
|
23
|
+
results.servers.each do |server|
|
24
|
+
data = results.data(server).sort
|
25
|
+
response_chart.data(server, data, colors.shift)
|
26
|
+
x = data.map { |d| d[0] }.max
|
27
|
+
y = data.map { |d| d[1] }.max
|
28
|
+
max_x = x if x > max_x
|
29
|
+
max_y = y if y > max_y
|
30
|
+
end
|
31
|
+
|
32
|
+
label = case results.benchmark
|
33
|
+
when "response_size"
|
34
|
+
"kilobytes served"
|
35
|
+
when "wait_fib", "concurrency"
|
36
|
+
"concurrency"
|
37
|
+
when "post_size"
|
38
|
+
"kilobytes uploaded"
|
39
|
+
end
|
40
|
+
|
41
|
+
response_chart.axis(:y, :range => [0,max_y])
|
42
|
+
response_chart.axis(:y, :labels => ['req/s'], :positions => [50])
|
43
|
+
response_chart.axis(:x, :range => [0,max_x])
|
44
|
+
response_chart.axis(:x, :labels => [label], :positions => [50])
|
45
|
+
puts response_chart.to_url
|
@@ -0,0 +1,152 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rack'
|
5
|
+
require 'application'
|
6
|
+
|
7
|
+
|
8
|
+
class Array
|
9
|
+
def avg
|
10
|
+
sum.to_f / length
|
11
|
+
end
|
12
|
+
|
13
|
+
def sum
|
14
|
+
inject(0) { |i, s| s += i }
|
15
|
+
end
|
16
|
+
|
17
|
+
def rand_each(&block)
|
18
|
+
sort_by{ rand }.each &block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ServerTestResults
|
23
|
+
def self.open(filename)
|
24
|
+
if File.readable?(filename)
|
25
|
+
new(Marshal.load(File.read(filename)))
|
26
|
+
else
|
27
|
+
new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(results = [])
|
32
|
+
@results = results
|
33
|
+
end
|
34
|
+
|
35
|
+
def benchmark
|
36
|
+
@results.first[:benchmark]
|
37
|
+
end
|
38
|
+
|
39
|
+
def write(filename='results.dump')
|
40
|
+
puts "writing dump file to #{filename}"
|
41
|
+
File.open(filename, 'w+') do |f|
|
42
|
+
f.write Marshal.dump(@results)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def <<(r)
|
47
|
+
@results << r
|
48
|
+
end
|
49
|
+
|
50
|
+
def length
|
51
|
+
@results.length
|
52
|
+
end
|
53
|
+
|
54
|
+
def servers
|
55
|
+
@results.map {|r| r[:server] }.uniq.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
def data(server)
|
59
|
+
server_data = @results.find_all { |r| r[:server] == server }
|
60
|
+
ticks = server_data.map { |d| d[:input] }.uniq
|
61
|
+
datas = []
|
62
|
+
ticks.each do |c|
|
63
|
+
measurements = server_data.find_all { |d| d[:input] == c }.map { |d| d[:rps] }
|
64
|
+
datas << [c, measurements.avg]
|
65
|
+
end
|
66
|
+
datas
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class ServerTest
|
72
|
+
attr_reader :name, :port, :app, :pid
|
73
|
+
def initialize(name, port, &start_block)
|
74
|
+
@name = name
|
75
|
+
@port = port.to_i
|
76
|
+
end
|
77
|
+
|
78
|
+
def <=>(a)
|
79
|
+
@name <=> a.name
|
80
|
+
end
|
81
|
+
|
82
|
+
def kill
|
83
|
+
Process.kill('KILL', @pid)
|
84
|
+
end
|
85
|
+
|
86
|
+
def running?
|
87
|
+
!@pid.nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
def start
|
91
|
+
puts "Starting #{name}"
|
92
|
+
case name
|
93
|
+
when 'emongrel'
|
94
|
+
@pid = fork { start_emongrel }
|
95
|
+
when 'ebb'
|
96
|
+
@pid = fork { start_ebb }
|
97
|
+
when 'mongrel'
|
98
|
+
@pid = fork { start_mongrel }
|
99
|
+
when 'thin'
|
100
|
+
@pid = fork { start_thin }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def app
|
105
|
+
SimpleApp.new
|
106
|
+
end
|
107
|
+
|
108
|
+
def start_emongrel
|
109
|
+
require 'mongrel'
|
110
|
+
require 'swiftcore/evented_mongrel'
|
111
|
+
ENV['EVENT'] = "1"
|
112
|
+
Rack::Handler::Mongrel.run(app, :Host => '0.0.0.0', :Port => @port.to_i)
|
113
|
+
end
|
114
|
+
|
115
|
+
def start_ebb
|
116
|
+
require File.dirname(__FILE__) + '/../ruby_lib/ebb'
|
117
|
+
server = Ebb::Server.run(app, :port => @port)
|
118
|
+
end
|
119
|
+
|
120
|
+
def start_mongrel
|
121
|
+
require 'mongrel'
|
122
|
+
ENV.delete('EVENT')
|
123
|
+
Rack::Handler::Mongrel.run(app, :Port => @port)
|
124
|
+
end
|
125
|
+
|
126
|
+
def start_thin
|
127
|
+
require 'thin'
|
128
|
+
Rack::Handler::Thin.run(app, :Port => @port)
|
129
|
+
end
|
130
|
+
|
131
|
+
def trial(ab_cmd)
|
132
|
+
cmd = ab_cmd.sub('PORT', @port.to_s)
|
133
|
+
|
134
|
+
puts "#{@name} (#{cmd})"
|
135
|
+
|
136
|
+
r = %x{#{cmd}}
|
137
|
+
|
138
|
+
return nil unless r =~ /Requests per second:\s*(\d+\.\d\d)/
|
139
|
+
rps = $1.to_f
|
140
|
+
if r =~ /Complete requests:\s*(\d+)/
|
141
|
+
requests_completed = $1.to_i
|
142
|
+
end
|
143
|
+
puts " #{rps} req/sec (#{requests_completed} completed)"
|
144
|
+
|
145
|
+
{
|
146
|
+
:server => @name,
|
147
|
+
:rps => rps,
|
148
|
+
:requests_completed => requests_completed,
|
149
|
+
:ab_cmd => cmd
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
data/benchmark/test.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../ruby_lib/ebb'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'net/http'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
|
7
|
+
class EbbTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@pid = fork do
|
10
|
+
server = Ebb::Server.new(self, :port => 4044)
|
11
|
+
server.start
|
12
|
+
end
|
13
|
+
sleep 0.5
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
Process.kill('KILL', @pid)
|
18
|
+
sleep 0.5
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(path)
|
22
|
+
Net::HTTP.get_response(URI.parse("http://0.0.0.0:4044#{path}"))
|
23
|
+
end
|
24
|
+
|
25
|
+
def post(path, data)
|
26
|
+
Net::HTTP.post_form(URI.parse("http://0.0.0.0:4044#{path}"), data)
|
27
|
+
end
|
28
|
+
|
29
|
+
@@responses = {}
|
30
|
+
def call(env)
|
31
|
+
commands = env['PATH_INFO'].split('/')
|
32
|
+
|
33
|
+
if commands.include?('bytes')
|
34
|
+
n = commands.last.to_i
|
35
|
+
raise "bytes called with n <= 0" if n <= 0
|
36
|
+
body = @@responses[n] || "C"*n
|
37
|
+
status = 200
|
38
|
+
|
39
|
+
elsif commands.include?('env')
|
40
|
+
env.delete('rack.input') # delete this because it's hard to marshal
|
41
|
+
env.delete('rack.errors')
|
42
|
+
body = Base64.encode64(Marshal.dump(env))
|
43
|
+
status = 200
|
44
|
+
|
45
|
+
elsif commands.include?('test_post_length')
|
46
|
+
input_body = ""
|
47
|
+
while chunk = env['rack.input'].read(512)
|
48
|
+
input_body << chunk
|
49
|
+
end
|
50
|
+
|
51
|
+
content_length_header = env['HTTP_CONTENT_LENGTH'].to_i
|
52
|
+
|
53
|
+
if content_length_header == input_body.length
|
54
|
+
body = "Content-Length matches input length"
|
55
|
+
status = 200
|
56
|
+
else
|
57
|
+
body = "Content-Length header is #{content_length_header} but body length is #{input_body.length}"
|
58
|
+
# content_length = #{env['HTTP_CONTENT_LENGTH'].to_i}
|
59
|
+
# input_body.length = #{input_body.length}"
|
60
|
+
status = 500
|
61
|
+
end
|
62
|
+
|
63
|
+
else
|
64
|
+
status = 404
|
65
|
+
body = "Undefined url"
|
66
|
+
end
|
67
|
+
|
68
|
+
[status, {'Content-Type' => 'text/plain'}, body]
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_get_bytes
|
72
|
+
[1,10,1000].each do |i|
|
73
|
+
response = get("/bytes/#{i}")
|
74
|
+
assert_equal "#{'C'*i.to_i}", response.body
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_get_unknown
|
79
|
+
response = get('/blah')
|
80
|
+
assert_equal "Undefined url", response.body
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_small_posts
|
84
|
+
[1,10,321,123,1000].each do |i|
|
85
|
+
response = post("/test_post_length", 'C'*i)
|
86
|
+
assert_equal 200, response.code.to_i, response.body
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# this is rough but does detect major problems
|
91
|
+
def test_ab
|
92
|
+
r = %x{ab -n 1000 -c 50 -q http://0.0.0.0:4044/bytes/123}
|
93
|
+
assert r =~ /Requests per second:\s*(\d+)/, r
|
94
|
+
assert $1.to_i > 100, r
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_large_post
|
98
|
+
[50,60,100].each do |i|
|
99
|
+
response = post("/test_post_length", 'C'*1024*i)
|
100
|
+
assert_equal 200, response.code.to_i, response.body
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_env
|
105
|
+
response = get('/env')
|
106
|
+
env = Marshal.load(Base64.decode64(response.body))
|
107
|
+
assert_equal '/env', env['PATH_INFO']
|
108
|
+
assert_equal '/env', env['REQUEST_PATH']
|
109
|
+
assert_equal 'HTTP/1.1', env['SERVER_PROTOCOL']
|
110
|
+
assert_equal 'CGI/1.2', env['GATEWAY_INTERFACE']
|
111
|
+
assert_equal '0.0.0.0', env['SERVER_NAME']
|
112
|
+
assert_equal '4044', env['SERVER_PORT']
|
113
|
+
assert_equal 'GET', env['REQUEST_METHOD']
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class EbbRailsTest < Test::Unit::TestCase
|
118
|
+
# just to make sure there isn't some load error
|
119
|
+
def test_ebb_rails_version
|
120
|
+
out = %x{ruby #{Ebb::LIBDIR}/../bin/ebb_rails -v}
|
121
|
+
assert_match %r{Ebb #{Ebb::VERSION}}, out
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
#
|
127
|
+
# class SocketTest < Test::Unit::TestCase
|
128
|
+
# def test_socket_creation
|
129
|
+
# filename = '/tmp/ebb.socket'
|
130
|
+
# @pid = fork do
|
131
|
+
# server = Ebb::Server.new(TestApp.new, {:socket => filename})
|
132
|
+
# server.start
|
133
|
+
# end
|
134
|
+
# sleep(1)
|
135
|
+
# assert File.exists?(filename)
|
136
|
+
#
|
137
|
+
# Process.kill('KILL', @pid)
|
138
|
+
#
|
139
|
+
# assert !File.exists?(filename)
|
140
|
+
# end
|
141
|
+
# end
|