puma 2.2.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3334 -0
  3. data/LICENSE +23 -20
  4. data/README.md +387 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/5.0-Upgrade.md +98 -0
  8. data/docs/6.0-Upgrade.md +56 -0
  9. data/docs/7.0-Upgrade.md +52 -0
  10. data/docs/8.0-Upgrade.md +100 -0
  11. data/docs/architecture.md +74 -0
  12. data/docs/compile_options.md +55 -0
  13. data/docs/deployment.md +137 -0
  14. data/docs/fork_worker.md +41 -0
  15. data/docs/grpc.md +62 -0
  16. data/docs/images/favicon.svg +1 -0
  17. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  18. data/docs/images/puma-connection-flow.png +0 -0
  19. data/docs/images/puma-general-arch.png +0 -0
  20. data/docs/images/running-puma.svg +1 -0
  21. data/docs/images/standard-logo.svg +1 -0
  22. data/docs/java_options.md +54 -0
  23. data/docs/jungle/README.md +9 -0
  24. data/docs/jungle/rc.d/README.md +74 -0
  25. data/docs/jungle/rc.d/puma +61 -0
  26. data/docs/jungle/rc.d/puma.conf +10 -0
  27. data/docs/kubernetes.md +73 -0
  28. data/docs/nginx.md +5 -5
  29. data/docs/plugins.md +42 -0
  30. data/docs/rails_dev_mode.md +28 -0
  31. data/docs/restart.md +65 -0
  32. data/docs/signals.md +98 -0
  33. data/docs/stats.md +148 -0
  34. data/docs/systemd.md +253 -0
  35. data/docs/testing_benchmarks_local_files.md +150 -0
  36. data/docs/testing_test_rackup_ci_files.md +36 -0
  37. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  38. data/ext/puma_http11/extconf.rb +59 -2
  39. data/ext/puma_http11/http11_parser.c +319 -487
  40. data/ext/puma_http11/http11_parser.h +15 -13
  41. data/ext/puma_http11/http11_parser.java.rl +64 -94
  42. data/ext/puma_http11/http11_parser.rl +27 -24
  43. data/ext/puma_http11/http11_parser_common.rl +8 -8
  44. data/ext/puma_http11/mini_ssl.c +696 -38
  45. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  46. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  47. data/ext/puma_http11/org/jruby/puma/Http11.java +241 -145
  48. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +174 -221
  49. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  50. data/ext/puma_http11/puma_http11.c +183 -175
  51. data/lib/puma/app/status.rb +77 -29
  52. data/lib/puma/binder.rb +349 -119
  53. data/lib/puma/cli.rb +163 -801
  54. data/lib/puma/client.rb +627 -144
  55. data/lib/puma/client_env.rb +171 -0
  56. data/lib/puma/cluster/worker.rb +183 -0
  57. data/lib/puma/cluster/worker_handle.rb +127 -0
  58. data/lib/puma/cluster.rb +634 -0
  59. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  60. data/lib/puma/commonlogger.rb +115 -0
  61. data/lib/puma/configuration.rb +420 -198
  62. data/lib/puma/const.rb +266 -115
  63. data/lib/puma/control_cli.rb +219 -120
  64. data/lib/puma/detect.rb +56 -2
  65. data/lib/puma/dsl.rb +1562 -0
  66. data/lib/puma/error_logger.rb +115 -0
  67. data/lib/puma/events.rb +44 -60
  68. data/lib/puma/io_buffer.rb +48 -5
  69. data/lib/puma/jruby_restart.rb +2 -51
  70. data/lib/puma/json_serialization.rb +96 -0
  71. data/lib/puma/launcher/bundle_pruner.rb +102 -0
  72. data/lib/puma/launcher.rb +501 -0
  73. data/lib/puma/log_writer.rb +153 -0
  74. data/lib/puma/minissl/context_builder.rb +96 -0
  75. data/lib/puma/minissl.rb +355 -37
  76. data/lib/puma/null_io.rb +79 -12
  77. data/lib/puma/plugin/systemd.rb +90 -0
  78. data/lib/puma/plugin/tmp_restart.rb +36 -0
  79. data/lib/puma/plugin.rb +111 -0
  80. data/lib/puma/rack/builder.rb +297 -0
  81. data/lib/puma/rack/urlmap.rb +93 -0
  82. data/lib/puma/rack_default.rb +21 -4
  83. data/lib/puma/reactor.rb +102 -133
  84. data/lib/puma/response.rb +532 -0
  85. data/lib/puma/runner.rb +211 -0
  86. data/lib/puma/sd_notify.rb +146 -0
  87. data/lib/puma/server.rb +582 -445
  88. data/lib/puma/server_plugin_control.rb +32 -0
  89. data/lib/puma/single.rb +72 -0
  90. data/lib/puma/state_file.rb +69 -0
  91. data/lib/puma/thread_pool.rb +389 -57
  92. data/lib/puma/util.rb +125 -0
  93. data/lib/puma.rb +80 -6
  94. data/lib/rack/handler/puma.rb +125 -47
  95. data/tools/Dockerfile +26 -0
  96. data/tools/trickletest.rb +44 -0
  97. metadata +88 -148
  98. data/COPYING +0 -55
  99. data/Gemfile +0 -10
  100. data/History.txt +0 -302
  101. data/Manifest.txt +0 -60
  102. data/Rakefile +0 -165
  103. data/TODO +0 -5
  104. data/docs/config.md +0 -0
  105. data/ext/puma_http11/ext_help.h +0 -15
  106. data/ext/puma_http11/io_buffer.c +0 -154
  107. data/lib/puma/accept_nonblock.rb +0 -23
  108. data/lib/puma/capistrano.rb +0 -34
  109. data/lib/puma/compat.rb +0 -11
  110. data/lib/puma/daemon_ext.rb +0 -20
  111. data/lib/puma/delegation.rb +0 -11
  112. data/lib/puma/java_io_buffer.rb +0 -45
  113. data/lib/puma/rack_patch.rb +0 -25
  114. data/puma.gemspec +0 -45
  115. data/test/test_app_status.rb +0 -88
  116. data/test/test_cli.rb +0 -171
  117. data/test/test_config.rb +0 -16
  118. data/test/test_http10.rb +0 -27
  119. data/test/test_http11.rb +0 -126
  120. data/test/test_integration.rb +0 -154
  121. data/test/test_iobuffer.rb +0 -38
  122. data/test/test_minissl.rb +0 -25
  123. data/test/test_null_io.rb +0 -31
  124. data/test/test_persistent.rb +0 -238
  125. data/test/test_puma_server.rb +0 -224
  126. data/test/test_rack_handler.rb +0 -10
  127. data/test/test_rack_server.rb +0 -141
  128. data/test/test_thread_pool.rb +0 -146
  129. data/test/test_unix_socket.rb +0 -39
  130. data/test/test_ws.rb +0 -89
  131. data/tools/jungle/init.d/README.md +0 -54
  132. data/tools/jungle/init.d/puma +0 -332
  133. data/tools/jungle/init.d/run-puma +0 -3
  134. data/tools/jungle/upstart/README.md +0 -61
  135. data/tools/jungle/upstart/puma-manager.conf +0 -31
  136. data/tools/jungle/upstart/puma.conf +0 -52
data/test/test_http11.rb DELETED
@@ -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,154 +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
- begin
32
- Process.wait @server.pid
33
- rescue Errno::ECHILD
34
- end
35
-
36
- @server.close
37
- end
38
-
39
- if @script
40
- @script.close!
41
- end
42
- end
43
-
44
- def server(opts)
45
- core = "#{Gem.ruby} -rubygems -Ilib bin/puma"
46
- cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}"
47
- tf = Tempfile.new "puma-test"
48
- tf.puts "exec #{cmd}"
49
- tf.close
50
-
51
- @script = tf
52
-
53
- @server = IO.popen("sh #{tf.path}", "r")
54
-
55
- true while @server.gets =~ /Ctrl-C/
56
-
57
- sleep 1
58
-
59
- @server
60
- end
61
-
62
- def signal(which)
63
- Process.kill which, @server.pid
64
- end
65
-
66
- def test_stop_via_pumactl
67
- if defined?(JRUBY_VERSION) || RbConfig::CONFIG["host_os"] =~ /mingw|mswin/
68
- assert true
69
- return
70
- end
71
-
72
- sin = StringIO.new
73
- sout = StringIO.new
74
-
75
- cli = Puma::CLI.new %W!-q -S #{@state_path} -b unix://#{@bind_path} --control unix://#{@control_path} test/hello.ru!, sin, sout
76
-
77
- t = Thread.new do
78
- cli.run
79
- end
80
-
81
- sleep 1
82
-
83
- s = UNIXSocket.new @bind_path
84
- s << "GET / HTTP/1.0\r\n\r\n"
85
- assert_equal "Hello World", s.read.split("\r\n").last
86
-
87
- ccli = Puma::ControlCLI.new %W!-S #{@state_path} stop!, sout
88
-
89
- ccli.run
90
-
91
- assert_kind_of Thread, t.join(1), "server didn't stop"
92
- end
93
-
94
- def notest_restart_closes_keepalive_sockets
95
- server("-q test/hello.ru")
96
-
97
- s = TCPSocket.new "localhost", @tcp_port
98
- s << "GET / HTTP/1.1\r\n\r\n"
99
- true until s.gets == "\r\n"
100
-
101
- s.readpartial(20)
102
- signal :USR2
103
-
104
- true while @server.gets =~ /Ctrl-C/
105
- sleep 1
106
-
107
- s.write "GET / HTTP/1.1\r\n\r\n"
108
-
109
- assert_raises Errno::ECONNRESET do
110
- Timeout.timeout(2) do
111
- raise Errno::ECONNRESET unless s.read(2)
112
- end
113
- end
114
-
115
- s = TCPSocket.new "localhost", @tcp_port
116
- s << "GET / HTTP/1.0\r\n\r\n"
117
- assert_equal "Hello World", s.read.split("\r\n").last
118
- end
119
-
120
- def notest_restart_closes_keepalive_sockets_workers
121
- server("-q -w 2 test/hello.ru")
122
-
123
- s = TCPSocket.new "localhost", @tcp_port
124
- s << "GET / HTTP/1.1\r\n\r\n"
125
- true until s.gets == "\r\n"
126
-
127
- s.readpartial(20)
128
- signal :USR2
129
-
130
- true while @server.gets =~ /Ctrl-C/
131
- sleep 1
132
-
133
- s.write "GET / HTTP/1.1\r\n\r\n"
134
-
135
- assert_raises Errno::ECONNRESET do
136
- Timeout.timeout(2) do
137
- raise Errno::ECONNRESET unless s.read(2)
138
- end
139
- end
140
-
141
- s = TCPSocket.new "localhost", @tcp_port
142
- s << "GET / HTTP/1.0\r\n\r\n"
143
- assert_equal "Hello World", s.read.split("\r\n").last
144
- end
145
-
146
- def test_bad_query_string_outputs_400
147
- server "-q test/hello.ru 2>&1"
148
-
149
- s = TCPSocket.new "localhost", @tcp_port
150
- s << "GET /?h=% HTTP/1.0\r\n\r\n"
151
- data = s.read
152
- assert_equal "HTTP/1.1 400 Bad Request\r\n\r\n", data
153
- end
154
- 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
data/test/test_minissl.rb DELETED
@@ -1,25 +0,0 @@
1
- require 'test/unit'
2
-
3
- unless defined? JRUBY_VERSION
4
-
5
- require 'puma'
6
- require 'puma/minissl'
7
-
8
- class TestMiniSSL < Test::Unit::TestCase
9
-
10
- def test_raises_with_invalid_key_file
11
- ctx = Puma::MiniSSL::Context.new
12
-
13
- exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
14
- assert_equal("No such key file '/no/such/key'", exception.message)
15
- end
16
-
17
- def test_raises_with_invalid_cert_file
18
- ctx = Puma::MiniSSL::Context.new
19
-
20
- exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
21
- assert_equal("No such cert file '/no/such/cert'", exception.message)
22
- end
23
- end
24
-
25
- end
data/test/test_null_io.rb DELETED
@@ -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