giraffesoft-unicorn 0.93.5

Sign up to get free protection for your applications and to get access to all the features.
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