jubilee 1.1.0-java

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.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG +32 -0
  5. data/Gemfile +35 -0
  6. data/Gemfile.lock +244 -0
  7. data/Guardfile +24 -0
  8. data/KNOWN_ISSUES +6 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +147 -0
  11. data/ROADMAP +5 -0
  12. data/Rakefile +98 -0
  13. data/bin/jubilee +6 -0
  14. data/bin/jubilee_d +13 -0
  15. data/examples/chatapp/Gemfile +5 -0
  16. data/examples/chatapp/Gemfile.lock +27 -0
  17. data/examples/chatapp/README.md +17 -0
  18. data/examples/chatapp/app.rb +57 -0
  19. data/examples/chatapp/config.ru +3 -0
  20. data/examples/chatapp/public/assets/javascripts/application.js +67 -0
  21. data/examples/chatapp/public/assets/javascripts/jquery.js +5 -0
  22. data/examples/chatapp/public/assets/javascripts/sockjs-0.3.4.min.js +27 -0
  23. data/examples/chatapp/public/assets/javascripts/vertxbus.js +216 -0
  24. data/examples/chatapp/public/assets/stylesheets/application.css +29 -0
  25. data/examples/client/sockjs-0.3.4.min.js +27 -0
  26. data/examples/client/vertxbus.js +216 -0
  27. data/examples/jubilee.conf.rb +12 -0
  28. data/examples/jubilee/keystore.jks +0 -0
  29. data/examples/jubilee/server-keystore.jks +0 -0
  30. data/jars/hazelcast-2.6.3.jar +0 -0
  31. data/jars/jackson-annotations-2.2.2.jar +0 -0
  32. data/jars/jackson-core-2.2.2.jar +0 -0
  33. data/jars/jackson-databind-2.2.2.jar +0 -0
  34. data/jars/netty-all-4.0.13.Final.jar +0 -0
  35. data/jars/vertx-core-2.1M3-SNAPSHOT.jar +0 -0
  36. data/jars/vertx-hazelcast-2.1M3-SNAPSHOT.jar +0 -0
  37. data/java/src/jubilee/JubileeService.java +20 -0
  38. data/java/src/org/jruby/jubilee/Const.java +32 -0
  39. data/java/src/org/jruby/jubilee/RackApplication.java +103 -0
  40. data/java/src/org/jruby/jubilee/RackEnvironment.java +150 -0
  41. data/java/src/org/jruby/jubilee/RackEnvironmentHash.java +449 -0
  42. data/java/src/org/jruby/jubilee/RackInput.java +62 -0
  43. data/java/src/org/jruby/jubilee/RackResponse.java +11 -0
  44. data/java/src/org/jruby/jubilee/RubyHttpServerResponse.java +88 -0
  45. data/java/src/org/jruby/jubilee/RubyServer.java +171 -0
  46. data/java/src/org/jruby/jubilee/deploy/Starter.java +26 -0
  47. data/java/src/org/jruby/jubilee/impl/RubyIORackInput.java +201 -0
  48. data/java/src/org/jruby/jubilee/impl/RubyNullIO.java +107 -0
  49. data/java/src/org/jruby/jubilee/utils/RubyHelper.java +37 -0
  50. data/java/src/org/jruby/jubilee/vertx/JubileeVertx.java +38 -0
  51. data/jubilee.gemspec +201 -0
  52. data/lib/jubilee.rb +17 -0
  53. data/lib/jubilee/application.rb +13 -0
  54. data/lib/jubilee/cli.rb +127 -0
  55. data/lib/jubilee/configuration.rb +177 -0
  56. data/lib/jubilee/const.rb +40 -0
  57. data/lib/jubilee/jubilee.jar +0 -0
  58. data/lib/jubilee/response.rb +69 -0
  59. data/lib/jubilee/server.rb +12 -0
  60. data/lib/jubilee/version.rb +10 -0
  61. data/lib/rack/chunked.rb +38 -0
  62. data/lib/rack/handler/jubilee.rb +44 -0
  63. data/lib/vertx.rb +26 -0
  64. data/lib/vertx/README.md +7 -0
  65. data/lib/vertx/buffer.rb +251 -0
  66. data/lib/vertx/event_bus.rb +206 -0
  67. data/lib/vertx/shared_data.rb +214 -0
  68. data/spec/apps/rack/basic/config.ru +50 -0
  69. data/spec/apps/rails4/basic/.gitignore +16 -0
  70. data/spec/apps/rails4/basic/Gemfile +41 -0
  71. data/spec/apps/rails4/basic/Gemfile.lock +127 -0
  72. data/spec/apps/rails4/basic/README.rdoc +28 -0
  73. data/spec/apps/rails4/basic/Rakefile +6 -0
  74. data/spec/apps/rails4/basic/app/assets/images/.keep +0 -0
  75. data/spec/apps/rails4/basic/app/assets/images/rails.png +0 -0
  76. data/spec/apps/rails4/basic/app/assets/javascripts/application.js +12 -0
  77. data/spec/apps/rails4/basic/app/assets/stylesheets/application.css +13 -0
  78. data/spec/apps/rails4/basic/app/controllers/application_controller.rb +5 -0
  79. data/spec/apps/rails4/basic/app/controllers/concerns/.keep +0 -0
  80. data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb +11 -0
  81. data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb.erb +11 -0
  82. data/spec/apps/rails4/basic/app/controllers/root_controller.rb +14 -0
  83. data/spec/apps/rails4/basic/app/helpers/application_helper.rb +2 -0
  84. data/spec/apps/rails4/basic/app/mailers/.keep +0 -0
  85. data/spec/apps/rails4/basic/app/models/.keep +0 -0
  86. data/spec/apps/rails4/basic/app/models/concerns/.keep +0 -0
  87. data/spec/apps/rails4/basic/app/views/layouts/application.html.erb +14 -0
  88. data/spec/apps/rails4/basic/app/views/reloader/index.html.erb +1 -0
  89. data/spec/apps/rails4/basic/app/views/root/index.html.erb +8 -0
  90. data/spec/apps/rails4/basic/app/views/root/streaming.html.erb +6 -0
  91. data/spec/apps/rails4/basic/bin/bundle +3 -0
  92. data/spec/apps/rails4/basic/bin/rails +4 -0
  93. data/spec/apps/rails4/basic/bin/rake +4 -0
  94. data/spec/apps/rails4/basic/config.ru +4 -0
  95. data/spec/apps/rails4/basic/config/application.rb +23 -0
  96. data/spec/apps/rails4/basic/config/boot.rb +4 -0
  97. data/spec/apps/rails4/basic/config/database.yml +20 -0
  98. data/spec/apps/rails4/basic/config/environment.rb +5 -0
  99. data/spec/apps/rails4/basic/config/environments/development.rb +29 -0
  100. data/spec/apps/rails4/basic/config/environments/production.rb +80 -0
  101. data/spec/apps/rails4/basic/config/environments/test.rb +36 -0
  102. data/spec/apps/rails4/basic/config/initializers/backtrace_silencers.rb +7 -0
  103. data/spec/apps/rails4/basic/config/initializers/filter_parameter_logging.rb +4 -0
  104. data/spec/apps/rails4/basic/config/initializers/inflections.rb +16 -0
  105. data/spec/apps/rails4/basic/config/initializers/mime_types.rb +5 -0
  106. data/spec/apps/rails4/basic/config/initializers/secret_token.rb +12 -0
  107. data/spec/apps/rails4/basic/config/initializers/session_store.rb +2 -0
  108. data/spec/apps/rails4/basic/config/initializers/wrap_parameters.rb +14 -0
  109. data/spec/apps/rails4/basic/config/locales/en.yml +23 -0
  110. data/spec/apps/rails4/basic/config/routes.rb +5 -0
  111. data/spec/apps/rails4/basic/db/seeds.rb +7 -0
  112. data/spec/apps/rails4/basic/lib/assets/.keep +0 -0
  113. data/spec/apps/rails4/basic/lib/tasks/.keep +0 -0
  114. data/spec/apps/rails4/basic/public/404.html +58 -0
  115. data/spec/apps/rails4/basic/public/422.html +58 -0
  116. data/spec/apps/rails4/basic/public/500.html +57 -0
  117. data/spec/apps/rails4/basic/public/favicon.ico +0 -0
  118. data/spec/apps/rails4/basic/public/robots.txt +5 -0
  119. data/spec/apps/rails4/basic/public/some_page.html +7 -0
  120. data/spec/apps/rails4/basic/test/controllers/.keep +0 -0
  121. data/spec/apps/rails4/basic/test/fixtures/.keep +0 -0
  122. data/spec/apps/rails4/basic/test/helpers/.keep +0 -0
  123. data/spec/apps/rails4/basic/test/integration/.keep +0 -0
  124. data/spec/apps/rails4/basic/test/mailers/.keep +0 -0
  125. data/spec/apps/rails4/basic/test/models/.keep +0 -0
  126. data/spec/apps/rails4/basic/test/test_helper.rb +15 -0
  127. data/spec/apps/rails4/basic/vendor/assets/javascripts/.keep +0 -0
  128. data/spec/apps/rails4/basic/vendor/assets/stylesheets/.keep +0 -0
  129. data/spec/apps/sinatra/basic/Gemfile +4 -0
  130. data/spec/apps/sinatra/basic/Gemfile.lock +20 -0
  131. data/spec/apps/sinatra/basic/basic.rb +27 -0
  132. data/spec/apps/sinatra/basic/config.ru +7 -0
  133. data/spec/apps/sinatra/basic/public/some_page.html +7 -0
  134. data/spec/apps/sinatra/basic/views/index.erb +4 -0
  135. data/spec/apps/sinatra/basic/views/posted.haml +2 -0
  136. data/spec/apps/sinatra/basic/views/poster.haml +4 -0
  137. data/spec/apps/sinatra/basic/views/request_mapping.haml +4 -0
  138. data/spec/integration/basic_rack_spec.rb +89 -0
  139. data/spec/integration/basic_rails4_spec.rb +64 -0
  140. data/spec/integration/basic_sinatra_spec.rb +80 -0
  141. data/spec/spec_helper.rb +13 -0
  142. data/test/.ruby-version +1 -0
  143. data/test/config/app.rb +5 -0
  144. data/test/jubilee/test_cli.rb +11 -0
  145. data/test/jubilee/test_configuration.rb +31 -0
  146. data/test/jubilee/test_rack_server.rb +137 -0
  147. data/test/jubilee/test_response.rb +272 -0
  148. data/test/jubilee/test_server.rb +72 -0
  149. data/test/jubilee/test_upload.rb +301 -0
  150. data/test/sinatra_app/app.rb +31 -0
  151. data/test/sinatra_app/config.ru +6 -0
  152. data/test/sinatra_app/public/test.html +10 -0
  153. data/test/sinatra_app/unicorn.conf.rb +29 -0
  154. data/test/test_helper.rb +93 -0
  155. metadata +242 -0
@@ -0,0 +1,272 @@
1
+ require 'test_helper'
2
+ require 'timeout'
3
+ require 'socket'
4
+ class TestResponse < 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 = 8080
26
+
27
+ @server = Jubilee::Server.new @simple
28
+ @server.start
29
+ sleep 0.1
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 valid_response(size)
48
+ Regexp.new("HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{size}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n", true)
49
+ end
50
+
51
+ def test_one_with_content_length
52
+ @client << @valid_request
53
+ sz = @body[0].size.to_s
54
+
55
+ assert_match valid_response(sz), lines(7)
56
+ assert_equal "Hello", @client.read(5)
57
+ end
58
+
59
+ def test_two_back_to_back
60
+ @client << @valid_request
61
+ sz = @body[0].size.to_s
62
+
63
+ assert_match valid_response(sz), lines(7)
64
+ assert_equal "Hello", @client.read(5)
65
+
66
+ @client << @valid_request
67
+ sz = @body[0].size.to_s
68
+
69
+ assert_match valid_response(sz), lines(7)
70
+ assert_equal "Hello", @client.read(5)
71
+ end
72
+
73
+ def test_post_then_get
74
+ @client << @valid_post
75
+ sz = @body[0].size.to_s
76
+
77
+ assert_match valid_response(sz), lines(7)
78
+ assert_equal "Hello", @client.read(5)
79
+
80
+ @client << @valid_request
81
+ sz = @body[0].size.to_s
82
+
83
+ assert_match valid_response(sz), lines(7)
84
+ assert_equal "Hello", @client.read(5)
85
+ end
86
+
87
+ =begin
88
+ def test_no_body_then_get
89
+ @client << @valid_no_body
90
+ assert_match %r{HTTP/1.1 204 No Content\r\nX-Header: Works(.*?\r\n)*?Connection: keep-alive\r\n\r\n}, lines(6)
91
+
92
+ @client << @valid_request
93
+ sz = @body[0].size.to_s
94
+
95
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\n\r\n}, lines(7)
96
+ assert_equal "Hello", @client.read(5)
97
+ end
98
+ =end
99
+
100
+ def test_chunked
101
+ @body << "Chunked"
102
+
103
+ @client << @valid_request
104
+
105
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\nServer(.*\r\n)*?Transfer-Encoding: chunked\r\nDate(.*?)\r\n\r\n5\r\nHello\r\n7\r\nChunked\r\n0\r\n\r\n}, lines(13)
106
+ end
107
+
108
+ def test_no_chunked_in_http10
109
+ @body << "Chunked"
110
+
111
+ @client << @http10_request
112
+
113
+ assert_match %r{HTTP/1.0 200 OK\r\nX-Header: Works(.*?\r\n)*?Connection: close\r\nDate(.*?)\r\n\r\n}, lines(6)
114
+ assert_equal "HelloChunked", @client.read
115
+ end
116
+
117
+ def test_hex
118
+ str = "This is longer and will be in hex"
119
+ @body << str
120
+
121
+ @client << @valid_request
122
+
123
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Transfer-Encoding: chunked\r\nDate(.*?)\r\n\r\n5\r\nHello\r\n#{str.size.to_s(16)}\r\n#{str}\r\n0\r\n\r\n}, lines(13)
124
+
125
+ end
126
+
127
+ def test_client11_close
128
+ @client << @close_request
129
+ sz = @body[0].size.to_s
130
+
131
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: close\r\nDate(.*?)\r\n\r\n}, lines(7)
132
+ assert_equal "Hello", @client.read(5)
133
+ end
134
+
135
+ def test_client10_close
136
+ @client << @http10_request
137
+ sz = @body[0].size.to_s
138
+
139
+ assert_match %r{HTTP/1.0 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: close\r\nDate(.*?)\r\n\r\n}, lines(7)
140
+ assert_equal "Hello", @client.read(5)
141
+ end
142
+
143
+ def test_one_with_keep_alive_header
144
+ @client << @keep_request
145
+ sz = @body[0].size.to_s
146
+
147
+ assert_match %r{HTTP/1.0 200 OK\r\nX-Header: Works\r\nServer(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
148
+ assert_equal "Hello", @client.read(5)
149
+ end
150
+
151
+ =begin
152
+ def test_persistent_timeout
153
+ @server.persistent_timeout = 2
154
+ @client << @valid_request
155
+ sz = @body[0].size.to_s
156
+
157
+ assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
158
+ assert_equal "Hello", @client.read(5)
159
+
160
+ sleep 3
161
+
162
+ assert_raises EOFError do
163
+ @client.read_nonblock(1)
164
+ end
165
+ end
166
+ =end
167
+
168
+ def test_app_sets_content_length
169
+ @body = ["hello", " world"]
170
+ @headers['Content-Length'] = "11"
171
+
172
+ @client << @valid_request
173
+
174
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: 11\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
175
+ assert_equal "hello world", @client.read(11)
176
+ end
177
+
178
+ def test_allow_app_to_chunk_itself
179
+ skip "body should not be chunked before sent to jubilee"
180
+ @headers = {'Transfer-Encoding' => "chunked" }
181
+
182
+ @body = ["5\r\nhello\r\n0\r\n\r\n"]
183
+
184
+ @client << @valid_request
185
+
186
+ assert_match %r{HTTP/1.1 200 OK\r\n(.*?\r\n)*?Transfer-Encoding: chunked\r\nDate(.*?)\r\n\r\n5\r\nhello\r\n0\r\n}, lines(10)
187
+ end
188
+
189
+
190
+ def test_two_requests_in_one_chunk
191
+ @server.persistent_timeout = 3
192
+
193
+ req = @valid_request.to_s
194
+ req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
195
+
196
+ @client << req
197
+
198
+ sz = @body[0].size.to_s
199
+
200
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
201
+ assert_equal "Hello", @client.read(5)
202
+
203
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
204
+ assert_equal "Hello", @client.read(5)
205
+ end
206
+
207
+ def test_second_request_not_in_first_req_body
208
+ @server.persistent_timeout = 3
209
+
210
+ req = @valid_request.to_s
211
+ req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
212
+
213
+ @client << req
214
+
215
+ sz = @body[0].size.to_s
216
+
217
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
218
+ assert_equal "Hello", @client.read(5)
219
+
220
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7)
221
+ assert_equal "Hello", @client.read(5)
222
+
223
+ #assert_kind_of Jubilee::NullIO, @inputs[0]
224
+ #assert_kind_of Jubilee::NullIO, @inputs[1]
225
+ end
226
+
227
+ def test_keepalive_doesnt_starve_clients
228
+ sz = @body[0].size.to_s
229
+
230
+ @client << @valid_request
231
+
232
+ c2 = TCPSocket.new @host, @port
233
+ c2 << @valid_request
234
+
235
+ out = IO.select([c2], nil, nil, 1)
236
+
237
+ assert out, "select returned nil"
238
+ assert_equal c2, out.first.first
239
+
240
+ assert_match %r{HTTP/1.1 200 OK\r\nX-Header: Works\r\n(.*?\r\n)*?Content-Length: #{sz}\r\nConnection: keep-alive\r\nDate(.*?)\r\n\r\n}, lines(7, c2)
241
+ assert_equal "Hello", c2.read(5)
242
+ end
243
+
244
+ =begin
245
+ def test_client_shutdown_writes
246
+ bs = 15609315 * rand
247
+ sock = TCPSocket.new('127.0.0.1', @port)
248
+ sock.syswrite("PUT /hello HTTP/1.1\r\n")
249
+ sock.syswrite("Host: example.com\r\n")
250
+ sock.syswrite("Transfer-Encoding: chunked\r\n")
251
+ sock.syswrite("Trailer: X-Foo\r\n")
252
+ sock.syswrite("\r\n")
253
+ sock.syswrite("%x\r\n" % [ bs ])
254
+ sock.syswrite("F" * bs)
255
+ sock.syswrite("\r\n0\r\nX-")
256
+ "Foo: bar\r\n\r\n".each_byte do |x|
257
+ sock.syswrite x.chr
258
+ sleep 0.05
259
+ end
260
+ # we wrote the entire request before shutting down, server should
261
+ # continue to process our request and never hit EOFError on our sock
262
+ sock.shutdown(Socket::SHUT_WR)
263
+ buf = sock.read
264
+ assert_equal 'Hello', buf.split(/\r\n\r\n/).last
265
+ next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
266
+ assert_equal 'Hello', next_client
267
+ lines = File.readlines("test_stderr.#$$.log")
268
+ assert lines.grep(/^Unicorn::ClientShutdown: /).empty?
269
+ assert_nil sock.close
270
+ end
271
+ =end
272
+ end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+ require 'net/http'
3
+
4
+ class TestJubileeServer < MiniTest::Unit::TestCase
5
+ def setup
6
+ @host, @port = "localhost", 8080
7
+ @server = nil
8
+ end
9
+
10
+ def teardown
11
+ @server.stop if @server
12
+ end
13
+
14
+ def test_server_lambda
15
+ app = lambda {|env| [200, {"Content-Type" => "text/plain"}, ["http"]] }
16
+ @server = Jubilee::Server.new(app)
17
+ @server.start
18
+ sleep 0.1
19
+
20
+ http, body = Net::HTTP.new(@host, @port), nil
21
+ http.start do
22
+ req = Net::HTTP::Get.new "/", {}
23
+ http.request(req) do |resp|
24
+ body = resp.body
25
+ end
26
+ end
27
+ assert_equal "http", body
28
+ end
29
+
30
+ def test_server_embeded
31
+ config = Jubilee::Configuration.new(rackup: File.join(File.dirname(__FILE__), "../config/app.rb"))
32
+ @server = Jubilee::Server.new(config.app)
33
+ @server.start
34
+ sleep 0.1
35
+ http, body = Net::HTTP.new(@host, @port), nil
36
+ http.start do
37
+ req = Net::HTTP::Get.new "/", {}
38
+ http.request(req) do |resp|
39
+ body = resp.body
40
+ end
41
+ end
42
+ assert_equal "embeded app", body
43
+ end
44
+
45
+ def test_large_post_body
46
+ skip
47
+ end
48
+
49
+ def test_url_scheme_for_https
50
+ app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
51
+ @server = Jubilee::Server.new(app, {port:@port, ssl:true,
52
+ keystore_path: File.join(File.dirname(__FILE__), "../../examples/jubilee/server-keystore.jks"),
53
+ keystore_password: "wibble"})
54
+ @server.start
55
+ sleep 0.1
56
+ http = Net::HTTP.new @host, @port
57
+ http.use_ssl = true
58
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
59
+
60
+ body = nil
61
+ http.start do
62
+ req = Net::HTTP::Get.new "/", {}
63
+
64
+ http.request(req) do |rep|
65
+ body = rep.body
66
+ end
67
+ end
68
+
69
+ assert_equal "https", body
70
+ end
71
+
72
+ end
@@ -0,0 +1,301 @@
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 = 8080
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 += buf.size
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
+
43
+ if expect_size = env['HTTP_X_EXPECT_SIZE']
44
+ if expect_size.to_i == i
45
+ resp[:expect_size_match] = true
46
+ end
47
+ end
48
+ resp[:size] = i
49
+ resp[:expect_size] = expect_size
50
+
51
+ [ 200, @hdr.merge({'X-Resp' => resp.inspect}), [] ]
52
+ end
53
+ end
54
+
55
+ def teardown
56
+ @server.stop
57
+ @random.close
58
+ end
59
+
60
+ def test_put
61
+ start_server(@sha1_app)
62
+ sock = TCPSocket.new(@addr, @port)
63
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
64
+ @count.times do |i|
65
+ buf = @random.sysread(@bs)
66
+ @sha1.update(buf)
67
+ sock.syswrite(buf)
68
+ end
69
+ read = sock.read.split(/\r\n/)
70
+ assert_equal "HTTP/1.0 200 OK", read[0]
71
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
72
+ assert_equal @sha1.hexdigest, resp[:sha1]
73
+ end
74
+
75
+ def test_put_content_md5
76
+ start_server(@sha1_app)
77
+ skip "Vert.x doesn't hendle trailing headers"
78
+ md5 = Digest::MD5.new
79
+ sock = TCPSocket.new(@addr, @port)
80
+ sock.syswrite("PUT / HTTP/1.0\r\nTransfer-Encoding: chunked\r\n" \
81
+ "Trailer: Content-MD5\r\n\r\n")
82
+ @count.times do |i|
83
+ buf = @random.sysread(@bs)
84
+ @sha1.update(buf)
85
+ md5.update(buf)
86
+ sock.syswrite("#{'%x' % buf.size}\r\n")
87
+ sock.syswrite(buf << "\r\n")
88
+ end
89
+ sock.syswrite("0\r\n")
90
+
91
+ content_md5 = [ md5.digest! ].pack('m').strip.freeze
92
+ sock.syswrite("Content-MD5: #{content_md5}\r\n\r\n")
93
+ read = sock.read.split(/\r\n/)
94
+ assert_equal "HTTP/1.0 200 OK", read[0]
95
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
96
+ assert_equal @sha1.hexdigest, resp[:sha1]
97
+ assert_equal content_md5, resp[:content_md5]
98
+ end
99
+
100
+ def test_put_trickle_small
101
+ start_server(@sha1_app)
102
+ @count, @bs = 2, 128
103
+ assert_equal 256, length
104
+ sock = TCPSocket.new(@addr, @port)
105
+ hdr = "PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n"
106
+ @count.times do
107
+ buf = @random.sysread(@bs)
108
+ @sha1.update(buf)
109
+ hdr << buf
110
+ sock.syswrite(hdr)
111
+ hdr = ''
112
+ sleep 0.6
113
+ end
114
+ read = sock.read.split(/\r\n/)
115
+ assert_equal "HTTP/1.0 200 OK", read[0]
116
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
117
+ assert_equal @sha1.hexdigest, resp[:sha1]
118
+ end
119
+
120
+ def test_put_keepalive_truncates_small_overwrite
121
+ start_server(@sha1_app)
122
+ sock = TCPSocket.new(@addr, @port)
123
+ to_upload = length + 1
124
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
125
+ @count.times do
126
+ buf = @random.sysread(@bs)
127
+ @sha1.update(buf)
128
+ sock.syswrite(buf)
129
+ end
130
+ sock.syswrite('12345') # write 4 bytes more than we expected
131
+ @sha1.update('1')
132
+
133
+ buf = sock.readpartial(4096)
134
+ while buf !~ /\r\n\r\n/
135
+ buf << sock.readpartial(4096)
136
+ end
137
+ read = buf.split(/\r\n/)
138
+ assert_equal "HTTP/1.0 200 OK", read[0]
139
+ resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
140
+ #assert_equal to_upload, resp[:size]
141
+ assert_equal @sha1.hexdigest, resp[:sha1]
142
+ end
143
+
144
+ def test_put_excessive_overwrite_closed
145
+ tmp = Tempfile.new('overwrite_check')
146
+ tmp.sync = true
147
+ start_server(lambda { |env|
148
+ nr = 0
149
+ while buf = env['rack.input'].read(65536)
150
+ nr += buf.size
151
+ end
152
+ tmp.write(nr.to_s)
153
+ [ 200, @hdr, [] ]
154
+ })
155
+ sock = TCPSocket.new(@addr, @port)
156
+ buf = ' ' * @bs
157
+ sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
158
+
159
+ @count.times { sock.syswrite(buf) }
160
+ assert_raises(Errno::ECONNRESET, Errno::EPIPE) do
161
+ 16384.times { sock.syswrite(buf) }
162
+ end
163
+ sock.gets
164
+ tmp.rewind
165
+ assert_equal length, tmp.read.to_i
166
+ end
167
+
168
+ def test_uncomfortable_with_onenine_encodings
169
+ # POSIX doesn't require all of these to be present on a system
170
+ which('curl') or return
171
+ which('sha1sum') or return
172
+ which('dd') or return
173
+
174
+ start_server(@sha1_app)
175
+
176
+ tmp = Tempfile.new('dd_dest')
177
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
178
+ "bs=#{@bs}", "count=#{@count}"),
179
+ "dd #@random to #{tmp}")
180
+ sha1_re = %r!\b([a-f0-9]{40})\b!
181
+ sha1_out = `sha1sum #{tmp.path}`
182
+
183
+ assert $?.success?, 'sha1sum ran OK'
184
+
185
+ assert_match(sha1_re, sha1_out)
186
+ sha1 = sha1_re.match(sha1_out)[1]
187
+ resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
188
+
189
+ assert $?.success?, 'curl ran OK'
190
+ assert_match(%r!\b#{sha1}\b!, resp)
191
+ assert_match(/sysread_read_byte_match/, resp)
192
+
193
+ # small StringIO path
194
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
195
+ "bs=1024", "count=1"),
196
+ "dd #@random to #{tmp}")
197
+ sha1_re = %r!\b([a-f0-9]{40})\b!
198
+ sha1_out = `sha1sum #{tmp.path}`
199
+ assert $?.success?, 'sha1sum ran OK'
200
+
201
+ assert_match(sha1_re, sha1_out)
202
+ sha1 = sha1_re.match(sha1_out)[1]
203
+ resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
204
+ assert $?.success?, 'curl ran OK'
205
+ assert_match(%r!\b#{sha1}\b!, resp)
206
+ assert_match(/sysread_read_byte_match/, resp)
207
+ end
208
+
209
+ =begin
210
+ def test_chunked_upload_via_curl
211
+ # POSIX doesn't require all of these to be present on a system
212
+ which('curl') or return
213
+ which('sha1sum') or return
214
+ which('dd') or return
215
+
216
+ start_server(@sha1_app)
217
+
218
+ tmp = Tempfile.new('dd_dest')
219
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
220
+ "bs=#{@bs}", "count=#{@count}"),
221
+ "dd #@random to #{tmp}")
222
+ sha1_re = %r!\b([a-f0-9]{40})\b!
223
+ sha1_out = `sha1sum #{tmp.path}`
224
+ assert $?.success?, 'sha1sum ran OK'
225
+
226
+ assert_match(sha1_re, sha1_out)
227
+ sha1 = sha1_re.match(sha1_out)[1]
228
+ cmd = "curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
229
+ -isSf --no-buffer -T- " \
230
+ "http://#@addr:#@port/"
231
+ resp = Tempfile.new('resp')
232
+ resp.sync = true
233
+
234
+ rd, wr = IO.pipe
235
+ wr.sync = rd.sync = true
236
+ pid = fork {
237
+ STDIN.reopen(rd)
238
+ rd.close
239
+ wr.close
240
+ STDOUT.reopen(resp)
241
+ exec cmd
242
+ }
243
+ rd.close
244
+
245
+ tmp.rewind
246
+ @count.times { |i|
247
+ wr.write(tmp.read(@bs))
248
+ sleep(rand / 10) if 0 == i % 8
249
+ }
250
+ wr.close
251
+ pid, status = Process.waitpid2(pid)
252
+
253
+ resp.rewind
254
+ resp = resp.read
255
+ assert status.success?, 'curl ran OK'
256
+ assert_match(%r!\b#{sha1}\b!, resp)
257
+ assert_match(/sysread_read_byte_match/, resp)
258
+ assert_match(/expect_size_match/, resp)
259
+ end
260
+ =end
261
+
262
+ def test_curl_chunked_small
263
+ # POSIX doesn't require all of these to be present on a system
264
+ which('curl') or return
265
+ which('sha1sum') or return
266
+ which('dd') or return
267
+
268
+ start_server(@sha1_app)
269
+
270
+ tmp = Tempfile.new('dd_dest')
271
+ # small StringIO path
272
+ assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
273
+ "bs=1024", "count=1"),
274
+ "dd #@random to #{tmp}")
275
+ sha1_re = %r!\b([a-f0-9]{40})\b!
276
+ sha1_out = `sha1sum #{tmp.path}`
277
+ assert $?.success?, 'sha1sum ran OK'
278
+
279
+ assert_match(sha1_re, sha1_out)
280
+ sha1 = sha1_re.match(sha1_out)[1]
281
+ resp = `curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
282
+ -isSf --no-buffer -T- http://#@addr:#@port/ < #{tmp.path}`
283
+ assert $?.success?, 'curl ran OK'
284
+ assert_match(%r!\b#{sha1}\b!, resp)
285
+ assert_match(/sysread_read_byte_match/, resp)
286
+ assert_match(/expect_size_match/, resp)
287
+ end
288
+
289
+ private
290
+
291
+ def length
292
+ @bs * @count
293
+ end
294
+
295
+ def start_server(app)
296
+ @server = Jubilee::Server.new app
297
+ @server.start
298
+ sleep 0.1
299
+ end
300
+
301
+ end