jubilee 1.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
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