jubilee 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rbenv-version +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +54 -0
- data/Guardfile +24 -0
- data/README.md +61 -0
- data/Rakefile +94 -0
- data/VERSION +1 -0
- data/bin/jubilee +6 -0
- data/bin/jubilee_d +10 -0
- data/examples/jubilee/keystore.jks +0 -0
- data/examples/jubilee/server-keystore.jks +0 -0
- data/examples/ssl/ServerTest.java +19 -0
- data/examples/ssl/webroot/index.html +10 -0
- data/jars/netty-3.6.0.Beta1.jar +0 -0
- data/jars/vertx-core-1.3.0.final.jar +0 -0
- data/java/.idea/ant.xml +7 -0
- data/java/.idea/libraries/jruby.xml +9 -0
- data/java/.idea/libraries/netty_3_6_0_Beta1.xml +9 -0
- data/java/.idea/libraries/vertx_core_1_3_0_final.xml +9 -0
- data/java/src/jubilee/JubileeService.java +21 -0
- data/java/src/org/jruby/jubilee/Const.java +148 -0
- data/java/src/org/jruby/jubilee/RackApplication.java +78 -0
- data/java/src/org/jruby/jubilee/RackEnvironment.java +13 -0
- data/java/src/org/jruby/jubilee/RackErrors.java +44 -0
- data/java/src/org/jruby/jubilee/RackInput.java +62 -0
- data/java/src/org/jruby/jubilee/RackResponse.java +16 -0
- data/java/src/org/jruby/jubilee/Server.java +104 -0
- data/java/src/org/jruby/jubilee/deploy/Starter.java +26 -0
- data/java/src/org/jruby/jubilee/impl/DefaultRackEnvironment.java +98 -0
- data/java/src/org/jruby/jubilee/impl/NullIO.java +111 -0
- data/java/src/org/jruby/jubilee/impl/RubyIORackErrors.java +68 -0
- data/java/src/org/jruby/jubilee/impl/RubyIORackInput.java +164 -0
- data/lib/jubilee.rb +11 -0
- data/lib/jubilee/application.rb +13 -0
- data/lib/jubilee/cli.rb +74 -0
- data/lib/jubilee/configuration.rb +52 -0
- data/lib/jubilee/const.rb +39 -0
- data/lib/jubilee/jubilee.jar +0 -0
- data/lib/jubilee/response.rb +64 -0
- data/lib/jubilee/server.rb +16 -0
- data/lib/rack/handler/jubilee.rb +43 -0
- data/test/.rbenv-version +1 -0
- data/test/config/app.rb +5 -0
- data/test/jubilee/test_cli.rb +11 -0
- data/test/jubilee/test_config.rb +14 -0
- data/test/jubilee/test_persistent.rb +238 -0
- data/test/jubilee/test_rack_server.rb +116 -0
- data/test/jubilee/test_server.rb +68 -0
- data/test/sinatra_app/app.rb +31 -0
- data/test/sinatra_app/config.ru +6 -0
- data/test/sinatra_app/public/test.html +10 -0
- data/test/sinatra_app/unicorn.conf.rb +29 -0
- data/test/test_helper.rb +21 -0
- metadata +160 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Jubilee
|
2
|
+
module Const
|
3
|
+
JUBILEE_VERSION = VERSION = "0.1.0".freeze
|
4
|
+
HTTP_11 = "HTTP/1.1".freeze
|
5
|
+
HTTP_10 = "HTTP/1.0".freeze
|
6
|
+
|
7
|
+
SERVER_SOFTWARE = "SERVER_SOFTWARE".freeze
|
8
|
+
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
9
|
+
GATEWAY_INTERFACE = "GATEWAY_INTERFACE".freeze
|
10
|
+
SERVER_NAME = "SERVER_NAME".freeze
|
11
|
+
SERVER_PORT = "SERVER_PORT".freeze
|
12
|
+
|
13
|
+
CGI_VER = "CGI/1.2".freeze
|
14
|
+
|
15
|
+
RACK_INPUT = "rack.input".freeze
|
16
|
+
|
17
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
18
|
+
GET = 'GET'.freeze
|
19
|
+
POST = "POST".freeze
|
20
|
+
REQUEST_PATH = "REQUEST_PATH".freeze
|
21
|
+
REQUEST_URI = "REQUEST_URI".freeze
|
22
|
+
PATH_INFO = "PATH_INFO".freeze
|
23
|
+
QUERY_STRING = "QUERY_STRING".freeze
|
24
|
+
|
25
|
+
CONTENT_LENGTH = "Content-Length".freeze
|
26
|
+
TRANSFER_ENCODING = "Transfer-Encoding".freeze
|
27
|
+
|
28
|
+
HTTP_VERSION = "HTTP_VERSION".freeze
|
29
|
+
HTTP_HOST = "HTTP_HOST".freeze
|
30
|
+
HTTP_USER_AGENT = "HTTP_USER_AGENT".freeze
|
31
|
+
HTTP_ACCEPT = "HTTP_ACCEPT".freeze
|
32
|
+
HTTP_COOKIE = "HTTP_COOKIE".freeze
|
33
|
+
HTTP_ACCEPT_LANGUAGE = "HTTP_ACCEPT_LANGUAGE".freeze
|
34
|
+
HTTP_ACCEPT_ENCODING = "HTTP_ACCEPT_ENCODING".freeze
|
35
|
+
HTTP_CONNECTION = "HTTP_CONNECTION".freeze
|
36
|
+
|
37
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.map{|s| [s, true]}]
|
38
|
+
end
|
39
|
+
end
|
Binary file
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'java'
|
2
|
+
module Jubilee
|
3
|
+
class Response
|
4
|
+
include Const
|
5
|
+
include org.jruby.jubilee.RackResponse
|
6
|
+
|
7
|
+
def initialize(array)
|
8
|
+
@status, @headers, @body = *array
|
9
|
+
@content_length = nil
|
10
|
+
if @body.kind_of? Array and @body.size == 1
|
11
|
+
@content_length = @body[0].bytesize
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond(response)
|
16
|
+
no_body = @status < 200 || STATUS_WITH_NO_ENTITY_BODY[@status]
|
17
|
+
write_status(response)
|
18
|
+
write_headers(response)
|
19
|
+
if no_body
|
20
|
+
response.end
|
21
|
+
else
|
22
|
+
if @body.respond_to?(:to_path)
|
23
|
+
response.sendFile(@body.to_path)
|
24
|
+
else
|
25
|
+
write_body(response)
|
26
|
+
response.end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
@body.close if @body.respond_to?(:close)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def write_status(response)
|
35
|
+
response.statusCode = @status
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_headers(response)
|
39
|
+
@headers.each do |key, value|
|
40
|
+
case key
|
41
|
+
when CONTENT_LENGTH
|
42
|
+
@content_length = value
|
43
|
+
next
|
44
|
+
when TRANSFER_ENCODING
|
45
|
+
@allow_chunked = false
|
46
|
+
@content_length = nil
|
47
|
+
end
|
48
|
+
response.putHeader(key, value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def write_body(response)
|
53
|
+
if @content_length
|
54
|
+
response.putHeader(CONTENT_LENGTH, @content_length.to_s)
|
55
|
+
else
|
56
|
+
response.setChunked(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
@body.each do |part|
|
60
|
+
response.write(part)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Jubilee
|
2
|
+
class Server < VertxServer
|
3
|
+
def initialize(app, opts = {})
|
4
|
+
options = {port: 3215, ssl: false}.merge(opts)
|
5
|
+
if (options[:ssl])
|
6
|
+
if options[:keystore].nil?
|
7
|
+
raise ArgumentError, "Please provide a keystore for ssl"
|
8
|
+
else
|
9
|
+
super(Application.new(app), options[:port], options[:ssl], options[:keystore], options[:keystore_password])
|
10
|
+
end
|
11
|
+
else
|
12
|
+
super(Application.new(app), options[:port])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rack/handler'
|
2
|
+
require 'jubilee'
|
3
|
+
require 'java'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
module Handler
|
7
|
+
module Jubilee
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
:host => '0.0.0.0',
|
10
|
+
:port => 3000,
|
11
|
+
:verbose => false
|
12
|
+
}
|
13
|
+
def self.run(app, options = {})
|
14
|
+
options = DEFAULT_OPTIONS.merge(options)
|
15
|
+
|
16
|
+
if options[:verbose]
|
17
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
18
|
+
end
|
19
|
+
|
20
|
+
if options[:environment]
|
21
|
+
ENV["RACK_ENV"] = options[:environment].to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
@server = ::Jubilee::Server.new(app, options)
|
25
|
+
|
26
|
+
puts "Jubilee starting..."
|
27
|
+
puts "Environment: #{ENV['RACK_ENV']}"
|
28
|
+
|
29
|
+
yield @server if block_given?
|
30
|
+
|
31
|
+
@server.start
|
32
|
+
@starter = org.jruby.jubilee.deploy.Starter.new
|
33
|
+
@starter.block
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.shutdown
|
37
|
+
@server.stop{ @starter.unblock }
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
register :jubilee, Jubilee
|
42
|
+
end
|
43
|
+
end
|
data/test/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.0
|
data/test/config/app.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'jubilee/cli'
|
3
|
+
|
4
|
+
class TestJubileeCLI < MiniTest::Unit::TestCase
|
5
|
+
def test_parse_options
|
6
|
+
cli = Jubilee::CLI.new(["app.rb"])
|
7
|
+
cli.parse_options
|
8
|
+
assert_equal "app.rb", cli.options[:rackup]
|
9
|
+
assert_equal 3215, cli.options[:port]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'jubilee/configuration'
|
3
|
+
|
4
|
+
class TestConfig < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@config = Jubilee::Configuration.new({rackup: "config/app.rb"})
|
7
|
+
end
|
8
|
+
def test_load
|
9
|
+
@config.load
|
10
|
+
resp = [200, {"Content-Type" => "text/plain"}, ["embeded app"]]
|
11
|
+
skip "hard to test because of Rack::Lint"
|
12
|
+
#assert_equal resp, @config.app.call({})
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'timeout'
|
3
|
+
require 'socket'
|
4
|
+
class TestPersistent < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
7
|
+
@close_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"
|
8
|
+
@http10_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
9
|
+
@keep_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: Keep-Alive\r\n\r\n"
|
10
|
+
|
11
|
+
@valid_post = "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nhello"
|
12
|
+
@valid_no_body = "GET / HTTP/1.1\r\nHost: test.com\r\nX-Status: 204\r\nContent-Type: text/plain\r\n\r\n"
|
13
|
+
|
14
|
+
@headers = { "X-Header" => "Works" }
|
15
|
+
@body = ["Hello"]
|
16
|
+
@inputs = []
|
17
|
+
|
18
|
+
@simple = lambda do |env|
|
19
|
+
@inputs << env['rack.input']
|
20
|
+
status = Integer(env['HTTP_X_STATUS'] || 200)
|
21
|
+
[status, @headers, @body]
|
22
|
+
end
|
23
|
+
|
24
|
+
@host = "127.0.0.1"
|
25
|
+
@port = 3215
|
26
|
+
|
27
|
+
@server = Jubilee::Server.new @simple
|
28
|
+
@server.start
|
29
|
+
|
30
|
+
@client = TCPSocket.new @host, @port
|
31
|
+
end
|
32
|
+
|
33
|
+
def teardown
|
34
|
+
@client.close
|
35
|
+
sleep 0.1 # in case server shutdown before request is submitted
|
36
|
+
@server.stop
|
37
|
+
end
|
38
|
+
|
39
|
+
def lines(count, s=@client)
|
40
|
+
str = ""
|
41
|
+
timeout(5) do
|
42
|
+
count.times { str << s.gets }
|
43
|
+
end
|
44
|
+
str
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_one_with_content_length
|
48
|
+
@client << @valid_request
|
49
|
+
sz = @body[0].size.to_s
|
50
|
+
|
51
|
+
assert_match Regexp.new("HTTP/1.1 200 OK\r\nContent-Length: #{sz}\r\nX-Header: Works\r\n\r\n", true), lines(4)
|
52
|
+
assert_equal "Hello", @client.read(5)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_two_back_to_back
|
56
|
+
@client << @valid_request
|
57
|
+
sz = @body[0].size.to_s
|
58
|
+
|
59
|
+
assert_match Regexp.new("HTTP/1.1 200 OK\r\nContent-Length: #{sz}\r\nX-Header: Works\r\n\r\n", true), lines(4)
|
60
|
+
assert_equal "Hello", @client.read(5)
|
61
|
+
|
62
|
+
@client << @valid_request
|
63
|
+
sz = @body[0].size.to_s
|
64
|
+
|
65
|
+
assert_match Regexp.new("HTTP/1.1 200 OK\r\nContent-Length: #{sz}\r\nX-Header: Works\r\n\r\n", true), lines(4)
|
66
|
+
assert_equal "Hello", @client.read(5)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_post_then_get
|
70
|
+
@client << @valid_post
|
71
|
+
sz = @body[0].size.to_s
|
72
|
+
|
73
|
+
assert_match Regexp.new("HTTP/1.1 200 OK\r\nContent-Length: #{sz}\r\nX-Header: Works\r\n\r\n", true), lines(4)
|
74
|
+
assert_equal "Hello", @client.read(5)
|
75
|
+
|
76
|
+
@client << @valid_request
|
77
|
+
sz = @body[0].size.to_s
|
78
|
+
|
79
|
+
assert_match Regexp.new("HTTP/1.1 200 OK\r\nContent-Length: #{sz}\r\nX-Header: Works\r\n\r\n", true), lines(4)
|
80
|
+
assert_equal "Hello", @client.read(5)
|
81
|
+
end
|
82
|
+
|
83
|
+
#def test_no_body_then_get
|
84
|
+
# @client << @valid_no_body
|
85
|
+
# assert_equal "HTTP/1.1 204 No Content\r\nX-Header: Works\r\n\r\n", lines(3)
|
86
|
+
|
87
|
+
# @client << @valid_request
|
88
|
+
# sz = @body[0].size.to_s
|
89
|
+
|
90
|
+
# assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
91
|
+
# assert_equal "Hello", @client.read(5)
|
92
|
+
#end
|
93
|
+
|
94
|
+
def test_chunked
|
95
|
+
@body << "Chunked"
|
96
|
+
|
97
|
+
@client << @valid_request
|
98
|
+
|
99
|
+
assert_equal "HTTP/1.1 200 OK\r\nx-header: Works\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n7\r\nChunked\r\n0\r\n\r\n", lines(10)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_no_chunked_in_http10
|
103
|
+
@body << "Chunked"
|
104
|
+
|
105
|
+
@client << @http10_request
|
106
|
+
|
107
|
+
assert_equal "HTTP/1.0 200 OK\r\nConnection: close\r\nx-header: Works\r\n\r\n", lines(4)
|
108
|
+
assert_equal "HelloChunked", @client.read
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_hex
|
112
|
+
str = "This is longer and will be in hex"
|
113
|
+
@body << str
|
114
|
+
|
115
|
+
@client << @valid_request
|
116
|
+
|
117
|
+
assert_equal "HTTP/1.1 200 OK\r\nx-header: Works\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n#{str.size.to_s(16)}\r\n#{str}\r\n0\r\n\r\n", lines(10)
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_client11_close
|
122
|
+
@client << @close_request
|
123
|
+
sz = @body[0].size.to_s
|
124
|
+
|
125
|
+
assert_equal "HTTP/1.1 200 OK\r\nConnection: close\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(5)
|
126
|
+
assert_equal "Hello", @client.read(5)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_client10_close
|
130
|
+
@client << @http10_request
|
131
|
+
sz = @body[0].size.to_s
|
132
|
+
|
133
|
+
assert_equal "HTTP/1.0 200 OK\r\nConnection: close\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(5)
|
134
|
+
assert_equal "Hello", @client.read(5)
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_one_with_keep_alive_header
|
138
|
+
@client << @keep_request
|
139
|
+
sz = @body[0].size.to_s
|
140
|
+
|
141
|
+
assert_equal "HTTP/1.0 200 OK\r\nConnection: keep-alive\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(5)
|
142
|
+
assert_equal "Hello", @client.read(5)
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_persistent_timeout
|
146
|
+
@server.persistent_timeout = 2
|
147
|
+
@client << @valid_request
|
148
|
+
sz = @body[0].size.to_s
|
149
|
+
|
150
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4)
|
151
|
+
assert_equal "Hello", @client.read(5)
|
152
|
+
|
153
|
+
sleep 3
|
154
|
+
|
155
|
+
assert_raises EOFError do
|
156
|
+
@client.read_nonblock(1)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_app_sets_content_length
|
161
|
+
@body = ["hello", " world"]
|
162
|
+
@headers['Content-Length'] = "11"
|
163
|
+
|
164
|
+
@client << @valid_request
|
165
|
+
|
166
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: 11\r\nx-header: Works\r\n\r\n",
|
167
|
+
lines(4)
|
168
|
+
assert_equal "hello world", @client.read(11)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_allow_app_to_chunk_itself
|
172
|
+
skip "vertx doesn't support chunk self yet"
|
173
|
+
@headers = {'Transfer-Encoding' => "chunked" }
|
174
|
+
|
175
|
+
@body = ["5\r\nhello\r\n0\r\n\r\n"]
|
176
|
+
|
177
|
+
@client << @valid_request
|
178
|
+
|
179
|
+
assert_equal "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n", lines(7)
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def test_two_requests_in_one_chunk
|
184
|
+
@server.persistent_timeout = 3
|
185
|
+
|
186
|
+
req = @valid_request.to_s
|
187
|
+
req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
188
|
+
|
189
|
+
@client << req
|
190
|
+
|
191
|
+
sz = @body[0].size.to_s
|
192
|
+
|
193
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4)
|
194
|
+
assert_equal "Hello", @client.read(5)
|
195
|
+
|
196
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4)
|
197
|
+
assert_equal "Hello", @client.read(5)
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_second_request_not_in_first_req_body
|
201
|
+
@server.persistent_timeout = 3
|
202
|
+
|
203
|
+
req = @valid_request.to_s
|
204
|
+
req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
205
|
+
|
206
|
+
@client << req
|
207
|
+
|
208
|
+
sz = @body[0].size.to_s
|
209
|
+
|
210
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4)
|
211
|
+
assert_equal "Hello", @client.read(5)
|
212
|
+
|
213
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4)
|
214
|
+
assert_equal "Hello", @client.read(5)
|
215
|
+
|
216
|
+
# Since rack process request before the body is ready, we cannot
|
217
|
+
# utilize this optimization
|
218
|
+
# assert_kind_of Jubilee::NullIO, @inputs[0]
|
219
|
+
# assert_kind_of Jubilee::NullIO, @inputs[1]
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_keepalive_doesnt_starve_clients
|
223
|
+
sz = @body[0].size.to_s
|
224
|
+
|
225
|
+
@client << @valid_request
|
226
|
+
|
227
|
+
c2 = TCPSocket.new @host, @port
|
228
|
+
c2 << @valid_request
|
229
|
+
|
230
|
+
out = IO.select([c2], nil, nil, 1)
|
231
|
+
|
232
|
+
assert out, "select returned nil"
|
233
|
+
assert_equal c2, out.first.first
|
234
|
+
|
235
|
+
assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4, c2)
|
236
|
+
assert_equal "Hello", c2.read(5)
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'rack/lint'
|
3
|
+
require 'rack/commonlogger'
|
4
|
+
|
5
|
+
class TestRackServer < MiniTest::Unit::TestCase
|
6
|
+
|
7
|
+
class ErrorChecker
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
@exception = nil
|
11
|
+
@env = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :exception, :env
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
begin
|
18
|
+
@env = env
|
19
|
+
return @app.call(env)
|
20
|
+
rescue Exception => e
|
21
|
+
@exception = e
|
22
|
+
|
23
|
+
[
|
24
|
+
500,
|
25
|
+
{ "X-Exception" => e.message, "X-Exception-Class" => e.class.to_s },
|
26
|
+
["Error detected"]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ServerLint < Rack::Lint
|
33
|
+
def call(env)
|
34
|
+
assert("No env given") { env }
|
35
|
+
check_env env
|
36
|
+
|
37
|
+
@app.call(env)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def setup
|
42
|
+
@valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
43
|
+
|
44
|
+
@simple = lambda { |env| [200, { "X-Header" => "Works" }, ["Hello"]] }
|
45
|
+
@checker = ErrorChecker.new ServerLint.new(@simple)
|
46
|
+
end
|
47
|
+
|
48
|
+
def teardown
|
49
|
+
@server.stop
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_lint
|
53
|
+
@server = Jubilee::Server.new @checker
|
54
|
+
|
55
|
+
@server.start
|
56
|
+
|
57
|
+
hit(['http://127.0.0.1:3215/test'])
|
58
|
+
|
59
|
+
if exc = @checker.exception
|
60
|
+
raise exc
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_large_post_body
|
65
|
+
@checker = ErrorChecker.new ServerLint.new(@simple)
|
66
|
+
@server = Jubilee::Server.new @checker
|
67
|
+
|
68
|
+
@server.start
|
69
|
+
|
70
|
+
big = "x" * (1024 * 16)
|
71
|
+
|
72
|
+
Net::HTTP.post_form URI.parse('http://127.0.0.1:3215/test'),
|
73
|
+
{ "big" => big }
|
74
|
+
|
75
|
+
if exc = @checker.exception
|
76
|
+
raise exc
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_path_info
|
81
|
+
input = nil
|
82
|
+
@server = Jubilee::Server.new (lambda { |env| input = env; @simple.call(env) })
|
83
|
+
@server.start
|
84
|
+
|
85
|
+
hit(['http://127.0.0.1:3215/test/a/b/c'])
|
86
|
+
|
87
|
+
assert_equal "/test/a/b/c", input['PATH_INFO']
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_query_string
|
91
|
+
input = nil
|
92
|
+
@server = Jubilee::Server.new (lambda { |env| input = env; @simple.call(env) })
|
93
|
+
@server.start
|
94
|
+
|
95
|
+
hit(['http://127.0.0.1:3215/test/a/b/c?foo=bar'])
|
96
|
+
|
97
|
+
assert_equal "foo=bar", input['QUERY_STRING']
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_post_data
|
101
|
+
require 'rack/request'
|
102
|
+
input = nil
|
103
|
+
@server = Jubilee::Server.new (lambda { |env| input = env; @simple.call(env) })
|
104
|
+
@server.start
|
105
|
+
|
106
|
+
req = Net::HTTP::Post::Multipart.new("/", "foo" => "bar")
|
107
|
+
resp = Net::HTTP.start('localhost', 3215) do |http|
|
108
|
+
http.request req
|
109
|
+
end
|
110
|
+
|
111
|
+
#Net::HTTP.post_form URI.parse('http://127.0.0.1:3215/test'), { "foo" => "bar" }
|
112
|
+
|
113
|
+
request = Rack::Request.new input
|
114
|
+
assert_equal "bar", request.params["foo"]
|
115
|
+
end
|
116
|
+
end
|