Class: WEBrick::GenericServer

Inherits:
Object
  • Object
show all
Defined in:
lib/webrick/ssl.rb,
lib/webrick/server.rb

Overview

Base TCP server class. You must subclass GenericServer and provide a #run method.

Direct Known Subclasses

HTTPServer

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}, default = Config::General) ⇒ GenericServer

Creates a new generic server from config. The default configuration comes from default.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/webrick/server.rb', line 88

def initialize(config={}, default=Config::General)
  @config = default.dup.update(config)
  @status = :Stop
  @config[:Logger] ||= Log::new
  @logger = @config[:Logger]

  @tokens = Thread::SizedQueue.new(@config[:MaxClients])
  @config[:MaxClients].times{ @tokens.push(nil) }

  webrickv = WEBrick::VERSION
  rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
  @logger.info("WEBrick #{webrickv}")
  @logger.info("ruby #{rubyv}")

  @listeners = []
  @shutdown_pipe = nil
  unless @config[:DoNotListen]
    raise ArgumentError, "Port must be an integer" unless @config[:Port].to_s == @config[:Port].to_i.to_s

    @config[:Port] = @config[:Port].to_i
    if @config[:Listen]
      warn(":Listen option is deprecated; use GenericServer#listen", uplevel: 1)
    end
    listen(@config[:BindAddress], @config[:Port])
    if @config[:Port] == 0
      @config[:Port] = @listeners[0].addr[1]
    end
  end
end

Instance Attribute Details

#configObject (readonly)

The server configuration



66
67
68
# File 'lib/webrick/server.rb', line 66

def config
  @config
end

#listenersObject (readonly)

Sockets listening for connections.



82
83
84
# File 'lib/webrick/server.rb', line 82

def listeners
  @listeners
end

#loggerObject (readonly)

The server logger. This is independent from the HTTP access log.



71
72
73
# File 'lib/webrick/server.rb', line 71

def logger
  @logger
end

#statusObject (readonly)

The server status. One of :Stop, :Running or :Shutdown



61
62
63
# File 'lib/webrick/server.rb', line 61

def status
  @status
end

#tokensObject (readonly)

Tokens control the number of outstanding clients. The :MaxClients configuration sets this.



77
78
79
# File 'lib/webrick/server.rb', line 77

def tokens
  @tokens
end

Instance Method Details

#[](key) ⇒ Object

Retrieves key from the configuration



121
122
123
# File 'lib/webrick/server.rb', line 121

def [](key)
  @config[key]
end

#listen(address, port) ⇒ Object

Adds listeners from address and port to the server. See WEBrick::Utils::create_listeners for details.



168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/webrick/ssl.rb', line 168

def listen(address, port) # :nodoc:
  listeners = Utils::create_listeners(address, port)
  if @config[:SSLEnable]
    listeners.collect!{|svr|
      ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
      ssvr.start_immediately = @config[:SSLStartImmediately]
      ssvr
    }
  end
  @listeners += listeners
  setup_shutdown_pipe
end

#run(sock) ⇒ Object

You must subclass GenericServer and implement #run which accepts a TCP client socket



244
245
246
# File 'lib/webrick/server.rb', line 244

def run(sock)
  @logger.fatal "run() must be provided by user."
end

#setup_ssl_context(config) ⇒ Object

Sets up an SSL context for config



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/webrick/ssl.rb', line 184

def setup_ssl_context(config) # :nodoc:
  unless config[:SSLCertificate]
    cn = config[:SSLCertName]
    comment = config[:SSLCertComment]
    cert, key = Utils::create_self_signed_cert(2048, cn, comment)
    config[:SSLCertificate] = cert
    config[:SSLPrivateKey] = key
  end
  ctx = OpenSSL::SSL::SSLContext.new
  ctx.key = config[:SSLPrivateKey]
  ctx.cert = config[:SSLCertificate]
  ctx.client_ca = config[:SSLClientCA]
  ctx.extra_chain_cert = config[:SSLExtraChainCert]
  ctx.ca_file = config[:SSLCACertificateFile]
  ctx.ca_path = config[:SSLCACertificatePath]
  ctx.cert_store = config[:SSLCertificateStore]
  ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
  ctx.verify_mode = config[:SSLVerifyClient]
  ctx.verify_depth = config[:SSLVerifyDepth]
  ctx.verify_callback = config[:SSLVerifyCallback]
  ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
  ctx.timeout = config[:SSLTimeout]
  ctx.options = config[:SSLOptions]
  ctx.ciphers = config[:SSLCiphers]
  ctx
end

#shutdownObject

Shuts down the server and all listening sockets. New listeners must be provided to restart the server.



234
235
236
237
238
# File 'lib/webrick/server.rb', line 234

def shutdown
  stop

  alarm_shutdown_pipe(&:close)
end

#ssl_contextObject

SSL context for the server when run in SSL mode



153
154
155
156
157
158
159
160
161
# File 'lib/webrick/ssl.rb', line 153

def ssl_context # :nodoc:
  @ssl_context ||= begin
    if @config[:SSLEnable]
      ssl_context = setup_ssl_context(@config)
      @logger.info("\n" + @config[:SSLCertificate].to_text)
      ssl_context
    end
  end
end

#ssl_servername_callback(sslsocket, hostname = nil) ⇒ Object

ServerNameIndication callback



214
215
216
# File 'lib/webrick/ssl.rb', line 214

def ssl_servername_callback(sslsocket, hostname = nil)
  # default
end

#start(&block) ⇒ Object

Starts the server and runs the block for each connection. This method does not return until the server is stopped from a signal handler or another thread using #stop or #shutdown.

If the block raises a subclass of StandardError the exception is logged and ignored. If an IOError or Errno::EBADF exception is raised the exception is ignored. If an Exception subclass is raised the exception is logged and re-raised which stops the server.

To completely shut down a server call #shutdown from ensure:

server = WEBrick::GenericServer.new
# or WEBrick::HTTPServer.new

begin
  server.start
ensure
  server.shutdown
end

Raises:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/webrick/server.rb', line 154

def start(&block)
  raise ServerError, "already started." if @status != :Stop
  server_type = @config[:ServerType] || SimpleServer

  setup_shutdown_pipe

  server_type.start{
    @logger.info \
      "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
    @status = :Running
    call_callback(:StartCallback)

    shutdown_pipe = @shutdown_pipe

    thgroup = ThreadGroup.new
    begin
      while @status == :Running
        begin
          sp = shutdown_pipe[0]
          if svrs = IO.select([sp, *@listeners])
            if svrs[0].include? sp
              # swallow shutdown pipe
              buf = String.new
              nil while String ===
                        sp.read_nonblock([sp.nread, 8].max, buf, exception: false)
              break
            end
            svrs[0].each{|svr|
              @tokens.pop          # blocks while no token is there.
              if sock = accept_client(svr)
                unless config[:DoNotReverseLookup].nil?
                  sock.do_not_reverse_lookup = !!config[:DoNotReverseLookup]
                end
                th = start_thread(sock, &block)
                th[:WEBrickThread] = true
                thgroup.add(th)
              else
                @tokens.push(nil)
              end
            }
          end
        rescue Errno::EBADF, Errno::ENOTSOCK, IOError => ex
          # if the listening socket was closed in GenericServer#shutdown,
          # IO::select raise it.
        rescue StandardError => ex
          msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
          @logger.error msg
        rescue Exception => ex
          @logger.fatal ex
          raise
        end
      end
    ensure
      cleanup_shutdown_pipe(shutdown_pipe)
      cleanup_listener
      @status = :Shutdown
      @logger.info "going to shutdown ..."
      thgroup.list.each{|th| th.join if th[:WEBrickThread] }
      call_callback(:StopCallback)
      @logger.info "#{self.class}#start done."
      @status = :Stop
    end
  }
end

#stopObject

Stops the server from accepting new connections.



222
223
224
225
226
227
228
# File 'lib/webrick/server.rb', line 222

def stop
  if @status == :Running
    @status = :Shutdown
  end

  alarm_shutdown_pipe {|f| f.write_nonblock("\0")}
end