mega-max-tool 0.0.1

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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/mega-max-tool.gemspec +12 -0
  3. data/unicorn-6.1.0/Application_Timeouts +77 -0
  4. data/unicorn-6.1.0/CONTRIBUTORS +39 -0
  5. data/unicorn-6.1.0/COPYING +674 -0
  6. data/unicorn-6.1.0/DESIGN +95 -0
  7. data/unicorn-6.1.0/Documentation/unicorn.1 +222 -0
  8. data/unicorn-6.1.0/Documentation/unicorn_rails.1 +207 -0
  9. data/unicorn-6.1.0/FAQ +70 -0
  10. data/unicorn-6.1.0/GIT-VERSION-FILE +1 -0
  11. data/unicorn-6.1.0/GIT-VERSION-GEN +39 -0
  12. data/unicorn-6.1.0/GNUmakefile +313 -0
  13. data/unicorn-6.1.0/HACKING +112 -0
  14. data/unicorn-6.1.0/ISSUES +97 -0
  15. data/unicorn-6.1.0/KNOWN_ISSUES +79 -0
  16. data/unicorn-6.1.0/LATEST +24 -0
  17. data/unicorn-6.1.0/LICENSE +67 -0
  18. data/unicorn-6.1.0/Links +58 -0
  19. data/unicorn-6.1.0/NEWS +2890 -0
  20. data/unicorn-6.1.0/PHILOSOPHY +139 -0
  21. data/unicorn-6.1.0/README +151 -0
  22. data/unicorn-6.1.0/Rakefile +16 -0
  23. data/unicorn-6.1.0/SIGNALS +123 -0
  24. data/unicorn-6.1.0/Sandbox +104 -0
  25. data/unicorn-6.1.0/TODO +3 -0
  26. data/unicorn-6.1.0/TUNING +119 -0
  27. data/unicorn-6.1.0/archive/slrnpull.conf +4 -0
  28. data/unicorn-6.1.0/bin/unicorn +128 -0
  29. data/unicorn-6.1.0/bin/unicorn_rails +209 -0
  30. data/unicorn-6.1.0/examples/big_app_gc.rb +2 -0
  31. data/unicorn-6.1.0/examples/echo.ru +27 -0
  32. data/unicorn-6.1.0/examples/init.sh +102 -0
  33. data/unicorn-6.1.0/examples/logger_mp_safe.rb +25 -0
  34. data/unicorn-6.1.0/examples/logrotate.conf +44 -0
  35. data/unicorn-6.1.0/examples/nginx.conf +156 -0
  36. data/unicorn-6.1.0/examples/unicorn.conf.minimal.rb +13 -0
  37. data/unicorn-6.1.0/examples/unicorn.conf.rb +110 -0
  38. data/unicorn-6.1.0/examples/unicorn.socket +11 -0
  39. data/unicorn-6.1.0/examples/unicorn@.service +40 -0
  40. data/unicorn-6.1.0/ext/unicorn_http/CFLAGS +13 -0
  41. data/unicorn-6.1.0/ext/unicorn_http/c_util.h +116 -0
  42. data/unicorn-6.1.0/ext/unicorn_http/common_field_optimization.h +128 -0
  43. data/unicorn-6.1.0/ext/unicorn_http/epollexclusive.h +124 -0
  44. data/unicorn-6.1.0/ext/unicorn_http/ext_help.h +38 -0
  45. data/unicorn-6.1.0/ext/unicorn_http/extconf.rb +37 -0
  46. data/unicorn-6.1.0/ext/unicorn_http/global_variables.h +97 -0
  47. data/unicorn-6.1.0/ext/unicorn_http/httpdate.c +79 -0
  48. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http.c +4319 -0
  49. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http.rl +1025 -0
  50. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http_common.rl +76 -0
  51. data/unicorn-6.1.0/lib/unicorn/app/old_rails/static.rb +59 -0
  52. data/unicorn-6.1.0/lib/unicorn/app/old_rails.rb +35 -0
  53. data/unicorn-6.1.0/lib/unicorn/cgi_wrapper.rb +147 -0
  54. data/unicorn-6.1.0/lib/unicorn/configurator.rb +743 -0
  55. data/unicorn-6.1.0/lib/unicorn/const.rb +21 -0
  56. data/unicorn-6.1.0/lib/unicorn/http_request.rb +201 -0
  57. data/unicorn-6.1.0/lib/unicorn/http_response.rb +61 -0
  58. data/unicorn-6.1.0/lib/unicorn/http_server.rb +893 -0
  59. data/unicorn-6.1.0/lib/unicorn/launcher.rb +62 -0
  60. data/unicorn-6.1.0/lib/unicorn/oob_gc.rb +81 -0
  61. data/unicorn-6.1.0/lib/unicorn/preread_input.rb +33 -0
  62. data/unicorn-6.1.0/lib/unicorn/select_waiter.rb +6 -0
  63. data/unicorn-6.1.0/lib/unicorn/socket_helper.rb +211 -0
  64. data/unicorn-6.1.0/lib/unicorn/stream_input.rb +147 -0
  65. data/unicorn-6.1.0/lib/unicorn/tee_input.rb +131 -0
  66. data/unicorn-6.1.0/lib/unicorn/tmpio.rb +33 -0
  67. data/unicorn-6.1.0/lib/unicorn/util.rb +90 -0
  68. data/unicorn-6.1.0/lib/unicorn/version.rb +1 -0
  69. data/unicorn-6.1.0/lib/unicorn/worker.rb +165 -0
  70. data/unicorn-6.1.0/lib/unicorn.rb +138 -0
  71. data/unicorn-6.1.0/man/man1/unicorn.1 +222 -0
  72. data/unicorn-6.1.0/man/man1/unicorn_rails.1 +207 -0
  73. data/unicorn-6.1.0/setup.rb +1586 -0
  74. data/unicorn-6.1.0/t/GNUmakefile +5 -0
  75. data/unicorn-6.1.0/t/README +42 -0
  76. data/unicorn-6.1.0/t/bin/content-md5-put +36 -0
  77. data/unicorn-6.1.0/t/bin/sha1sum.rb +17 -0
  78. data/unicorn-6.1.0/t/bin/unused_listen +40 -0
  79. data/unicorn-6.1.0/t/broken-app.ru +12 -0
  80. data/unicorn-6.1.0/t/detach.ru +11 -0
  81. data/unicorn-6.1.0/t/env.ru +3 -0
  82. data/unicorn-6.1.0/t/fails-rack-lint.ru +5 -0
  83. data/unicorn-6.1.0/t/heartbeat-timeout.ru +12 -0
  84. data/unicorn-6.1.0/t/listener_names.ru +4 -0
  85. data/unicorn-6.1.0/t/my-tap-lib.sh +201 -0
  86. data/unicorn-6.1.0/t/oob_gc.ru +20 -0
  87. data/unicorn-6.1.0/t/oob_gc_path.ru +20 -0
  88. data/unicorn-6.1.0/t/pid.ru +3 -0
  89. data/unicorn-6.1.0/t/preread_input.ru +17 -0
  90. data/unicorn-6.1.0/t/rack-input-tests.ru +21 -0
  91. data/unicorn-6.1.0/t/t0000-http-basic.sh +50 -0
  92. data/unicorn-6.1.0/t/t0001-reload-bad-config.sh +53 -0
  93. data/unicorn-6.1.0/t/t0002-config-conflict.sh +49 -0
  94. data/unicorn-6.1.0/t/t0002-parser-error.sh +94 -0
  95. data/unicorn-6.1.0/t/t0003-working_directory.sh +51 -0
  96. data/unicorn-6.1.0/t/t0004-heartbeat-timeout.sh +69 -0
  97. data/unicorn-6.1.0/t/t0004-working_directory_broken.sh +24 -0
  98. data/unicorn-6.1.0/t/t0005-working_directory_app.rb.sh +40 -0
  99. data/unicorn-6.1.0/t/t0006-reopen-logs.sh +83 -0
  100. data/unicorn-6.1.0/t/t0006.ru +13 -0
  101. data/unicorn-6.1.0/t/t0007-working_directory_no_embed_cli.sh +44 -0
  102. data/unicorn-6.1.0/t/t0008-back_out_of_upgrade.sh +110 -0
  103. data/unicorn-6.1.0/t/t0009-broken-app.sh +56 -0
  104. data/unicorn-6.1.0/t/t0009-winch_ttin.sh +59 -0
  105. data/unicorn-6.1.0/t/t0010-reap-logging.sh +55 -0
  106. data/unicorn-6.1.0/t/t0011-active-unix-socket.sh +79 -0
  107. data/unicorn-6.1.0/t/t0012-reload-empty-config.sh +86 -0
  108. data/unicorn-6.1.0/t/t0013-rewindable-input-false.sh +24 -0
  109. data/unicorn-6.1.0/t/t0013.ru +12 -0
  110. data/unicorn-6.1.0/t/t0014-rewindable-input-true.sh +24 -0
  111. data/unicorn-6.1.0/t/t0014.ru +12 -0
  112. data/unicorn-6.1.0/t/t0015-configurator-internals.sh +25 -0
  113. data/unicorn-6.1.0/t/t0018-write-on-close.sh +23 -0
  114. data/unicorn-6.1.0/t/t0019-max_header_len.sh +49 -0
  115. data/unicorn-6.1.0/t/t0020-at_exit-handler.sh +49 -0
  116. data/unicorn-6.1.0/t/t0021-process_detach.sh +29 -0
  117. data/unicorn-6.1.0/t/t0022-listener_names-preload_app.sh +32 -0
  118. data/unicorn-6.1.0/t/t0100-rack-input-tests.sh +124 -0
  119. data/unicorn-6.1.0/t/t0116-client_body_buffer_size.sh +80 -0
  120. data/unicorn-6.1.0/t/t0116.ru +16 -0
  121. data/unicorn-6.1.0/t/t0300-no-default-middleware.sh +20 -0
  122. data/unicorn-6.1.0/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  123. data/unicorn-6.1.0/t/t0301.ru +13 -0
  124. data/unicorn-6.1.0/t/t9000-preread-input.sh +48 -0
  125. data/unicorn-6.1.0/t/t9001-oob_gc.sh +47 -0
  126. data/unicorn-6.1.0/t/t9002-oob_gc-path.sh +75 -0
  127. data/unicorn-6.1.0/t/test-lib.sh +129 -0
  128. data/unicorn-6.1.0/t/write-on-close.ru +11 -0
  129. data/unicorn-6.1.0/test/aggregate.rb +15 -0
  130. data/unicorn-6.1.0/test/benchmark/README +60 -0
  131. data/unicorn-6.1.0/test/benchmark/dd.ru +18 -0
  132. data/unicorn-6.1.0/test/benchmark/ddstream.ru +50 -0
  133. data/unicorn-6.1.0/test/benchmark/readinput.ru +40 -0
  134. data/unicorn-6.1.0/test/benchmark/stack.ru +8 -0
  135. data/unicorn-6.1.0/test/benchmark/uconnect.perl +66 -0
  136. data/unicorn-6.1.0/test/exec/README +5 -0
  137. data/unicorn-6.1.0/test/exec/test_exec.rb +1101 -0
  138. data/unicorn-6.1.0/test/test_helper.rb +306 -0
  139. data/unicorn-6.1.0/test/unit/test_ccc.rb +91 -0
  140. data/unicorn-6.1.0/test/unit/test_configurator.rb +175 -0
  141. data/unicorn-6.1.0/test/unit/test_droplet.rb +28 -0
  142. data/unicorn-6.1.0/test/unit/test_http_parser.rb +884 -0
  143. data/unicorn-6.1.0/test/unit/test_http_parser_ng.rb +714 -0
  144. data/unicorn-6.1.0/test/unit/test_request.rb +182 -0
  145. data/unicorn-6.1.0/test/unit/test_response.rb +111 -0
  146. data/unicorn-6.1.0/test/unit/test_server.rb +342 -0
  147. data/unicorn-6.1.0/test/unit/test_signals.rb +188 -0
  148. data/unicorn-6.1.0/test/unit/test_socket_helper.rb +201 -0
  149. data/unicorn-6.1.0/test/unit/test_stream_input.rb +203 -0
  150. data/unicorn-6.1.0/test/unit/test_tee_input.rb +304 -0
  151. data/unicorn-6.1.0/test/unit/test_upload.rb +301 -0
  152. data/unicorn-6.1.0/test/unit/test_util.rb +131 -0
  153. data/unicorn-6.1.0/test/unit/test_waiter.rb +34 -0
  154. data/unicorn-6.1.0/unicorn.gemspec +49 -0
  155. data/unicorn-6.1.0/unicorn_1 +1 -0
  156. data/unicorn-6.1.0/unicorn_rails_1 +1 -0
  157. metadata +196 -0
@@ -0,0 +1,182 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2009 Eric Wong
4
+ # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5
+ # the GPLv2+ (GPLv3+ preferred)
6
+
7
+ require './test/test_helper'
8
+
9
+ include Unicorn
10
+
11
+ class RequestTest < Test::Unit::TestCase
12
+
13
+ class MockRequest < StringIO
14
+ alias_method :readpartial, :sysread
15
+ alias_method :kgio_read!, :sysread
16
+ alias_method :read_nonblock, :sysread
17
+ def kgio_addr
18
+ '127.0.0.1'
19
+ end
20
+ end
21
+
22
+ def setup
23
+ @request = HttpRequest.new
24
+ @app = lambda do |env|
25
+ [ 200, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
26
+ end
27
+ @lint = Rack::Lint.new(@app)
28
+ end
29
+
30
+ def test_options
31
+ client = MockRequest.new("OPTIONS * HTTP/1.1\r\n" \
32
+ "Host: foo\r\n\r\n")
33
+ env = @request.read(client)
34
+ assert_equal '', env['REQUEST_PATH']
35
+ assert_equal '', env['PATH_INFO']
36
+ assert_equal '*', env['REQUEST_URI']
37
+ assert_kind_of Array, @lint.call(env)
38
+ end
39
+
40
+ def test_absolute_uri_with_query
41
+ client = MockRequest.new("GET http://e:3/x?y=z HTTP/1.1\r\n" \
42
+ "Host: foo\r\n\r\n")
43
+ env = @request.read(client)
44
+ assert_equal '/x', env['REQUEST_PATH']
45
+ assert_equal '/x', env['PATH_INFO']
46
+ assert_equal 'y=z', env['QUERY_STRING']
47
+ assert_kind_of Array, @lint.call(env)
48
+ end
49
+
50
+ def test_absolute_uri_with_fragment
51
+ client = MockRequest.new("GET http://e:3/x#frag HTTP/1.1\r\n" \
52
+ "Host: foo\r\n\r\n")
53
+ env = @request.read(client)
54
+ assert_equal '/x', env['REQUEST_PATH']
55
+ assert_equal '/x', env['PATH_INFO']
56
+ assert_equal '', env['QUERY_STRING']
57
+ assert_equal 'frag', env['FRAGMENT']
58
+ assert_kind_of Array, @lint.call(env)
59
+ end
60
+
61
+ def test_absolute_uri_with_query_and_fragment
62
+ client = MockRequest.new("GET http://e:3/x?a=b#frag HTTP/1.1\r\n" \
63
+ "Host: foo\r\n\r\n")
64
+ env = @request.read(client)
65
+ assert_equal '/x', env['REQUEST_PATH']
66
+ assert_equal '/x', env['PATH_INFO']
67
+ assert_equal 'a=b', env['QUERY_STRING']
68
+ assert_equal 'frag', env['FRAGMENT']
69
+ assert_kind_of Array, @lint.call(env)
70
+ end
71
+
72
+ def test_absolute_uri_unsupported_schemes
73
+ %w(ssh+http://e/ ftp://e/x http+ssh://e/x).each do |abs_uri|
74
+ client = MockRequest.new("GET #{abs_uri} HTTP/1.1\r\n" \
75
+ "Host: foo\r\n\r\n")
76
+ assert_raises(HttpParserError) { @request.read(client) }
77
+ end
78
+ end
79
+
80
+ def test_x_forwarded_proto_https
81
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
82
+ "X-Forwarded-Proto: https\r\n" \
83
+ "Host: foo\r\n\r\n")
84
+ env = @request.read(client)
85
+ assert_equal "https", env['rack.url_scheme']
86
+ assert_kind_of Array, @lint.call(env)
87
+ end
88
+
89
+ def test_x_forwarded_proto_http
90
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
91
+ "X-Forwarded-Proto: http\r\n" \
92
+ "Host: foo\r\n\r\n")
93
+ env = @request.read(client)
94
+ assert_equal "http", env['rack.url_scheme']
95
+ assert_kind_of Array, @lint.call(env)
96
+ end
97
+
98
+ def test_x_forwarded_proto_invalid
99
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
100
+ "X-Forwarded-Proto: ftp\r\n" \
101
+ "Host: foo\r\n\r\n")
102
+ env = @request.read(client)
103
+ assert_equal "http", env['rack.url_scheme']
104
+ assert_kind_of Array, @lint.call(env)
105
+ end
106
+
107
+ def test_rack_lint_get
108
+ client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
109
+ env = @request.read(client)
110
+ assert_equal "http", env['rack.url_scheme']
111
+ assert_equal '127.0.0.1', env['REMOTE_ADDR']
112
+ assert_kind_of Array, @lint.call(env)
113
+ end
114
+
115
+ def test_no_content_stringio
116
+ client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
117
+ env = @request.read(client)
118
+ assert_equal StringIO, env['rack.input'].class
119
+ end
120
+
121
+ def test_zero_content_stringio
122
+ client = MockRequest.new("PUT / HTTP/1.1\r\n" \
123
+ "Content-Length: 0\r\n" \
124
+ "Host: foo\r\n\r\n")
125
+ env = @request.read(client)
126
+ assert_equal StringIO, env['rack.input'].class
127
+ end
128
+
129
+ def test_real_content_not_stringio
130
+ client = MockRequest.new("PUT / HTTP/1.1\r\n" \
131
+ "Content-Length: 1\r\n" \
132
+ "Host: foo\r\n\r\n")
133
+ env = @request.read(client)
134
+ assert_equal Unicorn::TeeInput, env['rack.input'].class
135
+ end
136
+
137
+ def test_rack_lint_put
138
+ client = MockRequest.new(
139
+ "PUT / HTTP/1.1\r\n" \
140
+ "Host: foo\r\n" \
141
+ "Content-Length: 5\r\n" \
142
+ "\r\n" \
143
+ "abcde")
144
+ env = @request.read(client)
145
+ assert ! env.include?(:http_body)
146
+ assert_kind_of Array, @lint.call(env)
147
+ end
148
+
149
+ def test_rack_lint_big_put
150
+ count = 100
151
+ bs = 0x10000
152
+ buf = (' ' * bs).freeze
153
+ length = bs * count
154
+ client = Tempfile.new('big_put')
155
+ def client.kgio_addr; '127.0.0.1'; end
156
+ def client.kgio_read(*args)
157
+ readpartial(*args)
158
+ rescue EOFError
159
+ end
160
+ def client.kgio_read!(*args)
161
+ readpartial(*args)
162
+ end
163
+ client.syswrite(
164
+ "PUT / HTTP/1.1\r\n" \
165
+ "Host: foo\r\n" \
166
+ "Content-Length: #{length}\r\n" \
167
+ "\r\n")
168
+ count.times { assert_equal bs, client.syswrite(buf) }
169
+ assert_equal 0, client.sysseek(0)
170
+ env = @request.read(client)
171
+ assert ! env.include?(:http_body)
172
+ assert_equal length, env['rack.input'].size
173
+ count.times {
174
+ tmp = env['rack.input'].read(bs)
175
+ tmp << env['rack.input'].read(bs - tmp.size) if tmp.size != bs
176
+ assert_equal buf, tmp
177
+ }
178
+ assert_nil env['rack.input'].read(bs)
179
+ env['rack.input'].rewind
180
+ assert_kind_of Array, @lint.call(env)
181
+ end
182
+ end
@@ -0,0 +1,111 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2005 Zed A. Shaw
4
+ # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5
+ # the GPLv2+ (GPLv3+ preferred)
6
+ #
7
+ # Additional work donated by contributors. See git history
8
+ # for more information.
9
+
10
+ require './test/test_helper'
11
+ require 'time'
12
+
13
+ include Unicorn
14
+
15
+ class ResponseTest < Test::Unit::TestCase
16
+ include Unicorn::HttpResponse
17
+
18
+ def test_httpdate
19
+ before = Time.now.to_i - 1
20
+ str = httpdate
21
+ assert_kind_of(String, str)
22
+ middle = Time.parse(str).to_i
23
+ after = Time.now.to_i
24
+ assert before <= middle
25
+ assert middle <= after
26
+ end
27
+
28
+ def test_response_headers
29
+ out = StringIO.new
30
+ http_response_write(out, 200, {"X-Whatever" => "stuff"}, ["cool"])
31
+ assert ! out.closed?
32
+
33
+ assert out.length > 0, "output didn't have data"
34
+ end
35
+
36
+ # ref: <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
37
+ def test_response_header_broken_nil
38
+ out = StringIO.new
39
+ http_response_write(out, 200, {"Nil" => nil}, %w(hysterical raisin))
40
+ assert ! out.closed?
41
+
42
+ assert_match %r{^Nil: \r\n}sm, out.string, 'nil accepted'
43
+ end
44
+
45
+ def test_response_string_status
46
+ out = StringIO.new
47
+ http_response_write(out,'200', {}, [])
48
+ assert ! out.closed?
49
+ assert out.length > 0, "output didn't have data"
50
+ end
51
+
52
+ def test_response_200
53
+ io = StringIO.new
54
+ http_response_write(io, 200, {}, [])
55
+ assert ! io.closed?
56
+ assert io.length > 0, "output didn't have data"
57
+ end
58
+
59
+ def test_response_with_default_reason
60
+ code = 400
61
+ io = StringIO.new
62
+ http_response_write(io, code, {}, [])
63
+ assert ! io.closed?
64
+ lines = io.string.split(/\r\n/)
65
+ assert_match(/.* Bad Request$/, lines.first,
66
+ "wrong default reason phrase")
67
+ end
68
+
69
+ def test_rack_multivalue_headers
70
+ out = StringIO.new
71
+ http_response_write(out,200, {"X-Whatever" => "stuff\nbleh"}, [])
72
+ assert ! out.closed?
73
+ assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
74
+ end
75
+
76
+ # Even though Rack explicitly forbids "Status" in the header hash,
77
+ # some broken clients still rely on it
78
+ def test_status_header_added
79
+ out = StringIO.new
80
+ http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
81
+ assert ! out.closed?
82
+ end
83
+
84
+ def test_unknown_status_pass_through
85
+ out = StringIO.new
86
+ http_response_write(out,"666 I AM THE BEAST", {}, [] )
87
+ assert ! out.closed?
88
+ headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
89
+ assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
90
+ end
91
+
92
+ def test_modified_rack_http_status_codes_late
93
+ r, w = IO.pipe
94
+ pid = fork do
95
+ r.close
96
+ # Users may want to globally override the status text associated
97
+ # with an HTTP status code in their app.
98
+ Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
99
+ http_response_write(w, 200, {}, [])
100
+ w.close
101
+ end
102
+ w.close
103
+ assert_equal "HTTP/1.1 200 HI\r\n", r.gets
104
+ r.read # just drain the pipe
105
+ pid, status = Process.waitpid2(pid)
106
+ assert status.success?, status.inspect
107
+ ensure
108
+ r.close
109
+ w.close unless w.closed?
110
+ end
111
+ end
@@ -0,0 +1,342 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2005 Zed A. Shaw
4
+ # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5
+ # the GPLv2+ (GPLv3+ preferred)
6
+ #
7
+ # Additional work donated by contributors. See git history
8
+ # for more information.
9
+
10
+ require './test/test_helper'
11
+
12
+ include Unicorn
13
+
14
+ class TestHandler
15
+
16
+ def call(env)
17
+ while env['rack.input'].read(4096)
18
+ end
19
+ [200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
20
+ rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
21
+ $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
22
+ raise e
23
+ end
24
+ end
25
+
26
+ class TestEarlyHintsHandler
27
+ def call(env)
28
+ while env['rack.input'].read(4096)
29
+ end
30
+ env['rack.early_hints'].call(
31
+ "Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload"
32
+ )
33
+ [200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
34
+ end
35
+ end
36
+
37
+ class TestRackAfterReply
38
+ def initialize
39
+ @called = false
40
+ end
41
+
42
+ def call(env)
43
+ while env['rack.input'].read(4096)
44
+ end
45
+
46
+ env["rack.after_reply"] << -> { @called = true }
47
+
48
+ [200, { 'Content-Type' => 'text/plain' }, ["after_reply_called: #{@called}"]]
49
+ rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
50
+ $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
51
+ raise e
52
+ end
53
+ end
54
+
55
+ class WebServerTest < Test::Unit::TestCase
56
+
57
+ def setup
58
+ @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n"
59
+ @port = unused_port
60
+ @tester = TestHandler.new
61
+ redirect_test_io do
62
+ @server = HttpServer.new(@tester, :listeners => [ "127.0.0.1:#{@port}" ] )
63
+ @server.start
64
+ end
65
+ end
66
+
67
+ def teardown
68
+ redirect_test_io do
69
+ wait_workers_ready("test_stderr.#$$.log", 1)
70
+ File.truncate("test_stderr.#$$.log", 0)
71
+ @server.stop(false)
72
+ end
73
+ reset_sig_handlers
74
+ end
75
+
76
+ def test_preload_app_config
77
+ teardown
78
+ tmp = Tempfile.new('test_preload_app_config')
79
+ ObjectSpace.undefine_finalizer(tmp)
80
+ app = lambda { ||
81
+ tmp.sysseek(0)
82
+ tmp.truncate(0)
83
+ tmp.syswrite($$)
84
+ lambda { |env| [ 200, { 'Content-Type' => 'text/plain' }, [ "#$$\n" ] ] }
85
+ }
86
+ redirect_test_io do
87
+ @server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
88
+ @server.start
89
+ end
90
+ results = hit(["http://localhost:#@port/"])
91
+ worker_pid = results[0].to_i
92
+ assert worker_pid != 0
93
+ tmp.sysseek(0)
94
+ loader_pid = tmp.sysread(4096).to_i
95
+ assert loader_pid != 0
96
+ assert_equal worker_pid, loader_pid
97
+ teardown
98
+
99
+ redirect_test_io do
100
+ @server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"],
101
+ :preload_app => true)
102
+ @server.start
103
+ end
104
+ results = hit(["http://localhost:#@port/"])
105
+ worker_pid = results[0].to_i
106
+ assert worker_pid != 0
107
+ tmp.sysseek(0)
108
+ loader_pid = tmp.sysread(4096).to_i
109
+ assert_equal $$, loader_pid
110
+ assert worker_pid != loader_pid
111
+ ensure
112
+ tmp.close!
113
+ end
114
+
115
+ def test_early_hints
116
+ teardown
117
+ redirect_test_io do
118
+ @server = HttpServer.new(TestEarlyHintsHandler.new,
119
+ :listeners => [ "127.0.0.1:#@port"],
120
+ :early_hints => true)
121
+ @server.start
122
+ end
123
+
124
+ sock = tcp_socket('127.0.0.1', @port)
125
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
126
+
127
+ responses = sock.read(4096)
128
+ assert_match %r{\AHTTP/1.[01] 103\b}, responses
129
+ assert_match %r{^Link: </style\.css>}, responses
130
+ assert_match %r{^Link: </script\.js>}, responses
131
+
132
+ assert_match %r{^HTTP/1.[01] 200\b}, responses
133
+ end
134
+
135
+ def test_after_reply
136
+ teardown
137
+
138
+ redirect_test_io do
139
+ @server = HttpServer.new(TestRackAfterReply.new,
140
+ :listeners => [ "127.0.0.1:#@port"])
141
+ @server.start
142
+ end
143
+
144
+ sock = tcp_socket('127.0.0.1', @port)
145
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
146
+
147
+ responses = sock.read(4096)
148
+ assert_match %r{\AHTTP/1.[01] 200\b}, responses
149
+ assert_match %r{^after_reply_called: false}, responses
150
+
151
+ sock = tcp_socket('127.0.0.1', @port)
152
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
153
+
154
+ responses = sock.read(4096)
155
+ assert_match %r{\AHTTP/1.[01] 200\b}, responses
156
+ assert_match %r{^after_reply_called: true}, responses
157
+
158
+ sock.close
159
+ end
160
+
161
+ def test_broken_app
162
+ teardown
163
+ app = lambda { |env| raise RuntimeError, "hello" }
164
+ # [200, {}, []] }
165
+ redirect_test_io do
166
+ @server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
167
+ @server.start
168
+ end
169
+ sock = tcp_socket('127.0.0.1', @port)
170
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
171
+ assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
172
+ assert_nil sock.close
173
+ end
174
+
175
+ def test_simple_server
176
+ results = hit(["http://localhost:#{@port}/test"])
177
+ assert_equal 'hello!\n', results[0], "Handler didn't really run"
178
+ end
179
+
180
+ def test_client_shutdown_writes
181
+ bs = 15609315 * rand
182
+ sock = tcp_socket('127.0.0.1', @port)
183
+ sock.syswrite("PUT /hello HTTP/1.1\r\n")
184
+ sock.syswrite("Host: example.com\r\n")
185
+ sock.syswrite("Transfer-Encoding: chunked\r\n")
186
+ sock.syswrite("Trailer: X-Foo\r\n")
187
+ sock.syswrite("\r\n")
188
+ sock.syswrite("%x\r\n" % [ bs ])
189
+ sock.syswrite("F" * bs)
190
+ sock.syswrite("\r\n0\r\nX-")
191
+ "Foo: bar\r\n\r\n".each_byte do |x|
192
+ sock.syswrite x.chr
193
+ sleep 0.05
194
+ end
195
+ # we wrote the entire request before shutting down, server should
196
+ # continue to process our request and never hit EOFError on our sock
197
+ sock.shutdown(Socket::SHUT_WR)
198
+ buf = sock.read
199
+ assert_equal 'hello!\n', buf.split(/\r\n\r\n/).last
200
+ next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
201
+ assert_equal 'hello!\n', next_client
202
+ lines = File.readlines("test_stderr.#$$.log")
203
+ assert lines.grep(/^Unicorn::ClientShutdown: /).empty?
204
+ assert_nil sock.close
205
+ end
206
+
207
+ def test_client_shutdown_write_truncates
208
+ bs = 15609315 * rand
209
+ sock = tcp_socket('127.0.0.1', @port)
210
+ sock.syswrite("PUT /hello HTTP/1.1\r\n")
211
+ sock.syswrite("Host: example.com\r\n")
212
+ sock.syswrite("Transfer-Encoding: chunked\r\n")
213
+ sock.syswrite("Trailer: X-Foo\r\n")
214
+ sock.syswrite("\r\n")
215
+ sock.syswrite("%x\r\n" % [ bs ])
216
+ sock.syswrite("F" * (bs / 2.0))
217
+
218
+ # shutdown prematurely, this will force the server to abort
219
+ # processing on us even during app dispatch
220
+ sock.shutdown(Socket::SHUT_WR)
221
+ IO.select([sock], nil, nil, 60) or raise "Timed out"
222
+ buf = sock.read
223
+ assert_equal "", buf
224
+ next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
225
+ assert_equal 'hello!\n', next_client
226
+ lines = File.readlines("test_stderr.#$$.log")
227
+ lines = lines.grep(/^Unicorn::ClientShutdown: bytes_read=\d+/)
228
+ assert_equal 1, lines.size
229
+ assert_match %r{\AUnicorn::ClientShutdown: bytes_read=\d+ true$}, lines[0]
230
+ assert_nil sock.close
231
+ end
232
+
233
+ def test_client_malformed_body
234
+ bs = 15653984
235
+ sock = tcp_socket('127.0.0.1', @port)
236
+ sock.syswrite("PUT /hello HTTP/1.1\r\n")
237
+ sock.syswrite("Host: example.com\r\n")
238
+ sock.syswrite("Transfer-Encoding: chunked\r\n")
239
+ sock.syswrite("Trailer: X-Foo\r\n")
240
+ sock.syswrite("\r\n")
241
+ sock.syswrite("%x\r\n" % [ bs ])
242
+ sock.syswrite("F" * bs)
243
+ begin
244
+ File.open("/dev/urandom", "rb") { |fp| sock.syswrite(fp.sysread(16384)) }
245
+ rescue
246
+ end
247
+ assert_nil sock.close
248
+ next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
249
+ assert_equal 'hello!\n', next_client
250
+ lines = File.readlines("test_stderr.#$$.log")
251
+ lines = lines.grep(/^Unicorn::HttpParserError: .* true$/)
252
+ assert_equal 1, lines.size
253
+ end
254
+
255
+ def do_test(string, chunk, close_after=nil, shutdown_delay=0)
256
+ # Do not use instance variables here, because it needs to be thread safe
257
+ socket = tcp_socket("127.0.0.1", @port);
258
+ request = StringIO.new(string)
259
+ chunks_out = 0
260
+
261
+ while data = request.read(chunk)
262
+ chunks_out += socket.write(data)
263
+ socket.flush
264
+ sleep 0.2
265
+ if close_after and chunks_out > close_after
266
+ socket.close
267
+ sleep 1
268
+ end
269
+ end
270
+ sleep(shutdown_delay)
271
+ socket.write(" ") # Some platforms only raise the exception on attempted write
272
+ socket.flush
273
+ end
274
+
275
+ def test_trickle_attack
276
+ do_test(@valid_request, 3)
277
+ end
278
+
279
+ def test_close_client
280
+ assert_raises IOError do
281
+ do_test(@valid_request, 10, 20)
282
+ end
283
+ end
284
+
285
+ def test_bad_client
286
+ redirect_test_io do
287
+ do_test("GET /test HTTP/BAD", 3)
288
+ end
289
+ end
290
+
291
+ def test_logger_set
292
+ assert_equal @server.logger, Unicorn::HttpRequest::DEFAULTS["rack.logger"]
293
+ end
294
+
295
+ def test_logger_changed
296
+ tmp = Logger.new($stdout)
297
+ @server.logger = tmp
298
+ assert_equal tmp, Unicorn::HttpRequest::DEFAULTS["rack.logger"]
299
+ end
300
+
301
+ def test_bad_client_400
302
+ sock = tcp_socket('127.0.0.1', @port)
303
+ sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
304
+ assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
305
+ assert_nil sock.close
306
+ end
307
+
308
+ def test_http_0_9
309
+ sock = tcp_socket('127.0.0.1', @port)
310
+ sock.syswrite("GET /hello\r\n")
311
+ assert_match 'hello!\n', sock.sysread(4096)
312
+ assert_nil sock.close
313
+ end
314
+
315
+ def test_header_is_too_long
316
+ redirect_test_io do
317
+ long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n"
318
+ assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do
319
+ do_test(long, long.length/2, 10)
320
+ end
321
+ end
322
+ end
323
+
324
+ def test_file_streamed_request
325
+ body = "a" * (Unicorn::Const::MAX_BODY * 2)
326
+ long = "PUT /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body
327
+ do_test(long, Unicorn::Const::CHUNK_SIZE * 2 - 400)
328
+ end
329
+
330
+ def test_file_streamed_request_bad_body
331
+ body = "a" * (Unicorn::Const::MAX_BODY * 2)
332
+ long = "GET /test HTTP/1.1\r\nContent-ength: #{body.length}\r\n\r\n" + body
333
+ assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
334
+ Errno::EBADF) {
335
+ do_test(long, Unicorn::Const::CHUNK_SIZE * 2 - 400)
336
+ }
337
+ end
338
+
339
+ def test_listener_names
340
+ assert_equal [ "127.0.0.1:#@port" ], Unicorn.listener_names
341
+ end
342
+ end