jubilee 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/jubilee.gemspec ADDED
@@ -0,0 +1,100 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "jubilee"
8
+ s.version = "0.4.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Isaiah Peng"]
12
+ s.date = "2013-09-16"
13
+ s.description = "Jubilee is a experimental webserver built for speed, it's based on Vertx."
14
+ s.email = "issaria@gmail.com"
15
+ s.executables = ["jubilee", "jubilee_d"]
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".ruby-version",
21
+ ".travis.yml",
22
+ "CHANGELOG",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "Guardfile",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/jubilee",
30
+ "bin/jubilee_d",
31
+ "examples/jubilee/keystore.jks",
32
+ "examples/jubilee/server-keystore.jks",
33
+ "examples/ssl/ServerTest.java",
34
+ "examples/ssl/webroot/index.html",
35
+ "jars/jackson-core-2.2.2.jar",
36
+ "jars/netty-all-4.0.4.Final.jar",
37
+ "jars/vertx-core-2.1.0-SNAPSHOT.jar",
38
+ "java/src/jubilee/JubileeService.java",
39
+ "java/src/org/jruby/jubilee/Const.java",
40
+ "java/src/org/jruby/jubilee/RackApplication.java",
41
+ "java/src/org/jruby/jubilee/RackEnvironment.java",
42
+ "java/src/org/jruby/jubilee/RackErrors.java",
43
+ "java/src/org/jruby/jubilee/RackInput.java",
44
+ "java/src/org/jruby/jubilee/RackResponse.java",
45
+ "java/src/org/jruby/jubilee/Server.java",
46
+ "java/src/org/jruby/jubilee/deploy/Starter.java",
47
+ "java/src/org/jruby/jubilee/impl/DefaultRackEnvironment.java",
48
+ "java/src/org/jruby/jubilee/impl/NullIO.java",
49
+ "java/src/org/jruby/jubilee/impl/RubyIORackErrors.java",
50
+ "java/src/org/jruby/jubilee/impl/RubyIORackInput.java",
51
+ "jubilee.gemspec",
52
+ "lib/jubilee.rb",
53
+ "lib/jubilee/application.rb",
54
+ "lib/jubilee/cli.rb",
55
+ "lib/jubilee/configuration.rb",
56
+ "lib/jubilee/const.rb",
57
+ "lib/jubilee/jubilee.jar",
58
+ "lib/jubilee/response.rb",
59
+ "lib/jubilee/server.rb",
60
+ "lib/jubilee/version.rb",
61
+ "lib/rack/handler/jubilee.rb",
62
+ "test/.ruby-version",
63
+ "test/config/app.rb",
64
+ "test/jubilee/test_cli.rb",
65
+ "test/jubilee/test_config.rb",
66
+ "test/jubilee/test_rack_server.rb",
67
+ "test/jubilee/test_response.rb",
68
+ "test/jubilee/test_server.rb",
69
+ "test/jubilee/test_upload.rb",
70
+ "test/sinatra_app/app.rb",
71
+ "test/sinatra_app/config.ru",
72
+ "test/sinatra_app/public/test.html",
73
+ "test/sinatra_app/unicorn.conf.rb",
74
+ "test/test_helper.rb"
75
+ ]
76
+ s.homepage = "http://github.com/isaiah/jubilee"
77
+ s.licenses = ["MIT"]
78
+ s.require_paths = ["lib"]
79
+ s.rubygems_version = "1.8.24"
80
+ s.summary = "JRuby webserver based on Vertx"
81
+
82
+ if s.respond_to? :specification_version then
83
+ s.specification_version = 3
84
+
85
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
86
+ s.add_runtime_dependency(%q<rack>, [">= 1.4.1"])
87
+ s.add_runtime_dependency(%q<spoon>, ["~> 0.0.4"])
88
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
89
+ else
90
+ s.add_dependency(%q<rack>, [">= 1.4.1"])
91
+ s.add_dependency(%q<spoon>, ["~> 0.0.4"])
92
+ s.add_dependency(%q<jeweler>, [">= 0"])
93
+ end
94
+ else
95
+ s.add_dependency(%q<rack>, [">= 1.4.1"])
96
+ s.add_dependency(%q<spoon>, ["~> 0.0.4"])
97
+ s.add_dependency(%q<jeweler>, [">= 0"])
98
+ end
99
+ end
100
+
@@ -1,5 +1,7 @@
1
1
  module Jubilee
2
2
  class Configuration
3
+ attr_reader :app
4
+
3
5
  def initialize(options, &block)
4
6
  @options = options
5
7
  @block = block
@@ -9,15 +11,6 @@ module Jubilee
9
11
  @app = load_rack_adapter(@options, &@block)
10
12
  end
11
13
 
12
- def app
13
- if !@options[:quiet] and @options[:environment] == "development"
14
- logger = @options[:logger] || STDOUT
15
- Rack::CommonLogger.new(@app, logger)
16
- else
17
- @app
18
- end
19
- end
20
-
21
14
  def port
22
15
  @options[:Port]
23
16
  end
@@ -41,18 +34,36 @@ module Jubilee
41
34
  private
42
35
  def load_rack_adapter(options, &block)
43
36
  if block
44
- app = Rack::Builder.new(&block).to_app
37
+ inner_app = Rack::Builder.new(&block).to_app
45
38
  else
46
39
  if options[:rackup]
47
40
  Kernel.load(options[:rackup])
48
- app = Object.const_get(File.basename(options[:rackup], '.rb').capitalize.to_sym).new
41
+ inner_app = Object.const_get(File.basename(options[:rackup], '.rb').capitalize.to_sym).new
49
42
  else
50
43
  Dir.chdir options[:chdir] if options[:chdir]
51
- app, opts = Rack::Builder.parse_file "config.ru"
44
+ inner_app, opts = Rack::Builder.parse_file "config.ru"
52
45
  end
53
46
  end
54
- app
47
+ case ENV["RACK_ENV"]
48
+ when "development"
49
+ Rack::Builder.new do
50
+ use Rack::ContentLength
51
+ use Rack::Chunked
52
+ use Rack::CommonLogger, $stderr
53
+ use Rack::ShowExceptions
54
+ use Rack::Lint
55
+ run inner_app
56
+ end.to_app
57
+ when "deployment"
58
+ Rack::Builder.new do
59
+ use Rack::ContentLength
60
+ use Rack::Chunked
61
+ use Rack::CommonLogger, $stderr
62
+ run inner_app
63
+ end.to_app
64
+ else
65
+ inner_app
66
+ end
55
67
  end
56
-
57
68
  end
58
69
  end
Binary file
@@ -2,8 +2,7 @@ require 'rack/methodoverride'
2
2
  module Jubilee
3
3
  class Server < VertxServer
4
4
  def initialize(app, opts = {})
5
- app = Rack::MethodOverride.new(app)
6
- options = {Port: 3215, ssl: false}.merge(opts)
5
+ options = {Port: 3215, ssl: false, number_of_workers: 4}.merge(opts)
7
6
  if (options[:ssl]) && options[:keystore_path].nil?
8
7
  raise ArgumentError, "Please provide a keystore for ssl"
9
8
  end
@@ -1,7 +1,7 @@
1
1
  require 'test_helper'
2
2
  require 'timeout'
3
3
  require 'socket'
4
- class TestPersistent < MiniTest::Unit::TestCase
4
+ class TestResponse < MiniTest::Unit::TestCase
5
5
  def setup
6
6
  @valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
7
7
  @close_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: Close\r\n\r\n"
@@ -26,7 +26,6 @@ class TestPersistent < MiniTest::Unit::TestCase
26
26
 
27
27
  @server = Jubilee::Server.new @simple
28
28
  @server.start
29
-
30
29
  sleep 0.1
31
30
  @client = TCPSocket.new @host, @port
32
31
  end
@@ -243,5 +242,32 @@ class TestPersistent < MiniTest::Unit::TestCase
243
242
  assert_equal "HTTP/1.1 200 OK\r\ncontent-length: #{sz}\r\nx-header: Works\r\n\r\n", lines(4, c2)
244
243
  assert_equal "Hello", c2.read(5)
245
244
  end
245
+
246
+ def test_client_shutdown_writes
247
+ bs = 15609315 * rand
248
+ sock = TCPSocket.new('127.0.0.1', @port)
249
+ sock.syswrite("PUT /hello HTTP/1.1\r\n")
250
+ sock.syswrite("Host: example.com\r\n")
251
+ sock.syswrite("Transfer-Encoding: chunked\r\n")
252
+ sock.syswrite("Trailer: X-Foo\r\n")
253
+ sock.syswrite("\r\n")
254
+ sock.syswrite("%x\r\n" % [ bs ])
255
+ sock.syswrite("F" * bs)
256
+ sock.syswrite("\r\n0\r\nX-")
257
+ "Foo: bar\r\n\r\n".each_byte do |x|
258
+ sock.syswrite x.chr
259
+ sleep 0.05
260
+ end
261
+ # we wrote the entire request before shutting down, server should
262
+ # continue to process our request and never hit EOFError on our sock
263
+ sock.shutdown(Socket::SHUT_WR)
264
+ buf = sock.read
265
+ assert_equal 'Hello', buf.split(/\r\n\r\n/).last
266
+ next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
267
+ assert_equal 'Hello', next_client
268
+ lines = File.readlines("test_stderr.#$$.log")
269
+ assert lines.grep(/^Unicorn::ClientShutdown: /).empty?
270
+ assert_nil sock.close
271
+ end
246
272
  =end
247
273
  end
@@ -42,7 +42,6 @@ class TestJubileeServer < MiniTest::Unit::TestCase
42
42
  skip
43
43
  end
44
44
 
45
-
46
45
  def test_url_scheme_for_https
47
46
  app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
48
47
  server = Jubilee::Server.new(app, {port:@port, ssl:true,
@@ -65,4 +64,5 @@ class TestJubileeServer < MiniTest::Unit::TestCase
65
64
  server.stop
66
65
  assert_equal "https", body
67
66
  end
67
+
68
68
  end
@@ -0,0 +1,298 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'test_helper'
4
+ require 'digest/md5'
5
+
6
+ class TestUpload < MiniTest::Unit::TestCase
7
+
8
+ def setup
9
+ @addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
10
+ @port = 3215
11
+ @hdr = {'Content-Type' => 'text/plain', 'Content-Length' => '0'}
12
+ @bs = 4096
13
+ @count = 256
14
+ @server = nil
15
+ @sha1 = Digest::SHA1.new
16
+
17
+ # we want random binary data to test 1.9 encoding-aware IO craziness
18
+ @random = File.open('/dev/urandom','rb')
19
+ @sha1_app = lambda do |env|
20
+ sha1 = Digest::SHA1.new
21
+ input = env['rack.input']
22
+ resp = {}
23
+
24
+ i = 0
25
+ while buf = input.read(@bs)
26
+ sha1.update(buf)
27
+ i += 1
28
+ end
29
+ resp[:sha1] = sha1.hexdigest
30
+
31
+ # rewind and read again
32
+ input.rewind
33
+ sha1.reset
34
+
35
+ while buf = input.read(@bs)
36
+ sha1.update(buf)
37
+ end
38
+
39
+ if resp[:sha1] == sha1.hexdigest
40
+ resp[:sysread_read_byte_match] = true
41
+ end
42
+ resp[:content_md5] = env['HTTP_CONTENT_MD5']
43
+
44
+ [ 200, @hdr.merge({'X-Resp' => resp.inspect}), [] ]
45
+ end
46
+ end
47
+
48
+ def teardown
49
+ @server.stop
50
+ @random.close
51
+ end
52
+
53
+ def test_put
54
+ start_server(@sha1_app)
55
+ sock = TCPSocket.new(@addr, @port)
56
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
57
+ @count.times do |i|
58
+ buf = @random.sysread(@bs)
59
+ @sha1.update(buf)
60
+ sock.syswrite(buf)
61
+ end
62
+ read = sock.read.split(/\r\n/)
63
+ assert_equal "HTTP/1.0 200 OK", read[0]
64
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
65
+ assert_equal @sha1.hexdigest, resp[:sha1]
66
+ end
67
+
68
+ def test_put_content_md5
69
+ start_server(@sha1_app)
70
+ md5 = Digest::MD5.new
71
+ sock = TCPSocket.new(@addr, @port)
72
+ sock.syswrite("PUT / HTTP/1.0\r\nTransfer-Encoding: chunked\r\n" \
73
+ "Trailer: Content-MD5\r\n\r\n")
74
+ @count.times do |i|
75
+ buf = @random.sysread(@bs)
76
+ @sha1.update(buf)
77
+ md5.update(buf)
78
+ sock.syswrite("#{'%x' % buf.size}\r\n")
79
+ sock.syswrite(buf << "\r\n")
80
+ end
81
+ sock.syswrite("0\r\n")
82
+
83
+ content_md5 = [ md5.digest! ].pack('m').strip.freeze
84
+ sock.syswrite("Content-MD5: #{content_md5}\r\n\r\n")
85
+ read = sock.read.split(/\r\n/)
86
+ assert_equal "HTTP/1.0 200 OK", read[0]
87
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
88
+ assert_equal @sha1.hexdigest, resp[:sha1]
89
+ #assert_equal content_md5, resp[:content_md5]
90
+ end
91
+
92
+ def test_put_trickle_small
93
+ start_server(@sha1_app)
94
+ @count, @bs = 2, 128
95
+ assert_equal 256, length
96
+ sock = TCPSocket.new(@addr, @port)
97
+ hdr = "PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n"
98
+ @count.times do
99
+ buf = @random.sysread(@bs)
100
+ @sha1.update(buf)
101
+ hdr << buf
102
+ sock.syswrite(hdr)
103
+ hdr = ''
104
+ sleep 0.6
105
+ end
106
+ read = sock.read.split(/\r\n/)
107
+ assert_equal "HTTP/1.0 200 OK", read[0]
108
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
109
+ assert_equal @sha1.hexdigest, resp[:sha1]
110
+ end
111
+
112
+ def test_put_keepalive_truncates_small_overwrite
113
+ start_server(@sha1_app)
114
+ sock = TCPSocket.new(@addr, @port)
115
+ to_upload = length + 1
116
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
117
+ @count.times do
118
+ buf = @random.sysread(@bs)
119
+ @sha1.update(buf)
120
+ sock.syswrite(buf)
121
+ end
122
+ sock.syswrite('12345') # write 4 bytes more than we expected
123
+ @sha1.update('1')
124
+
125
+ buf = sock.readpartial(4096)
126
+ while buf !~ /\r\n\r\n/
127
+ buf << sock.readpartial(4096)
128
+ end
129
+ read = buf.split(/\r\n/)
130
+ assert_equal "HTTP/1.0 200 OK", read[0]
131
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
132
+ #assert_equal to_upload, resp[:size]
133
+ assert_equal @sha1.hexdigest, resp[:sha1]
134
+ end
135
+
136
+ def test_put_excessive_overwrite_closed
137
+ tmp = Tempfile.new('overwrite_check')
138
+ tmp.sync = true
139
+ start_server(lambda { |env|
140
+ nr = 0
141
+ while buf = env['rack.input'].read(65536)
142
+ nr += buf.size
143
+ end
144
+ tmp.write(nr.to_s)
145
+ [ 200, @hdr, [] ]
146
+ })
147
+ sock = TCPSocket.new(@addr, @port)
148
+ buf = ' ' * @bs
149
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
150
+
151
+ @count.times { sock.syswrite(buf) }
152
+ assert_raises(Errno::ECONNRESET, Errno::EPIPE) do
153
+ 16384.times { sock.syswrite(buf) }
154
+ end
155
+ sock.gets
156
+ tmp.rewind
157
+ assert_equal length, tmp.read.to_i
158
+ end
159
+
160
+ # Despite reading numerous articles and inspecting the 1.9.1-p0 C
161
+ # source, Eric Wong will never trust that we're always handling
162
+ # encoding-aware IO objects correctly. Thus this test uses shell
163
+ # utilities that should always operate on files/sockets on a
164
+ # byte-level.
165
+ def test_uncomfortable_with_onenine_encodings
166
+ # POSIX doesn't require all of these to be present on a system
167
+ which('curl') or return
168
+ which('sha1sum') or return
169
+ which('dd') or return
170
+
171
+ start_server(@sha1_app)
172
+
173
+ tmp = Tempfile.new('dd_dest')
174
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
175
+ "bs=#{@bs}", "count=#{@count}"),
176
+ "dd #@random to #{tmp}")
177
+ sha1_re = %r!\b([a-f0-9]{40})\b!
178
+ sha1_out = `sha1sum #{tmp.path}`
179
+
180
+ assert $?.success?, 'sha1sum ran OK'
181
+
182
+ assert_match(sha1_re, sha1_out)
183
+ sha1 = sha1_re.match(sha1_out)[1]
184
+ resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
185
+
186
+ assert $?.success?, 'curl ran OK'
187
+ assert_match(%r!\b#{sha1}\b!, resp)
188
+ assert_match(/sysread_read_byte_match/, resp)
189
+
190
+ # small StringIO path
191
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
192
+ "bs=1024", "count=1"),
193
+ "dd #@random to #{tmp}")
194
+ sha1_re = %r!\b([a-f0-9]{40})\b!
195
+ sha1_out = `sha1sum #{tmp.path}`
196
+ assert $?.success?, 'sha1sum ran OK'
197
+
198
+ assert_match(sha1_re, sha1_out)
199
+ sha1 = sha1_re.match(sha1_out)[1]
200
+ resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
201
+ assert $?.success?, 'curl ran OK'
202
+ assert_match(%r!\b#{sha1}\b!, resp)
203
+ assert_match(/sysread_read_byte_match/, resp)
204
+ end
205
+
206
+ =begin
207
+ def test_chunked_upload_via_curl
208
+ # POSIX doesn't require all of these to be present on a system
209
+ which('curl') or return
210
+ which('sha1sum') or return
211
+ which('dd') or return
212
+
213
+ start_server(@sha1_app)
214
+
215
+ tmp = Tempfile.new('dd_dest')
216
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
217
+ "bs=#{@bs}", "count=#{@count}"),
218
+ "dd #@random to #{tmp}")
219
+ sha1_re = %r!\b([a-f0-9]{40})\b!
220
+ sha1_out = `sha1sum #{tmp.path}`
221
+ assert $?.success?, 'sha1sum ran OK'
222
+
223
+ assert_match(sha1_re, sha1_out)
224
+ sha1 = sha1_re.match(sha1_out)[1]
225
+ cmd = "curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
226
+ -isSf --no-buffer -T- " \
227
+ "http://#@addr:#@port/"
228
+ resp = Tempfile.new('resp')
229
+ resp.sync = true
230
+
231
+ rd, wr = IO.pipe
232
+ wr.sync = rd.sync = true
233
+ pid = fork {
234
+ STDIN.reopen(rd)
235
+ rd.close
236
+ wr.close
237
+ STDOUT.reopen(resp)
238
+ exec cmd
239
+ }
240
+ rd.close
241
+
242
+ tmp.rewind
243
+ @count.times { |i|
244
+ wr.write(tmp.read(@bs))
245
+ sleep(rand / 10) if 0 == i % 8
246
+ }
247
+ wr.close
248
+ pid, status = Process.waitpid2(pid)
249
+
250
+ resp.rewind
251
+ resp = resp.read
252
+ assert status.success?, 'curl ran OK'
253
+ assert_match(%r!\b#{sha1}\b!, resp)
254
+ assert_match(/sysread_read_byte_match/, resp)
255
+ assert_match(/expect_size_match/, resp)
256
+ end
257
+ =end
258
+
259
+ def test_curl_chunked_small
260
+ # POSIX doesn't require all of these to be present on a system
261
+ which('curl') or return
262
+ which('sha1sum') or return
263
+ which('dd') or return
264
+
265
+ start_server(@sha1_app)
266
+
267
+ tmp = Tempfile.new('dd_dest')
268
+ # small StringIO path
269
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
270
+ "bs=1024", "count=1"),
271
+ "dd #@random to #{tmp}")
272
+ sha1_re = %r!\b([a-f0-9]{40})\b!
273
+ sha1_out = `sha1sum #{tmp.path}`
274
+ assert $?.success?, 'sha1sum ran OK'
275
+
276
+ assert_match(sha1_re, sha1_out)
277
+ sha1 = sha1_re.match(sha1_out)[1]
278
+ resp = `curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
279
+ -isSf --no-buffer -T- http://#@addr:#@port/ < #{tmp.path}`
280
+ assert $?.success?, 'curl ran OK'
281
+ assert_match(%r!\b#{sha1}\b!, resp)
282
+ assert_match(/sysread_read_byte_match/, resp)
283
+ #assert_match(/expect_size_match/, resp)
284
+ end
285
+
286
+ private
287
+
288
+ def length
289
+ @bs * @count
290
+ end
291
+
292
+ def start_server(app)
293
+ @server = Jubilee::Server.new app
294
+ @server.start
295
+ sleep 0.1
296
+ end
297
+
298
+ end