remove kgio from all read(2) and write(2) wrappers
[unicorn.git] / lib / unicorn.rb
blobb817b776bd811a5928a68282a0849a9c1003792d
1 # -*- encoding: binary -*-
2 require 'etc'
3 require 'stringio'
4 require 'kgio'
5 require 'raindrops'
6 require 'io/wait'
8 begin
9   require 'rack'
10 rescue LoadError
11   warn 'rack not available, functionality reduced'
12 end
14 # :stopdoc:
15 # Unicorn module containing all of the classes (include C extensions) for
16 # running a Unicorn web server.  It contains a minimalist HTTP server with just
17 # enough functionality to service web application requests fast as possible.
18 # :startdoc:
20 # unicorn exposes very little of an user-visible API and most of its
21 # internals are subject to change.  unicorn is designed to host Rack
22 # applications, so applications should be written against the Rack SPEC
23 # and not unicorn internals.
24 module Unicorn
26   # Raised inside TeeInput when a client closes the socket inside the
27   # application dispatch.  This is always raised with an empty backtrace
28   # since there is nothing in the application stack that is responsible
29   # for client shutdowns/disconnects.  This exception is visible to Rack
30   # applications unless PrereadInput middleware is loaded.  This
31   # is a subclass of the standard EOFError class and applications should
32   # not rescue it explicitly, but rescue EOFError instead.
33   ClientShutdown = Class.new(EOFError)
35   # :stopdoc:
37   # This returns a lambda to pass in as the app, this does not "build" the
38   # app (which we defer based on the outcome of "preload_app" in the
39   # Unicorn config).  The returned lambda will be called when it is
40   # time to build the app.
41   def self.builder(ru, op)
42     # allow Configurator to parse cli switches embedded in the ru file
43     op = Unicorn::Configurator::RACKUP.merge!(:file => ru, :optparse => op)
44     if ru =~ /\.ru$/ && !defined?(Rack::Builder)
45       abort "rack and Rack::Builder must be available for processing #{ru}"
46     end
48     # always called after config file parsing, may be called after forking
49     lambda do |_, server|
50       inner_app = case ru
51       when /\.ru$/
52         raw = File.read(ru)
53         raw.sub!(/^__END__\n.*/, '')
54         eval("Rack::Builder.new {(\n#{raw}\n)}.to_app", TOPLEVEL_BINDING, ru)
55       else
56         require ru
57         Object.const_get(File.basename(ru, '.rb').capitalize)
58       end
60       if $DEBUG
61         require 'pp'
62         pp({ :inner_app => inner_app })
63       end
65       return inner_app unless server.default_middleware
67       middleware = { # order matters
68         ContentLength: nil,
69         CommonLogger: [ $stderr ],
70         ShowExceptions: nil,
71         Lint: nil,
72         TempfileReaper: nil,
73       }
75       # return value, matches rackup defaults based on env
76       # Unicorn does not support persistent connections, but Rainbows!
77       # does.  Users accustomed to the Rack::Server default
78       # middlewares will need ContentLength middleware.
79       case ENV["RACK_ENV"]
80       when "development"
81       when "deployment"
82         middleware.delete(:ShowExceptions)
83         middleware.delete(:Lint)
84       else
85         return inner_app
86       end
87       Rack::Builder.new do
88         middleware.each do |m, args|
89           use(Rack.const_get(m), *args) if Rack.const_defined?(m)
90         end
91         run inner_app
92       end.to_app
93     end
94   end
96   # returns an array of strings representing TCP listen socket addresses
97   # and Unix domain socket paths.  This is useful for use with
98   # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
99   def self.listener_names
100     Unicorn::HttpServer::LISTENERS.map do |io|
101       Unicorn::SocketHelper.sock_name(io)
102     end + Unicorn::HttpServer::NEW_LISTENERS
103   end
105   def self.log_error(logger, prefix, exc)
106     message = exc.message
107     message = message.dump if /[[:cntrl:]]/ =~ message
108     logger.error "#{prefix}: #{message} (#{exc.class})"
109     exc.backtrace.each { |line| logger.error(line) }
110   end
112   F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
114   def self.pipe # :nodoc:
115     Kgio::Pipe.new.each do |io|
116       # shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
117       # limits.
118       if defined?(F_SETPIPE_SZ)
119         begin
120           io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
121         rescue Errno::EINVAL
122           # old kernel
123         rescue Errno::EPERM
124           # resizes fail if Linux is close to the pipe limit for the user
125           # or if the user does not have permissions to resize
126         end
127       end
128     end
129   end
130   # :startdoc:
132 # :enddoc:
134 %w(const socket_helper stream_input tee_input http_request configurator
135    tmpio util http_response worker http_server).each do |s|
136   require_relative "unicorn/#{s}"