unicorn-shopify 4.8.2.5.23

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 (150) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitignore +25 -0
  5. data/.mailmap +26 -0
  6. data/.olddoc.yml +15 -0
  7. data/Application_Timeouts +77 -0
  8. data/CONTRIBUTORS +35 -0
  9. data/COPYING +674 -0
  10. data/DESIGN +97 -0
  11. data/Documentation/.gitignore +5 -0
  12. data/Documentation/GNUmakefile +30 -0
  13. data/Documentation/unicorn.1.txt +185 -0
  14. data/Documentation/unicorn_rails.1.txt +175 -0
  15. data/FAQ +61 -0
  16. data/GIT-VERSION-GEN +39 -0
  17. data/GNUmakefile +252 -0
  18. data/HACKING +120 -0
  19. data/ISSUES +100 -0
  20. data/KNOWN_ISSUES +79 -0
  21. data/LICENSE +67 -0
  22. data/Links +59 -0
  23. data/PHILOSOPHY +145 -0
  24. data/README +145 -0
  25. data/Rakefile +16 -0
  26. data/SIGNALS +123 -0
  27. data/Sandbox +103 -0
  28. data/TODO +5 -0
  29. data/TUNING +101 -0
  30. data/archive/.gitignore +3 -0
  31. data/archive/slrnpull.conf +4 -0
  32. data/bin/unicorn +126 -0
  33. data/bin/unicorn_rails +209 -0
  34. data/examples/big_app_gc.rb +2 -0
  35. data/examples/echo.ru +27 -0
  36. data/examples/init.sh +74 -0
  37. data/examples/logger_mp_safe.rb +25 -0
  38. data/examples/logrotate.conf +29 -0
  39. data/examples/nginx.conf +156 -0
  40. data/examples/unicorn.conf.minimal.rb +13 -0
  41. data/examples/unicorn.conf.rb +113 -0
  42. data/ext/unicorn_http/CFLAGS +13 -0
  43. data/ext/unicorn_http/c_util.h +124 -0
  44. data/ext/unicorn_http/common_field_optimization.h +111 -0
  45. data/ext/unicorn_http/ext_help.h +82 -0
  46. data/ext/unicorn_http/extconf.rb +10 -0
  47. data/ext/unicorn_http/global_variables.h +97 -0
  48. data/ext/unicorn_http/httpdate.c +78 -0
  49. data/ext/unicorn_http/unicorn_http.rl +934 -0
  50. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  51. data/lib/unicorn.rb +112 -0
  52. data/lib/unicorn/app/old_rails.rb +35 -0
  53. data/lib/unicorn/app/old_rails/static.rb +59 -0
  54. data/lib/unicorn/cgi_wrapper.rb +147 -0
  55. data/lib/unicorn/configurator.rb +686 -0
  56. data/lib/unicorn/const.rb +21 -0
  57. data/lib/unicorn/http_request.rb +125 -0
  58. data/lib/unicorn/http_response.rb +73 -0
  59. data/lib/unicorn/http_server.rb +816 -0
  60. data/lib/unicorn/launcher.rb +62 -0
  61. data/lib/unicorn/oob_gc.rb +81 -0
  62. data/lib/unicorn/preread_input.rb +33 -0
  63. data/lib/unicorn/socket_helper.rb +197 -0
  64. data/lib/unicorn/stream_input.rb +146 -0
  65. data/lib/unicorn/tee_input.rb +133 -0
  66. data/lib/unicorn/tmpio.rb +27 -0
  67. data/lib/unicorn/util.rb +90 -0
  68. data/lib/unicorn/worker.rb +140 -0
  69. data/setup.rb +1586 -0
  70. data/t/.gitignore +4 -0
  71. data/t/GNUmakefile +74 -0
  72. data/t/README +42 -0
  73. data/t/before_murder.ru +7 -0
  74. data/t/bin/content-md5-put +36 -0
  75. data/t/bin/sha1sum.rb +17 -0
  76. data/t/bin/unused_listen +40 -0
  77. data/t/broken-app.ru +12 -0
  78. data/t/detach.ru +11 -0
  79. data/t/env.ru +3 -0
  80. data/t/fails-rack-lint.ru +5 -0
  81. data/t/heartbeat-timeout.ru +12 -0
  82. data/t/hijack.ru +42 -0
  83. data/t/listener_names.ru +4 -0
  84. data/t/my-tap-lib.sh +201 -0
  85. data/t/oob_gc.ru +20 -0
  86. data/t/oob_gc_path.ru +20 -0
  87. data/t/pid.ru +3 -0
  88. data/t/preread_input.ru +17 -0
  89. data/t/rack-input-tests.ru +21 -0
  90. data/t/t0000-http-basic.sh +50 -0
  91. data/t/t0001-reload-bad-config.sh +53 -0
  92. data/t/t0002-config-conflict.sh +49 -0
  93. data/t/t0002-parser-error.sh +94 -0
  94. data/t/t0003-working_directory.sh +51 -0
  95. data/t/t0004-heartbeat-timeout.sh +69 -0
  96. data/t/t0004-working_directory_broken.sh +24 -0
  97. data/t/t0005-working_directory_app.rb.sh +40 -0
  98. data/t/t0006-reopen-logs.sh +83 -0
  99. data/t/t0006.ru +13 -0
  100. data/t/t0007-working_directory_no_embed_cli.sh +44 -0
  101. data/t/t0008-back_out_of_upgrade.sh +110 -0
  102. data/t/t0009-broken-app.sh +56 -0
  103. data/t/t0009-winch_ttin.sh +59 -0
  104. data/t/t0010-reap-logging.sh +55 -0
  105. data/t/t0011-active-unix-socket.sh +79 -0
  106. data/t/t0012-reload-empty-config.sh +85 -0
  107. data/t/t0013-rewindable-input-false.sh +24 -0
  108. data/t/t0013.ru +12 -0
  109. data/t/t0014-rewindable-input-true.sh +24 -0
  110. data/t/t0014.ru +12 -0
  111. data/t/t0015-configurator-internals.sh +25 -0
  112. data/t/t0018-write-on-close.sh +23 -0
  113. data/t/t0019-max_header_len.sh +49 -0
  114. data/t/t0020-at_exit-handler.sh +49 -0
  115. data/t/t0021-process_detach.sh +29 -0
  116. data/t/t0022-listener_names-preload_app.sh +32 -0
  117. data/t/t0023-before-murder.sh +40 -0
  118. data/t/t0024-before-murder_once.sh +52 -0
  119. data/t/t0100-rack-input-tests.sh +124 -0
  120. data/t/t0116-client_body_buffer_size.sh +80 -0
  121. data/t/t0116.ru +16 -0
  122. data/t/t0200-rack-hijack.sh +27 -0
  123. data/t/t0300-no-default-middleware.sh +20 -0
  124. data/t/t9000-preread-input.sh +48 -0
  125. data/t/t9001-oob_gc.sh +47 -0
  126. data/t/t9002-oob_gc-path.sh +75 -0
  127. data/t/test-lib.sh +128 -0
  128. data/t/write-on-close.ru +11 -0
  129. data/test/aggregate.rb +15 -0
  130. data/test/benchmark/README +50 -0
  131. data/test/benchmark/dd.ru +18 -0
  132. data/test/benchmark/stack.ru +8 -0
  133. data/test/exec/README +5 -0
  134. data/test/exec/test_exec.rb +1047 -0
  135. data/test/test_helper.rb +297 -0
  136. data/test/unit/test_configurator.rb +175 -0
  137. data/test/unit/test_droplet.rb +28 -0
  138. data/test/unit/test_http_parser.rb +854 -0
  139. data/test/unit/test_http_parser_ng.rb +622 -0
  140. data/test/unit/test_request.rb +182 -0
  141. data/test/unit/test_response.rb +93 -0
  142. data/test/unit/test_server.rb +268 -0
  143. data/test/unit/test_signals.rb +188 -0
  144. data/test/unit/test_socket_helper.rb +197 -0
  145. data/test/unit/test_stream_input.rb +203 -0
  146. data/test/unit/test_tee_input.rb +304 -0
  147. data/test/unit/test_upload.rb +306 -0
  148. data/test/unit/test_util.rb +105 -0
  149. data/unicorn.gemspec +41 -0
  150. metadata +311 -0
@@ -0,0 +1,62 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # :enddoc:
4
+ $stdout.sync = $stderr.sync = true
5
+ $stdin.binmode
6
+ $stdout.binmode
7
+ $stderr.binmode
8
+
9
+ require 'unicorn'
10
+
11
+ module Unicorn::Launcher
12
+
13
+ # We don't do a lot of standard daemonization stuff:
14
+ # * umask is whatever was set by the parent process at startup
15
+ # and can be set in config.ru and config_file, so making it
16
+ # 0000 and potentially exposing sensitive log data can be bad
17
+ # policy.
18
+ # * don't bother to chdir("/") here since unicorn is designed to
19
+ # run inside APP_ROOT. Unicorn will also re-chdir() to
20
+ # the directory it was started in when being re-executed
21
+ # to pickup code changes if the original deployment directory
22
+ # is a symlink or otherwise got replaced.
23
+ def self.daemonize!(options)
24
+ cfg = Unicorn::Configurator
25
+ $stdin.reopen("/dev/null")
26
+
27
+ # We only start a new process group if we're not being reexecuted
28
+ # and inheriting file descriptors from our parent
29
+ unless ENV['UNICORN_FD']
30
+ # grandparent - reads pipe, exits when master is ready
31
+ # \_ parent - exits immediately ASAP
32
+ # \_ unicorn master - writes to pipe when ready
33
+
34
+ rd, wr = IO.pipe
35
+ grandparent = $$
36
+ if fork
37
+ wr.close # grandparent does not write
38
+ else
39
+ rd.close # unicorn master does not read
40
+ Process.setsid
41
+ exit if fork # parent dies now
42
+ end
43
+
44
+ if grandparent == $$
45
+ # this will block until HttpServer#join runs (or it dies)
46
+ master_pid = (rd.readpartial(16) rescue nil).to_i
47
+ unless master_pid > 1
48
+ warn "master failed to start, check stderr log for details"
49
+ exit!(1)
50
+ end
51
+ exit 0
52
+ else # unicorn master process
53
+ options[:ready_pipe] = wr
54
+ end
55
+ end
56
+ # $stderr/$stderr can/will be redirected separately in the Unicorn config
57
+ cfg::DEFAULTS[:stderr_path] ||= "/dev/null"
58
+ cfg::DEFAULTS[:stdout_path] ||= "/dev/null"
59
+ cfg::RACKUP[:daemonized] = true
60
+ end
61
+
62
+ end
@@ -0,0 +1,81 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Strongly consider https://github.com/tmm1/gctools if using Ruby 2.1+
4
+ # It is built on new APIs in Ruby 2.1, so it is more intelligent than
5
+ # this historical implementation.
6
+ #
7
+ # Users on Ruby 2.0 (not 2.1+) may also want to check out
8
+ # lib/middleware/unicorn_oobgc.rb from the Discourse project
9
+ # (https://github.com/discourse/discourse)
10
+ #
11
+ # The following information is only for historical versions of Ruby.
12
+ #
13
+ # Runs GC after requests, after closing the client socket and
14
+ # before attempting to accept more connections.
15
+ #
16
+ # This shouldn't hurt overall performance as long as the server cluster
17
+ # is at <50% CPU capacity, and improves the performance of most memory
18
+ # intensive requests. This serves to improve _client-visible_
19
+ # performance (possibly at the cost of overall performance).
20
+ #
21
+ # Increasing the number of +worker_processes+ may be necessary to
22
+ # improve average client response times because some of your workers
23
+ # will be busy doing GC and unable to service clients. Think of
24
+ # using more workers with this module as a poor man's concurrent GC.
25
+ #
26
+ # We'll call GC after each request is been written out to the socket, so
27
+ # the client never sees the extra GC hit it.
28
+ #
29
+ # This middleware is _only_ effective for applications that use a lot
30
+ # of memory, and will hurt simpler apps/endpoints that can process
31
+ # multiple requests before incurring GC.
32
+ #
33
+ # This middleware is only designed to work with unicorn, as it harms
34
+ # performance with keepalive-enabled servers.
35
+ #
36
+ # Example (in config.ru):
37
+ #
38
+ # require 'unicorn/oob_gc'
39
+ #
40
+ # # GC ever two requests that hit /expensive/foo or /more_expensive/foo
41
+ # # in your app. By default, this will GC once every 5 requests
42
+ # # for all endpoints in your app
43
+ # use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
44
+ #
45
+ # Feedback from users of early implementations of this module:
46
+ # * http://comments.gmane.org/gmane.comp.lang.ruby.unicorn.general/486
47
+ # * http://article.gmane.org/gmane.comp.lang.ruby.unicorn.general/596
48
+ module Unicorn::OobGC
49
+
50
+ # this pretends to be Rack middleware because it used to be
51
+ # But we need to hook into unicorn internals so we need to close
52
+ # the socket before clearing the request env.
53
+ #
54
+ # +interval+ is the number of requests matching the +path+ regular
55
+ # expression before invoking GC.
56
+ def self.new(app, interval = 5, path = %r{\A/})
57
+ @@nr = interval
58
+ self.const_set :OOBGC_PATH, path
59
+ self.const_set :OOBGC_INTERVAL, interval
60
+ ObjectSpace.each_object(Unicorn::HttpServer) do |s|
61
+ s.extend(self)
62
+ self.const_set :OOBGC_ENV, s.instance_variable_get(:@request).env
63
+ end
64
+ app # pretend to be Rack middleware since it was in the past
65
+ end
66
+
67
+ #:stopdoc:
68
+ PATH_INFO = "PATH_INFO"
69
+ def process_client(client)
70
+ super(client) # Unicorn::HttpServer#process_client
71
+ if OOBGC_PATH =~ OOBGC_ENV[PATH_INFO] && ((@@nr -= 1) <= 0)
72
+ @@nr = OOBGC_INTERVAL
73
+ OOBGC_ENV.clear
74
+ disabled = GC.enable
75
+ GC.start
76
+ GC.disable if disabled
77
+ end
78
+ end
79
+
80
+ # :startdoc:
81
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ module Unicorn
4
+ # This middleware is used to ensure input is buffered to memory
5
+ # or disk (depending on size) before the application is dispatched
6
+ # by entirely consuming it (from TeeInput) beforehand.
7
+ #
8
+ # Usage (in config.ru):
9
+ #
10
+ # require 'unicorn/preread_input'
11
+ # if defined?(Unicorn)
12
+ # use Unicorn::PrereadInput
13
+ # end
14
+ # run YourApp.new
15
+ class PrereadInput
16
+
17
+ # :stopdoc:
18
+ def initialize(app)
19
+ @app = app
20
+ end
21
+
22
+ def call(env)
23
+ buf = ""
24
+ input = env["rack.input"]
25
+ if input.respond_to?(:rewind)
26
+ true while input.read(16384, buf)
27
+ input.rewind
28
+ end
29
+ @app.call(env)
30
+ end
31
+ # :startdoc:
32
+ end
33
+ end
@@ -0,0 +1,197 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ require 'socket'
4
+
5
+ module Unicorn
6
+ module SocketHelper
7
+
8
+ # internal interface, only used by Rainbows!/Zbatery
9
+ DEFAULTS = {
10
+ # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
11
+ # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
12
+ # This change shouldn't affect Unicorn users behind nginx (a
13
+ # value of 1 remains an optimization), but Rainbows! users may
14
+ # want to use a higher value on Linux 2.6.32+ to protect against
15
+ # denial-of-service attacks
16
+ :tcp_defer_accept => 1,
17
+
18
+ # FreeBSD, we need to override this to 'dataready' if we
19
+ # eventually support non-HTTP/1.x
20
+ :accept_filter => 'httpready',
21
+
22
+ # same default value as Mongrel
23
+ :backlog => 1024,
24
+
25
+ # favor latency over bandwidth savings
26
+ :tcp_nopush => nil,
27
+ :tcp_nodelay => true,
28
+ }
29
+
30
+ # configure platform-specific options (only tested on Linux 2.6 so far)
31
+ def accf_arg(af_name)
32
+ [ af_name, nil ].pack('a16a240')
33
+ end if RUBY_PLATFORM =~ /freebsd/ && Socket.const_defined?(:SO_ACCEPTFILTER)
34
+
35
+ def set_tcp_sockopt(sock, opt)
36
+ # just in case, even LANs can break sometimes. Linux sysadmins
37
+ # can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
38
+ Socket.const_defined?(:SO_KEEPALIVE) and
39
+ sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
40
+
41
+ if Socket.const_defined?(:TCP_NODELAY)
42
+ val = opt[:tcp_nodelay]
43
+ val = DEFAULTS[:tcp_nodelay] if val.nil?
44
+ sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, val ? 1 : 0)
45
+ end
46
+
47
+ val = opt[:tcp_nopush]
48
+ unless val.nil?
49
+ if Socket.const_defined?(:TCP_CORK) # Linux
50
+ sock.setsockopt(:IPPROTO_TCP, :TCP_CORK, val)
51
+ elsif Socket.const_defined?(:TCP_NOPUSH) # FreeBSD
52
+ sock.setsockopt(:IPPROTO_TCP, :TCP_NOPUSH, val)
53
+ end
54
+ end
55
+
56
+ # No good reason to ever have deferred accepts off in single-threaded
57
+ # servers (except maybe benchmarking)
58
+ if Socket.const_defined?(:TCP_DEFER_ACCEPT)
59
+ # this differs from nginx, since nginx doesn't allow us to
60
+ # configure the the timeout...
61
+ seconds = opt[:tcp_defer_accept]
62
+ seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
63
+ seconds = 0 unless seconds # nil/false means disable this
64
+ sock.setsockopt(:IPPROTO_TCP, :TCP_DEFER_ACCEPT, seconds)
65
+ elsif respond_to?(:accf_arg)
66
+ name = opt[:accept_filter]
67
+ name = DEFAULTS[:accept_filter] if name.nil?
68
+ begin
69
+ sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, accf_arg(name))
70
+ rescue => e
71
+ logger.error("#{sock_name(sock)} " \
72
+ "failed to set accept_filter=#{name} (#{e.inspect})")
73
+ end
74
+ end
75
+ end
76
+
77
+ def set_server_sockopt(sock, opt)
78
+ opt = DEFAULTS.merge(opt || {})
79
+
80
+ TCPSocket === sock and set_tcp_sockopt(sock, opt)
81
+
82
+ rcvbuf, sndbuf = opt.values_at(:rcvbuf, :sndbuf)
83
+ if rcvbuf || sndbuf
84
+ log_buffer_sizes(sock, "before: ")
85
+ sock.setsockopt(:SOL_SOCKET, :SO_RCVBUF, rcvbuf) if rcvbuf
86
+ sock.setsockopt(:SOL_SOCKET, :SO_SNDBUF, sndbuf) if sndbuf
87
+ log_buffer_sizes(sock, " after: ")
88
+ end
89
+ sock.listen(opt[:backlog])
90
+ rescue => e
91
+ Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
92
+ end
93
+
94
+ def log_buffer_sizes(sock, pfx = '')
95
+ rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int
96
+ sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int
97
+ logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
98
+ end
99
+
100
+ # creates a new server, socket. address may be a HOST:PORT or
101
+ # an absolute path to a UNIX socket. address can even be a Socket
102
+ # object in which case it is immediately returned
103
+ def bind_listen(address = '0.0.0.0:8080', opt = {})
104
+ return address unless String === address
105
+
106
+ sock = if address[0] == ?/
107
+ if File.exist?(address)
108
+ if File.socket?(address)
109
+ begin
110
+ UNIXSocket.new(address).close
111
+ # fall through, try to bind(2) and fail with EADDRINUSE
112
+ # (or succeed from a small race condition we can't sanely avoid).
113
+ rescue Errno::ECONNREFUSED
114
+ logger.info "unlinking existing socket=#{address}"
115
+ File.unlink(address)
116
+ end
117
+ else
118
+ raise ArgumentError,
119
+ "socket=#{address} specified but it is not a socket!"
120
+ end
121
+ end
122
+ old_umask = File.umask(opt[:umask] || 0)
123
+ begin
124
+ Kgio::UNIXServer.new(address)
125
+ ensure
126
+ File.umask(old_umask)
127
+ end
128
+ elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
129
+ new_tcp_server($1, $2.to_i, opt.merge(:ipv6=>true))
130
+ elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
131
+ new_tcp_server($1, $2.to_i, opt)
132
+ else
133
+ raise ArgumentError, "Don't know how to bind: #{address}"
134
+ end
135
+ set_server_sockopt(sock, opt)
136
+ sock
137
+ end
138
+
139
+ def new_tcp_server(addr, port, opt)
140
+ # n.b. we set FD_CLOEXEC in the workers
141
+ sock = Socket.new(opt[:ipv6] ? :AF_INET6 : :AF_INET, :SOCK_STREAM)
142
+ if opt.key?(:ipv6only)
143
+ Socket.const_defined?(:IPV6_V6ONLY) or
144
+ abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
145
+ sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
146
+ end
147
+ sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
148
+ if Socket.const_defined?(:SO_REUSEPORT) && opt[:reuseport]
149
+ sock.setsockopt(:SOL_SOCKET, :SO_REUSEPORT, 1)
150
+ end
151
+ sock.bind(Socket.pack_sockaddr_in(port, addr))
152
+ sock.autoclose = false
153
+ Kgio::TCPServer.for_fd(sock.fileno)
154
+ end
155
+
156
+ # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
157
+ def tcp_name(sock)
158
+ port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
159
+ addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
160
+ end
161
+ module_function :tcp_name
162
+
163
+ # Returns the configuration name of a socket as a string. sock may
164
+ # be a string value, in which case it is returned as-is
165
+ # Warning: TCP sockets may not always return the name given to it.
166
+ def sock_name(sock)
167
+ case sock
168
+ when String then sock
169
+ when UNIXServer
170
+ Socket.unpack_sockaddr_un(sock.getsockname)
171
+ when TCPServer
172
+ tcp_name(sock)
173
+ when Socket
174
+ begin
175
+ tcp_name(sock)
176
+ rescue ArgumentError
177
+ Socket.unpack_sockaddr_un(sock.getsockname)
178
+ end
179
+ else
180
+ raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
181
+ end
182
+ end
183
+
184
+ module_function :sock_name
185
+
186
+ # casts a given Socket to be a TCPServer or UNIXServer
187
+ def server_cast(sock)
188
+ begin
189
+ Socket.unpack_sockaddr_in(sock.getsockname)
190
+ Kgio::TCPServer.for_fd(sock.fileno)
191
+ rescue ArgumentError
192
+ Kgio::UNIXServer.for_fd(sock.fileno)
193
+ end
194
+ end
195
+
196
+ end # module SocketHelper
197
+ end # module Unicorn
@@ -0,0 +1,146 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # When processing uploads, Unicorn may expose a StreamInput object under
4
+ # "rack.input" of the (future) Rack (2.x) environment.
5
+ class Unicorn::StreamInput
6
+ # The I/O chunk size (in +bytes+) for I/O operations where
7
+ # the size cannot be user-specified when a method is called.
8
+ # The default is 16 kilobytes.
9
+ @@io_chunk_size = Unicorn::Const::CHUNK_SIZE
10
+
11
+ # Initializes a new StreamInput object. You normally do not have to call
12
+ # this unless you are writing an HTTP server.
13
+ def initialize(socket, request)
14
+ @chunked = request.content_length.nil?
15
+ @socket = socket
16
+ @parser = request
17
+ @buf = request.buf
18
+ @rbuf = ''
19
+ @bytes_read = 0
20
+ filter_body(@rbuf, @buf) unless @buf.empty?
21
+ end
22
+
23
+ # :call-seq:
24
+ # ios.read([length [, buffer ]]) => string, buffer, or nil
25
+ #
26
+ # Reads at most length bytes from the I/O stream, or to the end of
27
+ # file if length is omitted or is nil. length must be a non-negative
28
+ # integer or nil. If the optional buffer argument is present, it
29
+ # must reference a String, which will receive the data.
30
+ #
31
+ # At end of file, it returns nil or '' depend on length.
32
+ # ios.read() and ios.read(nil) returns ''.
33
+ # ios.read(length [, buffer]) returns nil.
34
+ #
35
+ # If the Content-Length of the HTTP request is known (as is the common
36
+ # case for POST requests), then ios.read(length [, buffer]) will block
37
+ # until the specified length is read (or it is the last chunk).
38
+ # Otherwise, for uncommon "Transfer-Encoding: chunked" requests,
39
+ # ios.read(length [, buffer]) will return immediately if there is
40
+ # any data and only block when nothing is available (providing
41
+ # IO#readpartial semantics).
42
+ def read(length = nil, rv = '')
43
+ if length
44
+ if length <= @rbuf.size
45
+ length < 0 and raise ArgumentError, "negative length #{length} given"
46
+ rv.replace(@rbuf.slice!(0, length))
47
+ else
48
+ to_read = length - @rbuf.size
49
+ rv.replace(@rbuf.slice!(0, @rbuf.size))
50
+ until to_read == 0 || eof? || (rv.size > 0 && @chunked)
51
+ @socket.kgio_read(to_read, @buf) or eof!
52
+ filter_body(@rbuf, @buf)
53
+ rv << @rbuf
54
+ to_read -= @rbuf.size
55
+ end
56
+ @rbuf.replace('')
57
+ end
58
+ rv = nil if rv.empty? && length != 0
59
+ else
60
+ read_all(rv)
61
+ end
62
+ rv
63
+ end
64
+
65
+ # :call-seq:
66
+ # ios.gets => string or nil
67
+ #
68
+ # Reads the next ``line'' from the I/O stream; lines are separated
69
+ # by the global record separator ($/, typically "\n"). A global
70
+ # record separator of nil reads the entire unread contents of ios.
71
+ # Returns nil if called at the end of file.
72
+ # This takes zero arguments for strict Rack::Lint compatibility,
73
+ # unlike IO#gets.
74
+ def gets
75
+ sep = $/
76
+ if sep.nil?
77
+ read_all(rv = '')
78
+ return rv.empty? ? nil : rv
79
+ end
80
+ re = /\A(.*?#{Regexp.escape(sep)})/
81
+
82
+ begin
83
+ @rbuf.sub!(re, '') and return $1
84
+ return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size) if eof?
85
+ @socket.kgio_read(@@io_chunk_size, @buf) or eof!
86
+ filter_body(once = '', @buf)
87
+ @rbuf << once
88
+ end while true
89
+ end
90
+
91
+ # :call-seq:
92
+ # ios.each { |line| block } => ios
93
+ #
94
+ # Executes the block for every ``line'' in *ios*, where lines are
95
+ # separated by the global record separator ($/, typically "\n").
96
+ def each
97
+ while line = gets
98
+ yield line
99
+ end
100
+
101
+ self # Rack does not specify what the return value is here
102
+ end
103
+
104
+ private
105
+
106
+ def eof?
107
+ if @parser.body_eof?
108
+ while @chunked && ! @parser.parse
109
+ once = @socket.kgio_read(@@io_chunk_size) or eof!
110
+ @buf << once
111
+ end
112
+ @socket = nil
113
+ true
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ def filter_body(dst, src)
120
+ rv = @parser.filter_body(dst, src)
121
+ @bytes_read += dst.size
122
+ rv
123
+ end
124
+
125
+ def read_all(dst)
126
+ dst.replace(@rbuf)
127
+ @socket or return
128
+ until eof?
129
+ @socket.kgio_read(@@io_chunk_size, @buf) or eof!
130
+ filter_body(@rbuf, @buf)
131
+ dst << @rbuf
132
+ end
133
+ ensure
134
+ @rbuf.replace('')
135
+ end
136
+
137
+ def eof!
138
+ # in case client only did a premature shutdown(SHUT_WR)
139
+ # we do support clients that shutdown(SHUT_WR) after the
140
+ # _entire_ request has been sent, and those will not have
141
+ # raised EOFError on us.
142
+ @socket.shutdown if @socket
143
+ ensure
144
+ raise Unicorn::ClientShutdown, "bytes_read=#{@bytes_read}", []
145
+ end
146
+ end