giraffesoft-unicorn 0.93.5

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 (141) hide show
  1. data/.CHANGELOG.old +25 -0
  2. data/.document +16 -0
  3. data/.gitignore +20 -0
  4. data/.mailmap +26 -0
  5. data/CONTRIBUTORS +31 -0
  6. data/COPYING +339 -0
  7. data/DESIGN +105 -0
  8. data/Documentation/.gitignore +5 -0
  9. data/Documentation/GNUmakefile +30 -0
  10. data/Documentation/unicorn.1.txt +167 -0
  11. data/Documentation/unicorn_rails.1.txt +169 -0
  12. data/GIT-VERSION-GEN +40 -0
  13. data/GNUmakefile +270 -0
  14. data/HACKING +113 -0
  15. data/KNOWN_ISSUES +40 -0
  16. data/LICENSE +55 -0
  17. data/PHILOSOPHY +144 -0
  18. data/README +153 -0
  19. data/Rakefile +108 -0
  20. data/SIGNALS +97 -0
  21. data/TODO +16 -0
  22. data/TUNING +70 -0
  23. data/bin/unicorn +165 -0
  24. data/bin/unicorn_rails +208 -0
  25. data/examples/echo.ru +27 -0
  26. data/examples/git.ru +13 -0
  27. data/examples/init.sh +53 -0
  28. data/ext/unicorn_http/c_util.h +107 -0
  29. data/ext/unicorn_http/common_field_optimization.h +111 -0
  30. data/ext/unicorn_http/ext_help.h +73 -0
  31. data/ext/unicorn_http/extconf.rb +14 -0
  32. data/ext/unicorn_http/global_variables.h +91 -0
  33. data/ext/unicorn_http/unicorn_http.rl +715 -0
  34. data/ext/unicorn_http/unicorn_http_common.rl +74 -0
  35. data/lib/unicorn.rb +730 -0
  36. data/lib/unicorn/app/exec_cgi.rb +150 -0
  37. data/lib/unicorn/app/inetd.rb +109 -0
  38. data/lib/unicorn/app/old_rails.rb +31 -0
  39. data/lib/unicorn/app/old_rails/static.rb +60 -0
  40. data/lib/unicorn/cgi_wrapper.rb +145 -0
  41. data/lib/unicorn/configurator.rb +403 -0
  42. data/lib/unicorn/const.rb +37 -0
  43. data/lib/unicorn/http_request.rb +74 -0
  44. data/lib/unicorn/http_response.rb +74 -0
  45. data/lib/unicorn/launcher.rb +39 -0
  46. data/lib/unicorn/socket_helper.rb +138 -0
  47. data/lib/unicorn/tee_input.rb +174 -0
  48. data/lib/unicorn/util.rb +64 -0
  49. data/local.mk.sample +53 -0
  50. data/setup.rb +1586 -0
  51. data/test/aggregate.rb +15 -0
  52. data/test/benchmark/README +50 -0
  53. data/test/benchmark/dd.ru +18 -0
  54. data/test/exec/README +5 -0
  55. data/test/exec/test_exec.rb +855 -0
  56. data/test/rails/app-1.2.3/.gitignore +2 -0
  57. data/test/rails/app-1.2.3/Rakefile +7 -0
  58. data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
  59. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
  60. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
  61. data/test/rails/app-1.2.3/config/boot.rb +11 -0
  62. data/test/rails/app-1.2.3/config/database.yml +12 -0
  63. data/test/rails/app-1.2.3/config/environment.rb +13 -0
  64. data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
  65. data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
  66. data/test/rails/app-1.2.3/config/routes.rb +6 -0
  67. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  68. data/test/rails/app-1.2.3/public/404.html +1 -0
  69. data/test/rails/app-1.2.3/public/500.html +1 -0
  70. data/test/rails/app-2.0.2/.gitignore +2 -0
  71. data/test/rails/app-2.0.2/Rakefile +7 -0
  72. data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
  73. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
  74. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
  75. data/test/rails/app-2.0.2/config/boot.rb +11 -0
  76. data/test/rails/app-2.0.2/config/database.yml +12 -0
  77. data/test/rails/app-2.0.2/config/environment.rb +17 -0
  78. data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
  79. data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
  80. data/test/rails/app-2.0.2/config/routes.rb +6 -0
  81. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  82. data/test/rails/app-2.0.2/public/404.html +1 -0
  83. data/test/rails/app-2.0.2/public/500.html +1 -0
  84. data/test/rails/app-2.1.2/.gitignore +2 -0
  85. data/test/rails/app-2.1.2/Rakefile +7 -0
  86. data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
  87. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
  88. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
  89. data/test/rails/app-2.1.2/config/boot.rb +111 -0
  90. data/test/rails/app-2.1.2/config/database.yml +12 -0
  91. data/test/rails/app-2.1.2/config/environment.rb +17 -0
  92. data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
  93. data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
  94. data/test/rails/app-2.1.2/config/routes.rb +6 -0
  95. data/test/rails/app-2.1.2/db/.gitignore +0 -0
  96. data/test/rails/app-2.1.2/public/404.html +1 -0
  97. data/test/rails/app-2.1.2/public/500.html +1 -0
  98. data/test/rails/app-2.2.2/.gitignore +2 -0
  99. data/test/rails/app-2.2.2/Rakefile +7 -0
  100. data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
  101. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
  102. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
  103. data/test/rails/app-2.2.2/config/boot.rb +111 -0
  104. data/test/rails/app-2.2.2/config/database.yml +12 -0
  105. data/test/rails/app-2.2.2/config/environment.rb +17 -0
  106. data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
  107. data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
  108. data/test/rails/app-2.2.2/config/routes.rb +6 -0
  109. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  110. data/test/rails/app-2.2.2/public/404.html +1 -0
  111. data/test/rails/app-2.2.2/public/500.html +1 -0
  112. data/test/rails/app-2.3.3.1/.gitignore +2 -0
  113. data/test/rails/app-2.3.3.1/Rakefile +7 -0
  114. data/test/rails/app-2.3.3.1/app/controllers/application_controller.rb +5 -0
  115. data/test/rails/app-2.3.3.1/app/controllers/foo_controller.rb +36 -0
  116. data/test/rails/app-2.3.3.1/app/helpers/application_helper.rb +4 -0
  117. data/test/rails/app-2.3.3.1/config/boot.rb +109 -0
  118. data/test/rails/app-2.3.3.1/config/database.yml +12 -0
  119. data/test/rails/app-2.3.3.1/config/environment.rb +17 -0
  120. data/test/rails/app-2.3.3.1/config/environments/development.rb +7 -0
  121. data/test/rails/app-2.3.3.1/config/environments/production.rb +6 -0
  122. data/test/rails/app-2.3.3.1/config/routes.rb +6 -0
  123. data/test/rails/app-2.3.3.1/db/.gitignore +0 -0
  124. data/test/rails/app-2.3.3.1/public/404.html +1 -0
  125. data/test/rails/app-2.3.3.1/public/500.html +1 -0
  126. data/test/rails/app-2.3.3.1/public/x.txt +1 -0
  127. data/test/rails/test_rails.rb +280 -0
  128. data/test/test_helper.rb +296 -0
  129. data/test/unit/test_configurator.rb +150 -0
  130. data/test/unit/test_http_parser.rb +492 -0
  131. data/test/unit/test_http_parser_ng.rb +308 -0
  132. data/test/unit/test_request.rb +184 -0
  133. data/test/unit/test_response.rb +110 -0
  134. data/test/unit/test_server.rb +188 -0
  135. data/test/unit/test_signals.rb +202 -0
  136. data/test/unit/test_socket_helper.rb +133 -0
  137. data/test/unit/test_tee_input.rb +229 -0
  138. data/test/unit/test_upload.rb +297 -0
  139. data/test/unit/test_util.rb +96 -0
  140. data/unicorn.gemspec +42 -0
  141. metadata +228 -0
@@ -0,0 +1,403 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'socket'
4
+ require 'logger'
5
+
6
+ module Unicorn
7
+
8
+ # Implements a simple DSL for configuring a Unicorn server.
9
+ #
10
+ # Example (when used with the unicorn config file):
11
+ # worker_processes 4
12
+ # listen '/tmp/my_app.sock', :backlog => 1
13
+ # listen 9292, :tcp_nopush => true
14
+ # timeout 10
15
+ # pid "/tmp/my_app.pid"
16
+ #
17
+ # # combine REE with "preload_app true" for memory savings
18
+ # # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
19
+ # preload_app true
20
+ # GC.respond_to?(:copy_on_write_friendly=) and
21
+ # GC.copy_on_write_friendly = true
22
+ #
23
+ # before_fork do |server, worker|
24
+ # # the following is recomended for Rails + "preload_app true"
25
+ # # as there's no need for the master process to hold a connection
26
+ # defined?(ActiveRecord::Base) and
27
+ # ActiveRecord::Base.connection.disconnect!
28
+ #
29
+ # # the following allows a new master process to incrementally
30
+ # # phase out the old master process with SIGTTOU to avoid a
31
+ # # thundering herd (especially in the "preload_app false" case)
32
+ # # when doing a transparent upgrade. The last worker spawned
33
+ # # will then kill off the old master process with a SIGQUIT.
34
+ # old_pid = "#{server.config[:pid]}.oldbin"
35
+ # if old_pid != server.pid
36
+ # begin
37
+ # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
38
+ # Process.kill(sig, File.read(old_pid).to_i)
39
+ # rescue Errno::ENOENT, Errno::ESRCH
40
+ # end
41
+ # end
42
+ #
43
+ # # optionally throttle the master from forking too quickly by sleeping
44
+ # sleep 1
45
+ # end
46
+ #
47
+ # after_fork do |server, worker|
48
+ # # per-process listener ports for debugging/admin/migrations
49
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
50
+ # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
51
+ #
52
+ # # the following is required for Rails + "preload_app true",
53
+ # defined?(ActiveRecord::Base) and
54
+ # ActiveRecord::Base.establish_connection
55
+ #
56
+ # # if preload_app is true, then you may also want to check and
57
+ # # restart any other shared sockets/descriptors such as Memcached,
58
+ # # and Redis. TokyoCabinet file handles are safe to reuse
59
+ # # between any number of forked children (assuming your kernel
60
+ # # correctly implements pread()/pwrite() system calls)
61
+ # end
62
+ class Configurator < Struct.new(:set, :config_file)
63
+
64
+ # Default settings for Unicorn
65
+ DEFAULTS = {
66
+ :timeout => 60,
67
+ :logger => Logger.new($stderr),
68
+ :worker_processes => 1,
69
+ :after_fork => lambda { |server, worker|
70
+ server.logger.info("worker=#{worker.nr} spawned pid=#{$$}")
71
+ },
72
+ :before_fork => lambda { |server, worker|
73
+ server.logger.info("worker=#{worker.nr} spawning...")
74
+ },
75
+ :before_exec => lambda { |server|
76
+ server.logger.info("forked child re-executing...")
77
+ },
78
+ :pid => nil,
79
+ :preload_app => false,
80
+ }
81
+
82
+ def initialize(defaults = {}) #:nodoc:
83
+ self.set = Hash.new(:unset)
84
+ use_defaults = defaults.delete(:use_defaults)
85
+ self.config_file = defaults.delete(:config_file)
86
+ set.merge!(DEFAULTS) if use_defaults
87
+ defaults.each { |key, value| self.send(key, value) }
88
+ Hash === set[:listener_opts] or
89
+ set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
90
+ Array === set[:listeners] or set[:listeners] = []
91
+ reload
92
+ end
93
+
94
+ def reload #:nodoc:
95
+ instance_eval(File.read(config_file), config_file) if config_file
96
+ end
97
+
98
+ def commit!(server, options = {}) #:nodoc:
99
+ skip = options[:skip] || []
100
+ set.each do |key, value|
101
+ value == :unset and next
102
+ skip.include?(key) and next
103
+ server.__send__("#{key}=", value)
104
+ end
105
+ end
106
+
107
+ def [](key) # :nodoc:
108
+ set[key]
109
+ end
110
+
111
+ # sets object to the +new+ Logger-like object. The new logger-like
112
+ # object must respond to the following methods:
113
+ # +debug+, +info+, +warn+, +error+, +fatal+, +close+
114
+ def logger(new)
115
+ %w(debug info warn error fatal close).each do |m|
116
+ new.respond_to?(m) and next
117
+ raise ArgumentError, "logger=#{new} does not respond to method=#{m}"
118
+ end
119
+
120
+ set[:logger] = new
121
+ end
122
+
123
+ # sets after_fork hook to a given block. This block will be called by
124
+ # the worker after forking. The following is an example hook which adds
125
+ # a per-process listener to every worker:
126
+ #
127
+ # after_fork do |server,worker|
128
+ # # per-process listener ports for debugging/admin:
129
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
130
+ #
131
+ # # the negative :tries parameter indicates we will retry forever
132
+ # # waiting on the existing process to exit with a 5 second :delay
133
+ # # Existing options for Unicorn::Configurator#listen such as
134
+ # # :backlog, :rcvbuf, :sndbuf are available here as well.
135
+ # server.listen(addr, :tries => -1, :delay => 5, :backlog => 128)
136
+ #
137
+ # # drop permissions to "www-data" in the worker
138
+ # # generally there's no reason to start Unicorn as a priviledged user
139
+ # # as it is not recommended to expose Unicorn to public clients.
140
+ # uid, gid = Process.euid, Process.egid
141
+ # user, group = 'www-data', 'www-data'
142
+ # target_uid = Etc.getpwnam(user).uid
143
+ # target_gid = Etc.getgrnam(group).gid
144
+ # worker.tmp.chown(target_uid, target_gid)
145
+ # if uid != target_uid || gid != target_gid
146
+ # Process.initgroups(user, target_gid)
147
+ # Process::GID.change_privilege(target_gid)
148
+ # Process::UID.change_privilege(target_uid)
149
+ # end
150
+ # end
151
+ def after_fork(*args, &block)
152
+ set_hook(:after_fork, block_given? ? block : args[0])
153
+ end
154
+
155
+ # sets before_fork got be a given Proc object. This Proc
156
+ # object will be called by the master process before forking
157
+ # each worker.
158
+ def before_fork(*args, &block)
159
+ set_hook(:before_fork, block_given? ? block : args[0])
160
+ end
161
+
162
+ # sets the before_exec hook to a given Proc object. This
163
+ # Proc object will be called by the master process right
164
+ # before exec()-ing the new unicorn binary. This is useful
165
+ # for freeing certain OS resources that you do NOT wish to
166
+ # share with the reexeced child process.
167
+ # There is no corresponding after_exec hook (for obvious reasons).
168
+ def before_exec(*args, &block)
169
+ set_hook(:before_exec, block_given? ? block : args[0], 1)
170
+ end
171
+
172
+ # sets the timeout of worker processes to +seconds+. Workers
173
+ # handling the request/app.call/response cycle taking longer than
174
+ # this time period will be forcibly killed (via SIGKILL). This
175
+ # timeout is enforced by the master process itself and not subject
176
+ # to the scheduling limitations by the worker process. Due the
177
+ # low-complexity, low-overhead implementation, timeouts of less
178
+ # than 3.0 seconds can be considered inaccurate and unsafe.
179
+ #
180
+ # For running Unicorn behind nginx, it is recommended to set
181
+ # "fail_timeout=0" for in your nginx configuration like this
182
+ # to have nginx always retry backends that may have had workers
183
+ # SIGKILL-ed due to timeouts.
184
+ #
185
+ # # See http://wiki.nginx.org/NginxHttpUpstreamModule for more details
186
+ # # on nginx upstream configuration:
187
+ # upstream unicorn_backend {
188
+ # # for UNIX domain socket setups:
189
+ # server unix:/path/to/unicorn.sock fail_timeout=0;
190
+ #
191
+ # # for TCP setups
192
+ # server 192.168.0.7:8080 fail_timeout=0;
193
+ # server 192.168.0.8:8080 fail_timeout=0;
194
+ # server 192.168.0.9:8080 fail_timeout=0;
195
+ # }
196
+ def timeout(seconds)
197
+ Numeric === seconds or raise ArgumentError,
198
+ "not numeric: timeout=#{seconds.inspect}"
199
+ seconds >= 3 or raise ArgumentError,
200
+ "too low: timeout=#{seconds.inspect}"
201
+ set[:timeout] = seconds
202
+ end
203
+
204
+ # sets the current number of worker_processes to +nr+. Each worker
205
+ # process will serve exactly one client at a time.
206
+ def worker_processes(nr)
207
+ Integer === nr or raise ArgumentError,
208
+ "not an integer: worker_processes=#{nr.inspect}"
209
+ nr >= 0 or raise ArgumentError,
210
+ "not non-negative: worker_processes=#{nr.inspect}"
211
+ set[:worker_processes] = nr
212
+ end
213
+
214
+ # sets listeners to the given +addresses+, replacing or augmenting the
215
+ # current set. This is for the global listener pool shared by all
216
+ # worker processes. For per-worker listeners, see the after_fork example
217
+ # This is for internal API use only, do not use it in your Unicorn
218
+ # config file. Use listen instead.
219
+ def listeners(addresses) # :nodoc:
220
+ Array === addresses or addresses = Array(addresses)
221
+ addresses.map! { |addr| expand_addr(addr) }
222
+ set[:listeners] = addresses
223
+ end
224
+
225
+ # adds an +address+ to the existing listener set.
226
+ #
227
+ # The following options may be specified (but are generally not needed):
228
+ #
229
+ # +:backlog+: this is the backlog of the listen() syscall.
230
+ #
231
+ # Some operating systems allow negative values here to specify the
232
+ # maximum allowable value. In most cases, this number is only
233
+ # recommendation and there are other OS-specific tunables and
234
+ # variables that can affect this number. See the listen(2)
235
+ # syscall documentation of your OS for the exact semantics of
236
+ # this.
237
+ #
238
+ # If you are running unicorn on multiple machines, lowering this number
239
+ # can help your load balancer detect when a machine is overloaded
240
+ # and give requests to a different machine.
241
+ #
242
+ # Default: 1024
243
+ #
244
+ # +:rcvbuf+, +:sndbuf+: maximum receive and send buffer sizes of sockets
245
+ #
246
+ # These correspond to the SO_RCVBUF and SO_SNDBUF settings which
247
+ # can be set via the setsockopt(2) syscall. Some kernels
248
+ # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
249
+ # there is no need (and it is sometimes detrimental) to specify them.
250
+ #
251
+ # See the socket API documentation of your operating system
252
+ # to determine the exact semantics of these settings and
253
+ # other operating system-specific knobs where they can be
254
+ # specified.
255
+ #
256
+ # Defaults: operating system defaults
257
+ #
258
+ # +:tcp_nodelay+: disables Nagle's algorithm on TCP sockets
259
+ #
260
+ # This has no effect on UNIX sockets.
261
+ #
262
+ # Default: operating system defaults (usually Nagle's algorithm enabled)
263
+ #
264
+ # +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
265
+ #
266
+ # This will prevent partial TCP frames from being sent out.
267
+ # Enabling +tcp_nopush+ is generally not needed or recommended as
268
+ # controlling +tcp_nodelay+ already provides sufficient latency
269
+ # reduction whereas Unicorn does not know when the best times are
270
+ # for flushing corked sockets.
271
+ #
272
+ # This has no effect on UNIX sockets.
273
+ #
274
+ # +:tries+: times to retry binding a socket if it is already in use
275
+ #
276
+ # A negative number indicates we will retry indefinitely, this is
277
+ # useful for migrations and upgrades when individual workers
278
+ # are binding to different ports.
279
+ #
280
+ # Default: 5
281
+ #
282
+ # +:delay+: seconds to wait between successive +tries+
283
+ #
284
+ # Default: 0.5 seconds
285
+ def listen(address, opt = {})
286
+ address = expand_addr(address)
287
+ if String === address
288
+ [ :backlog, :sndbuf, :rcvbuf, :tries ].each do |key|
289
+ value = opt[key] or next
290
+ Integer === value or
291
+ raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
292
+ end
293
+ [ :tcp_nodelay, :tcp_nopush ].each do |key|
294
+ (value = opt[key]).nil? and next
295
+ TrueClass === value || FalseClass === value or
296
+ raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
297
+ end
298
+ unless (value = opt[:delay]).nil?
299
+ Numeric === value or
300
+ raise ArgumentError, "not numeric: delay=#{value.inspect}"
301
+ end
302
+ set[:listener_opts][address].merge!(opt)
303
+ end
304
+
305
+ set[:listeners] << address
306
+ end
307
+
308
+ # sets the +path+ for the PID file of the unicorn master process
309
+ def pid(path); set_path(:pid, path); end
310
+
311
+ # Enabling this preloads an application before forking worker
312
+ # processes. This allows memory savings when using a
313
+ # copy-on-write-friendly GC but can cause bad things to happen when
314
+ # resources like sockets are opened at load time by the master
315
+ # process and shared by multiple children. People enabling this are
316
+ # highly encouraged to look at the before_fork/after_fork hooks to
317
+ # properly close/reopen sockets. Files opened for logging do not
318
+ # have to be reopened as (unbuffered-in-userspace) files opened with
319
+ # the File::APPEND flag are written to atomically on UNIX.
320
+ #
321
+ # In addition to reloading the unicorn-specific config settings,
322
+ # SIGHUP will reload application code in the working
323
+ # directory/symlink when workers are gracefully restarted.
324
+ def preload_app(bool)
325
+ case bool
326
+ when TrueClass, FalseClass
327
+ set[:preload_app] = bool
328
+ else
329
+ raise ArgumentError, "preload_app=#{bool.inspect} not a boolean"
330
+ end
331
+ end
332
+
333
+ # Allow redirecting $stderr to a given path. Unlike doing this from
334
+ # the shell, this allows the unicorn process to know the path its
335
+ # writing to and rotate the file if it is used for logging. The
336
+ # file will be opened with the File::APPEND flag and writes
337
+ # synchronized to the kernel (but not necessarily to _disk_) so
338
+ # multiple processes can safely append to it.
339
+ def stderr_path(path)
340
+ set_path(:stderr_path, path)
341
+ end
342
+
343
+ # Same as stderr_path, except for $stdout
344
+ def stdout_path(path)
345
+ set_path(:stdout_path, path)
346
+ end
347
+
348
+ # expands "unix:path/to/foo" to a socket relative to the current path
349
+ # expands pathnames of sockets if relative to "~" or "~username"
350
+ # expands "*:port and ":port" to "0.0.0.0:port"
351
+ def expand_addr(address) #:nodoc
352
+ return "0.0.0.0:#{address}" if Integer === address
353
+ return address unless String === address
354
+
355
+ case address
356
+ when %r{\Aunix:(.*)\z}
357
+ File.expand_path($1)
358
+ when %r{\A~}
359
+ File.expand_path(address)
360
+ when %r{\A(?:\*:)?(\d+)\z}
361
+ "0.0.0.0:#$1"
362
+ when %r{\A(.*):(\d+)\z}
363
+ # canonicalize the name
364
+ packed = Socket.pack_sockaddr_in($2.to_i, $1)
365
+ Socket.unpack_sockaddr_in(packed).reverse!.join(':')
366
+ else
367
+ address
368
+ end
369
+ end
370
+
371
+ private
372
+
373
+ def set_path(var, path) #:nodoc:
374
+ case path
375
+ when NilClass
376
+ when String
377
+ path = File.expand_path(path)
378
+ File.writable?(File.dirname(path)) or \
379
+ raise ArgumentError, "directory for #{var}=#{path} not writable"
380
+ else
381
+ raise ArgumentError
382
+ end
383
+ set[var] = path
384
+ end
385
+
386
+ def set_hook(var, my_proc, req_arity = 2) #:nodoc:
387
+ case my_proc
388
+ when Proc
389
+ arity = my_proc.arity
390
+ (arity == req_arity) or \
391
+ raise ArgumentError,
392
+ "#{var}=#{my_proc.inspect} has invalid arity: " \
393
+ "#{arity} (need #{req_arity})"
394
+ when NilClass
395
+ my_proc = DEFAULTS[var]
396
+ else
397
+ raise ArgumentError, "invalid type: #{var}=#{my_proc.inspect}"
398
+ end
399
+ set[var] = my_proc
400
+ end
401
+
402
+ end
403
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ module Unicorn
4
+
5
+ # Frequently used constants when constructing requests or responses. Many times
6
+ # the constant just refers to a string with the same contents. Using these constants
7
+ # gave about a 3% to 10% performance improvement over using the strings directly.
8
+ # Symbols did not really improve things much compared to constants.
9
+ module Const
10
+ UNICORN_VERSION="0.93.5"
11
+
12
+ DEFAULT_HOST = "0.0.0.0" # default TCP listen host address
13
+ DEFAULT_PORT = 8080 # default TCP listen port
14
+ DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}"
15
+
16
+ # The basic max request size we'll try to read.
17
+ CHUNK_SIZE=(16 * 1024)
18
+
19
+ # This is the maximum header that is allowed before a client is booted. The parser detects
20
+ # this, but we'd also like to do this as well.
21
+ MAX_HEADER=1024 * (80 + 32)
22
+
23
+ # Maximum request body size before it is moved out of memory and into a tempfile for reading.
24
+ MAX_BODY=MAX_HEADER
25
+
26
+ # common errors we'll send back
27
+ ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
28
+ ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
29
+ EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
30
+
31
+ # A frozen format for this is about 15% faster
32
+ REMOTE_ADDR="REMOTE_ADDR".freeze
33
+ HTTP_EXPECT="HTTP_EXPECT".freeze
34
+ RACK_INPUT="rack.input".freeze
35
+ end
36
+
37
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'stringio'
4
+ require 'unicorn_http'
5
+
6
+ module Unicorn
7
+ class HttpRequest
8
+
9
+ # default parameters we merge into the request env for Rack handlers
10
+ DEFAULTS = {
11
+ "rack.errors" => $stderr,
12
+ "rack.multiprocess" => true,
13
+ "rack.multithread" => false,
14
+ "rack.run_once" => false,
15
+ "rack.version" => [1, 0],
16
+ "SCRIPT_NAME" => "",
17
+
18
+ # this is not in the Rack spec, but some apps may rely on it
19
+ "SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}"
20
+ }
21
+
22
+ NULL_IO = StringIO.new("")
23
+ LOCALHOST = '127.0.0.1'
24
+
25
+ # Being explicitly single-threaded, we have certain advantages in
26
+ # not having to worry about variables being clobbered :)
27
+ BUF = ' ' * Const::CHUNK_SIZE # initial size, may grow
28
+ PARSER = HttpParser.new
29
+ REQ = {}
30
+
31
+ # Does the majority of the IO processing. It has been written in
32
+ # Ruby using about 8 different IO processing strategies.
33
+ #
34
+ # It is currently carefully constructed to make sure that it gets
35
+ # the best possible performance for the common case: GET requests
36
+ # that are fully complete after a single read(2)
37
+ #
38
+ # Anyone who thinks they can make it faster is more than welcome to
39
+ # take a crack at it.
40
+ #
41
+ # returns an environment hash suitable for Rack if successful
42
+ # This does minimal exception trapping and it is up to the caller
43
+ # to handle any socket errors (e.g. user aborted upload).
44
+ def read(socket)
45
+ REQ.clear
46
+ PARSER.reset
47
+
48
+ # From http://www.ietf.org/rfc/rfc3875:
49
+ # "Script authors should be aware that the REMOTE_ADDR and
50
+ # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
51
+ # may not identify the ultimate source of the request. They
52
+ # identify the client for the immediate request to the server;
53
+ # that client may be a proxy, gateway, or other intermediary
54
+ # acting on behalf of the actual source client."
55
+ REQ[Const::REMOTE_ADDR] =
56
+ TCPSocket === socket ? socket.peeraddr.last : LOCALHOST
57
+
58
+ # short circuit the common case with small GET requests first
59
+ if PARSER.headers(REQ, socket.readpartial(Const::CHUNK_SIZE, BUF)).nil?
60
+ data = BUF.dup # socket.readpartial will clobber data
61
+
62
+ # Parser is not done, queue up more data to read and continue parsing
63
+ # an Exception thrown from the PARSER will throw us out of the loop
64
+ begin
65
+ BUF << socket.readpartial(Const::CHUNK_SIZE, data)
66
+ end while PARSER.headers(REQ, BUF).nil?
67
+ end
68
+ REQ[Const::RACK_INPUT] = 0 == PARSER.content_length ?
69
+ NULL_IO : Unicorn::TeeInput.new(socket, REQ, PARSER, BUF)
70
+ REQ.update(DEFAULTS)
71
+ end
72
+
73
+ end
74
+ end