puma 2.0.0.b5 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1598 -0
  3. data/LICENSE +23 -20
  4. data/README.md +222 -62
  5. data/bin/puma-wild +31 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/architecture.md +37 -0
  8. data/docs/deployment.md +113 -0
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +13 -0
  14. data/docs/jungle/rc.d/README.md +74 -0
  15. data/docs/jungle/rc.d/puma +61 -0
  16. data/docs/jungle/rc.d/puma.conf +10 -0
  17. data/docs/jungle/upstart/README.md +61 -0
  18. data/docs/jungle/upstart/puma-manager.conf +31 -0
  19. data/docs/jungle/upstart/puma.conf +69 -0
  20. data/docs/nginx.md +5 -10
  21. data/docs/plugins.md +38 -0
  22. data/docs/restart.md +41 -0
  23. data/docs/signals.md +97 -0
  24. data/docs/systemd.md +228 -0
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  26. data/ext/puma_http11/extconf.rb +23 -2
  27. data/ext/puma_http11/http11_parser.c +301 -482
  28. data/ext/puma_http11/http11_parser.h +13 -11
  29. data/ext/puma_http11/http11_parser.java.rl +26 -42
  30. data/ext/puma_http11/http11_parser.rl +22 -21
  31. data/ext/puma_http11/http11_parser_common.rl +5 -5
  32. data/ext/puma_http11/mini_ssl.c +377 -18
  33. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -107
  34. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +137 -170
  35. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +265 -191
  36. data/ext/puma_http11/puma_http11.c +57 -81
  37. data/lib/puma.rb +25 -4
  38. data/lib/puma/accept_nonblock.rb +7 -1
  39. data/lib/puma/app/status.rb +61 -24
  40. data/lib/puma/binder.rb +212 -78
  41. data/lib/puma/cli.rb +149 -644
  42. data/lib/puma/client.rb +316 -65
  43. data/lib/puma/cluster.rb +659 -0
  44. data/lib/puma/commonlogger.rb +108 -0
  45. data/lib/puma/configuration.rb +279 -180
  46. data/lib/puma/const.rb +126 -39
  47. data/lib/puma/control_cli.rb +183 -96
  48. data/lib/puma/detect.rb +20 -1
  49. data/lib/puma/dsl.rb +776 -0
  50. data/lib/puma/events.rb +91 -23
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +9 -5
  53. data/lib/puma/launcher.rb +487 -0
  54. data/lib/puma/minissl.rb +239 -93
  55. data/lib/puma/minissl/context_builder.rb +76 -0
  56. data/lib/puma/null_io.rb +22 -12
  57. data/lib/puma/plugin.rb +111 -0
  58. data/lib/puma/plugin/tmp_restart.rb +36 -0
  59. data/lib/puma/rack/builder.rb +297 -0
  60. data/lib/puma/rack/urlmap.rb +93 -0
  61. data/lib/puma/rack_default.rb +9 -0
  62. data/lib/puma/reactor.rb +290 -43
  63. data/lib/puma/runner.rb +163 -0
  64. data/lib/puma/server.rb +493 -126
  65. data/lib/puma/single.rb +66 -0
  66. data/lib/puma/state_file.rb +34 -0
  67. data/lib/puma/thread_pool.rb +228 -47
  68. data/lib/puma/util.rb +115 -0
  69. data/lib/rack/handler/puma.rb +78 -31
  70. data/tools/Dockerfile +16 -0
  71. data/tools/trickletest.rb +44 -0
  72. metadata +60 -155
  73. data/COPYING +0 -55
  74. data/Gemfile +0 -8
  75. data/History.txt +0 -196
  76. data/Manifest.txt +0 -56
  77. data/Rakefile +0 -121
  78. data/TODO +0 -5
  79. data/docs/config.md +0 -0
  80. data/ext/puma_http11/io_buffer.c +0 -154
  81. data/lib/puma/capistrano.rb +0 -26
  82. data/lib/puma/compat.rb +0 -11
  83. data/lib/puma/daemon_ext.rb +0 -20
  84. data/lib/puma/delegation.rb +0 -11
  85. data/lib/puma/java_io_buffer.rb +0 -45
  86. data/lib/puma/rack_patch.rb +0 -25
  87. data/puma.gemspec +0 -45
  88. data/test/test_app_status.rb +0 -88
  89. data/test/test_cli.rb +0 -171
  90. data/test/test_config.rb +0 -16
  91. data/test/test_http10.rb +0 -27
  92. data/test/test_http11.rb +0 -126
  93. data/test/test_integration.rb +0 -150
  94. data/test/test_iobuffer.rb +0 -38
  95. data/test/test_minissl.rb +0 -22
  96. data/test/test_null_io.rb +0 -31
  97. data/test/test_persistent.rb +0 -238
  98. data/test/test_puma_server.rb +0 -128
  99. data/test/test_rack_handler.rb +0 -10
  100. data/test/test_rack_server.rb +0 -141
  101. data/test/test_thread_pool.rb +0 -146
  102. data/test/test_unix_socket.rb +0 -39
  103. data/test/test_ws.rb +0 -89
  104. data/tools/jungle/README.md +0 -54
  105. data/tools/jungle/puma +0 -332
  106. data/tools/jungle/run-puma +0 -3
@@ -1,16 +0,0 @@
1
- require 'test/unit'
2
-
3
- require 'puma'
4
- require 'puma/configuration'
5
-
6
- class TestConfigFile < Test::Unit::TestCase
7
- def test_app_from_app_DSL
8
- opts = { :config_file => "test/config/app.rb" }
9
- conf = Puma::Configuration.new opts
10
- conf.load
11
-
12
- app = conf.app
13
-
14
- assert_equal [200, {}, ["embedded app"]], app.call(nil)
15
- end
16
- end
@@ -1,27 +0,0 @@
1
- require 'test/testhelp'
2
-
3
- class Http10ParserTest < Test::Unit::TestCase
4
- include Puma
5
-
6
- def test_parse_simple
7
- parser = HttpParser.new
8
- req = {}
9
- http = "GET / HTTP/1.0\r\n\r\n"
10
- nread = parser.execute(req, http, 0)
11
-
12
- assert nread == http.length, "Failed to parse the full HTTP request"
13
- assert parser.finished?, "Parser didn't finish"
14
- assert !parser.error?, "Parser had error"
15
- assert nread == parser.nread, "Number read returned from execute does not match"
16
-
17
- assert_equal '/', req['REQUEST_PATH']
18
- assert_equal 'HTTP/1.0', req['HTTP_VERSION']
19
- assert_equal '/', req['REQUEST_URI']
20
- assert_equal 'GET', req['REQUEST_METHOD']
21
- assert_nil req['FRAGMENT']
22
- assert_nil req['QUERY_STRING']
23
-
24
- parser.reset
25
- assert parser.nread == 0, "Number read after reset should be 0"
26
- end
27
- end
@@ -1,126 +0,0 @@
1
- # Copyright (c) 2011 Evan Phoenix
2
- # Copyright (c) 2005 Zed A. Shaw
3
-
4
- require 'test/testhelp'
5
-
6
- include Puma
7
-
8
- class Http11ParserTest < Test::Unit::TestCase
9
-
10
- def test_parse_simple
11
- parser = HttpParser.new
12
- req = {}
13
- http = "GET / HTTP/1.1\r\n\r\n"
14
- nread = parser.execute(req, http, 0)
15
-
16
- assert nread == http.length, "Failed to parse the full HTTP request"
17
- assert parser.finished?, "Parser didn't finish"
18
- assert !parser.error?, "Parser had error"
19
- assert nread == parser.nread, "Number read returned from execute does not match"
20
-
21
- assert_equal '/', req['REQUEST_PATH']
22
- assert_equal 'HTTP/1.1', req['HTTP_VERSION']
23
- assert_equal '/', req['REQUEST_URI']
24
- assert_equal 'GET', req['REQUEST_METHOD']
25
- assert_nil req['FRAGMENT']
26
- assert_nil req['QUERY_STRING']
27
-
28
- parser.reset
29
- assert parser.nread == 0, "Number read after reset should be 0"
30
- end
31
-
32
- def test_parse_dumbfuck_headers
33
- parser = HttpParser.new
34
- req = {}
35
- should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
36
- nread = parser.execute(req, should_be_good, 0)
37
- assert_equal should_be_good.length, nread
38
- assert parser.finished?
39
- assert !parser.error?
40
- end
41
-
42
- def test_parse_error
43
- parser = HttpParser.new
44
- req = {}
45
- bad_http = "GET / SsUTF/1.1"
46
-
47
- error = false
48
- begin
49
- parser.execute(req, bad_http, 0)
50
- rescue
51
- error = true
52
- end
53
-
54
- assert error, "failed to throw exception"
55
- assert !parser.finished?, "Parser shouldn't be finished"
56
- assert parser.error?, "Parser SHOULD have error"
57
- end
58
-
59
- def test_fragment_in_uri
60
- parser = HttpParser.new
61
- req = {}
62
- get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
63
- assert_nothing_raised do
64
- parser.execute(req, get, 0)
65
- end
66
- assert parser.finished?
67
- assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
68
- assert_equal 'posts-17408', req['FRAGMENT']
69
- end
70
-
71
- # lame random garbage maker
72
- def rand_data(min, max, readable=true)
73
- count = min + ((rand(max)+1) *10).to_i
74
- res = count.to_s + "/"
75
-
76
- if readable
77
- res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
78
- else
79
- res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
80
- end
81
-
82
- return res
83
- end
84
-
85
-
86
- def test_horrible_queries
87
- parser = HttpParser.new
88
-
89
- # then that large header names are caught
90
- 10.times do |c|
91
- get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
92
- assert_raises Puma::HttpParserError do
93
- parser.execute({}, get, 0)
94
- parser.reset
95
- end
96
- end
97
-
98
- # then that large mangled field values are caught
99
- 10.times do |c|
100
- get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
101
- assert_raises Puma::HttpParserError do
102
- parser.execute({}, get, 0)
103
- parser.reset
104
- end
105
- end
106
-
107
- # then large headers are rejected too
108
- get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
109
- get << "X-Test: test\r\n" * (80 * 1024)
110
- assert_raises Puma::HttpParserError do
111
- parser.execute({}, get, 0)
112
- parser.reset
113
- end
114
-
115
- # finally just that random garbage gets blocked all the time
116
- 10.times do |c|
117
- get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
118
- assert_raises Puma::HttpParserError do
119
- parser.execute({}, get, 0)
120
- parser.reset
121
- end
122
- end
123
-
124
- end
125
- end
126
-
@@ -1,150 +0,0 @@
1
- require "rbconfig"
2
- require 'test/unit'
3
- require 'socket'
4
- require 'timeout'
5
- require 'net/http'
6
- require 'tempfile'
7
-
8
- require 'puma/cli'
9
- require 'puma/control_cli'
10
-
11
- # These don't run on travis because they're too fragile
12
-
13
- class TestIntegration < Test::Unit::TestCase
14
- def setup
15
- @state_path = "test/test_puma.state"
16
- @bind_path = "test/test_server.sock"
17
- @control_path = "test/test_control.sock"
18
- @tcp_port = 9998
19
-
20
- @server = nil
21
- @script = nil
22
- end
23
-
24
- def teardown
25
- File.unlink @state_path rescue nil
26
- File.unlink @bind_path rescue nil
27
- File.unlink @control_path rescue nil
28
-
29
- if @server
30
- Process.kill "INT", @server.pid
31
- Process.wait @server.pid
32
- @server.close
33
- end
34
-
35
- if @script
36
- @script.close!
37
- end
38
- end
39
-
40
- def server(opts)
41
- core = "#{Gem.ruby} -rubygems -Ilib bin/puma"
42
- cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}"
43
- tf = Tempfile.new "puma-test"
44
- tf.puts "exec #{cmd}"
45
- tf.close
46
-
47
- @script = tf
48
-
49
- @server = IO.popen("sh #{tf.path}", "r")
50
-
51
- true while @server.gets =~ /Ctrl-C/
52
-
53
- sleep 1
54
-
55
- @server
56
- end
57
-
58
- def signal(which)
59
- Process.kill which, @server.pid
60
- end
61
-
62
- def test_stop_via_pumactl
63
- if defined?(JRUBY_VERSION) || RbConfig::CONFIG["host_os"] =~ /mingw|mswin/
64
- assert true
65
- return
66
- end
67
-
68
- sin = StringIO.new
69
- sout = StringIO.new
70
-
71
- cli = Puma::CLI.new %W!-q -S #{@state_path} -b unix://#{@bind_path} --control unix://#{@control_path} test/hello.ru!, sin, sout
72
-
73
- t = Thread.new do
74
- cli.run
75
- end
76
-
77
- sleep 1
78
-
79
- s = UNIXSocket.new @bind_path
80
- s << "GET / HTTP/1.0\r\n\r\n"
81
- assert_equal "Hello World", s.read.split("\r\n").last
82
-
83
- ccli = Puma::ControlCLI.new %W!-S #{@state_path} stop!, sout
84
-
85
- ccli.run
86
-
87
- assert_kind_of Thread, t.join(1), "server didn't stop"
88
- end
89
-
90
- def notest_restart_closes_keepalive_sockets
91
- server("-q test/hello.ru")
92
-
93
- s = TCPSocket.new "localhost", @tcp_port
94
- s << "GET / HTTP/1.1\r\n\r\n"
95
- true until s.gets == "\r\n"
96
-
97
- s.readpartial(20)
98
- signal :USR2
99
-
100
- true while @server.gets =~ /Ctrl-C/
101
- sleep 1
102
-
103
- s.write "GET / HTTP/1.1\r\n\r\n"
104
-
105
- assert_raises Errno::ECONNRESET do
106
- Timeout.timeout(2) do
107
- raise Errno::ECONNRESET unless s.read(2)
108
- end
109
- end
110
-
111
- s = TCPSocket.new "localhost", @tcp_port
112
- s << "GET / HTTP/1.0\r\n\r\n"
113
- assert_equal "Hello World", s.read.split("\r\n").last
114
- end
115
-
116
- def notest_restart_closes_keepalive_sockets_workers
117
- server("-q -w 2 test/hello.ru")
118
-
119
- s = TCPSocket.new "localhost", @tcp_port
120
- s << "GET / HTTP/1.1\r\n\r\n"
121
- true until s.gets == "\r\n"
122
-
123
- s.readpartial(20)
124
- signal :USR2
125
-
126
- true while @server.gets =~ /Ctrl-C/
127
- sleep 1
128
-
129
- s.write "GET / HTTP/1.1\r\n\r\n"
130
-
131
- assert_raises Errno::ECONNRESET do
132
- Timeout.timeout(2) do
133
- raise Errno::ECONNRESET unless s.read(2)
134
- end
135
- end
136
-
137
- s = TCPSocket.new "localhost", @tcp_port
138
- s << "GET / HTTP/1.0\r\n\r\n"
139
- assert_equal "Hello World", s.read.split("\r\n").last
140
- end
141
-
142
- def test_bad_query_string_outputs_400
143
- server "-q test/hello.ru 2>&1"
144
-
145
- s = TCPSocket.new "localhost", @tcp_port
146
- s << "GET /?h=% HTTP/1.0\r\n\r\n"
147
- data = s.read
148
- assert_equal "HTTP/1.1 400 Bad Request\r\n\r\n", data
149
- end
150
- end unless ENV['TRAVIS']
@@ -1,38 +0,0 @@
1
- require 'puma/io_buffer'
2
- require 'test/unit'
3
-
4
- class TestIOBuffer < Test::Unit::TestCase
5
- attr_accessor :iobuf
6
- def setup
7
- self.iobuf = Puma::IOBuffer.new
8
- end
9
-
10
- def test_initial_size
11
- assert_equal 0, iobuf.used
12
- assert iobuf.capacity > 0
13
- end
14
-
15
- def test_append_op
16
- iobuf << "abc"
17
- assert_equal "abc", iobuf.to_s
18
- iobuf << "123"
19
- assert_equal "abc123", iobuf.to_s
20
- assert_equal 6, iobuf.used
21
- end
22
-
23
- def test_append
24
- expected = "mary had a little lamb"
25
- iobuf.append("mary", " ", "had ", "a little", " lamb")
26
- assert_equal expected, iobuf.to_s
27
- assert_equal expected.length, iobuf.used
28
- end
29
-
30
- def test_reset
31
- iobuf << "content"
32
- assert_equal "content", iobuf.to_s
33
- iobuf.reset
34
- assert_equal 0, iobuf.used
35
- assert_equal "", iobuf.to_s
36
- end
37
-
38
- end
@@ -1,22 +0,0 @@
1
- require 'test/unit'
2
-
3
- require 'puma'
4
- require 'puma/minissl'
5
-
6
- class TestMiniSSL < Test::Unit::TestCase
7
-
8
- def test_raises_with_invalid_key_file
9
- ctx = Puma::MiniSSL::Context.new
10
-
11
- exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
12
- assert_equal("No such key file '/no/such/key'", exception.message)
13
- end unless defined? JRUBY_VERSION
14
-
15
- def test_raises_with_invalid_cert_file
16
- ctx = Puma::MiniSSL::Context.new
17
-
18
- exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
19
- assert_equal("No such cert file '/no/such/cert'", exception.message)
20
- end unless defined? JRUBY_VERSION
21
-
22
- end
@@ -1,31 +0,0 @@
1
- require 'puma/null_io'
2
- require 'test/unit'
3
-
4
- class TestNullIO < Test::Unit::TestCase
5
- attr_accessor :nio
6
- def setup
7
- self.nio = Puma::NullIO.new
8
- end
9
-
10
- def test_read_with_no_arguments
11
- assert_equal "", nio.read
12
- end
13
-
14
- def test_read_with_nil_length
15
- assert_equal "", nio.read(nil)
16
- end
17
-
18
- def test_read_with_zero_length
19
- assert_equal "", nio.read(0)
20
- end
21
-
22
- def test_read_with_positive_integer_length
23
- assert_nil nio.read(1)
24
- end
25
-
26
- def test_read_with_length_and_buffer
27
- buf = ""
28
- assert_nil nio.read(1,buf)
29
- assert_equal "", buf
30
- end
31
- end
@@ -1,238 +0,0 @@
1
- require 'puma'
2
- require 'test/unit'
3
- require 'timeout'
4
-
5
- class TestPersistent < Test::Unit::TestCase
6
- def setup
7
- @valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
8
- @close_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"
9
- @http10_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
10
- @keep_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: Keep-Alive\r\n\r\n"
11
-
12
- @valid_post = "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nhello"
13
- @valid_no_body = "GET / HTTP/1.1\r\nHost: test.com\r\nX-Status: 204\r\nContent-Type: text/plain\r\n\r\n"
14
-
15
- @headers = { "X-Header" => "Works" }
16
- @body = ["Hello"]
17
- @inputs = []
18
-
19
- @simple = lambda do |env|
20
- @inputs << env['rack.input']
21
- status = Integer(env['HTTP_X_STATUS'] || 200)
22
- [status, @headers, @body]
23
- end
24
-
25
- @host = "127.0.0.1"
26
- @port = 9988
27
-
28
- @server = Puma::Server.new @simple
29
- @server.add_tcp_listener "127.0.0.1", 9988
30
- @server.max_threads = 1
31
- @server.run
32
-
33
- @client = TCPSocket.new "127.0.0.1", 9988
34
- end
35
-
36
- def teardown
37
- @client.close
38
- @server.stop(true)
39
- end
40
-
41
- def lines(count, s=@client)
42
- str = ""
43
- timeout(5) do
44
- count.times { str << s.gets }
45
- end
46
- str
47
- end
48
-
49
- def test_one_with_content_length
50
- @client << @valid_request
51
- sz = @body[0].size.to_s
52
-
53
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
54
- assert_equal "Hello", @client.read(5)
55
- end
56
-
57
- def test_two_back_to_back
58
- @client << @valid_request
59
- sz = @body[0].size.to_s
60
-
61
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
62
- assert_equal "Hello", @client.read(5)
63
-
64
- @client << @valid_request
65
- sz = @body[0].size.to_s
66
-
67
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
68
- assert_equal "Hello", @client.read(5)
69
- end
70
-
71
- def test_post_then_get
72
- @client << @valid_post
73
- sz = @body[0].size.to_s
74
-
75
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
76
- assert_equal "Hello", @client.read(5)
77
-
78
- @client << @valid_request
79
- sz = @body[0].size.to_s
80
-
81
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
82
- assert_equal "Hello", @client.read(5)
83
- end
84
-
85
- def test_no_body_then_get
86
- @client << @valid_no_body
87
- assert_equal "HTTP/1.1 204 No Content\r\nX-Header: Works\r\n\r\n", lines(3)
88
-
89
- @client << @valid_request
90
- sz = @body[0].size.to_s
91
-
92
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
93
- assert_equal "Hello", @client.read(5)
94
- end
95
-
96
- def test_chunked
97
- @body << "Chunked"
98
-
99
- @client << @valid_request
100
-
101
- 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)
102
- end
103
-
104
- def test_no_chunked_in_http10
105
- @body << "Chunked"
106
-
107
- @client << @http10_request
108
-
109
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\n\r\n", lines(4)
110
- assert_equal "HelloChunked", @client.read
111
- end
112
-
113
- def test_hex
114
- str = "This is longer and will be in hex"
115
- @body << str
116
-
117
- @client << @valid_request
118
-
119
- 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)
120
-
121
- end
122
-
123
- def test_client11_close
124
- @client << @close_request
125
- sz = @body[0].size.to_s
126
-
127
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
128
- assert_equal "Hello", @client.read(5)
129
- end
130
-
131
- def test_client10_close
132
- @client << @http10_request
133
- sz = @body[0].size.to_s
134
-
135
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
136
- assert_equal "Hello", @client.read(5)
137
- end
138
-
139
- def test_one_with_keep_alive_header
140
- @client << @keep_request
141
- sz = @body[0].size.to_s
142
-
143
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: Keep-Alive\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
144
- assert_equal "Hello", @client.read(5)
145
- end
146
-
147
- def test_persistent_timeout
148
- @server.persistent_timeout = 2
149
- @client << @valid_request
150
- sz = @body[0].size.to_s
151
-
152
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
153
- assert_equal "Hello", @client.read(5)
154
-
155
- sleep 3
156
-
157
- assert_raises EOFError do
158
- @client.read_nonblock(1)
159
- end
160
- end
161
-
162
- def test_app_sets_content_length
163
- @body = ["hello", " world"]
164
- @headers['Content-Length'] = "11"
165
-
166
- @client << @valid_request
167
-
168
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: 11\r\n\r\n",
169
- lines(4)
170
- assert_equal "hello world", @client.read(11)
171
- end
172
-
173
- def test_allow_app_to_chunk_itself
174
- @headers = {'Transfer-Encoding' => "chunked" }
175
-
176
- @body = ["5\r\nhello\r\n0\r\n\r\n"]
177
-
178
- @client << @valid_request
179
-
180
- 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)
181
- end
182
-
183
-
184
- def test_two_requests_in_one_chunk
185
- @server.persistent_timeout = 3
186
-
187
- req = @valid_request.to_s
188
- req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
189
-
190
- @client << req
191
-
192
- sz = @body[0].size.to_s
193
-
194
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
195
- assert_equal "Hello", @client.read(5)
196
-
197
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
198
- assert_equal "Hello", @client.read(5)
199
- end
200
-
201
- def test_second_request_not_in_first_req_body
202
- @server.persistent_timeout = 3
203
-
204
- req = @valid_request.to_s
205
- req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
206
-
207
- @client << req
208
-
209
- sz = @body[0].size.to_s
210
-
211
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
212
- assert_equal "Hello", @client.read(5)
213
-
214
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
215
- assert_equal "Hello", @client.read(5)
216
-
217
- assert_kind_of Puma::NullIO, @inputs[0]
218
- assert_kind_of Puma::NullIO, @inputs[1]
219
- end
220
-
221
- def test_keepalive_doesnt_starve_clients
222
- sz = @body[0].size.to_s
223
-
224
- @client << @valid_request
225
-
226
- c2 = TCPSocket.new @host, @port
227
- c2 << @valid_request
228
-
229
- out = IO.select([c2], nil, nil, 1)
230
-
231
- assert out, "select returned nil"
232
- assert_equal c2, out.first.first
233
-
234
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4, c2)
235
- assert_equal "Hello", c2.read(5)
236
- end
237
-
238
- end