mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Remove TCP mode (#2169)
This commit is contained in:
parent
fc03781cf3
commit
8d3db816fa
10 changed files with 6 additions and 353 deletions
10
History.md
10
History.md
|
@ -4,12 +4,15 @@
|
|||
* Add pumactl `thread-backtraces` command to print thread backtraces (#2053)
|
||||
* Configuration: `environment` is read from `RAILS_ENV`, if `RACK_ENV` can't be found (#2022)
|
||||
* Do not set user_config to quiet by default to allow for file config (#2074)
|
||||
* `Puma.stats` now returns a Hash instead of a JSON string (#2086)
|
||||
* `GC.compact` is called before fork if available (#2093)
|
||||
* Add `requests_count` to workers stats. (#2106)
|
||||
* Changed #connected_port to #connected_ports (#2076)
|
||||
|
||||
* Deprecations, Removals and Breaking API Changes
|
||||
* `Puma.stats` now returns a Hash instead of a JSON string (#2086)
|
||||
* `--control` has been removed. Use `--control-url` (#1487)
|
||||
* `worker_directory` has been removed. Use `directory`
|
||||
* `worker_directory` has been removed. Use `directory`.
|
||||
* `tcp_mode` has been removed without replacement. (#2169)
|
||||
* Changed #connected_port to #connected_ports (#2076)
|
||||
|
||||
* Bugfixes
|
||||
* Windows update extconf.rb for use with ssp and varied Ruby/MSYS2 combinations (#2069)
|
||||
|
@ -24,7 +27,6 @@
|
|||
* Simplify `Runner#start_control` URL parsing (#2111)
|
||||
* Removed the IOBuffer extension and replaced with Ruby (#1980)
|
||||
|
||||
|
||||
## 4.3.3 and 3.12.4 / 2020-02-28
|
||||
|
||||
* Bugfixes
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
# TCP mode
|
||||
|
||||
Puma also could be used as a TCP server to process incoming TCP
|
||||
connections.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
TCP mode can be enabled with CLI option `--tcp-mode`:
|
||||
|
||||
```
|
||||
$ puma --tcp-mode
|
||||
```
|
||||
|
||||
Default ip and port to listen to are `0.0.0.0` and `9292`. You can configure
|
||||
them with `--port` and `--bind` options:
|
||||
|
||||
```
|
||||
$ puma --tcp-mode --bind tcp://127.0.0.1:9293
|
||||
$ puma --tcp-mode --port 9293
|
||||
```
|
||||
|
||||
TCP mode could be set with a configuration file as well with `tcp_mode`
|
||||
and `tcp_mode!` methods:
|
||||
|
||||
```
|
||||
# config/puma.rb
|
||||
tcp_mode
|
||||
```
|
||||
|
||||
When Puma starts in the TCP mode it prints the corresponding message:
|
||||
|
||||
```
|
||||
puma --tcp-mode
|
||||
Puma starting in single mode...
|
||||
...
|
||||
* Mode: Lopez Express (tcp)
|
||||
```
|
||||
|
||||
|
||||
## How to declare an application
|
||||
|
||||
An application to process TCP connections should be declared as a
|
||||
callable object which accepts `env` and `socket` arguments.
|
||||
|
||||
`env` argument is a Hash with following structure:
|
||||
|
||||
```ruby
|
||||
{ "thread" => {}, "REMOTE_ADDR" => "127.0.0.1:51133", "log" => "#<Proc:0x000..." }
|
||||
```
|
||||
|
||||
It consists of:
|
||||
* `thread` - a Hash for each thread in the thread pool that could be
|
||||
used to store information between requests
|
||||
* `REMOTE_ADDR` - a client ip address
|
||||
* `log` - a proc object to write something down
|
||||
|
||||
`log` object could be used this way:
|
||||
|
||||
```ruby
|
||||
env['log'].call('message to log')
|
||||
#> 19/Oct/2019 20:28:53 - 127.0.0.1:51266 - message to log
|
||||
```
|
||||
|
||||
|
||||
## Example of an application
|
||||
|
||||
Let's look at an example of a simple application which just echoes
|
||||
incoming string:
|
||||
|
||||
```ruby
|
||||
# config/puma.rb
|
||||
app do |env, socket|
|
||||
s = socket.gets
|
||||
socket.puts "Echo #{s}"
|
||||
end
|
||||
```
|
||||
|
||||
We can easily access the TCP server with `telnet` command and receive an
|
||||
echo:
|
||||
|
||||
```shell
|
||||
telnet 0.0.0.0 9293
|
||||
Trying 0.0.0.0...
|
||||
Connected to 0.0.0.0.
|
||||
Escape character is '^]'.
|
||||
sssss
|
||||
Echo sssss
|
||||
^CConnection closed by foreign host.
|
||||
```
|
||||
|
||||
|
||||
## Socket management
|
||||
|
||||
After the application finishes, Puma closes the socket. In order to
|
||||
prevent this, the application should set `env['detach'] = true`.
|
|
@ -187,10 +187,6 @@ module Puma
|
|||
end
|
||||
end
|
||||
|
||||
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
||||
user_config.tcp_mode!
|
||||
end
|
||||
|
||||
o.on "--early-hints", "Enable early hints support" do
|
||||
user_config.early_hints
|
||||
end
|
||||
|
|
|
@ -245,14 +245,6 @@ module Puma
|
|||
def app
|
||||
found = options[:app] || load_rackup
|
||||
|
||||
if @options[:mode] == :tcp
|
||||
require 'puma/tcp_logger'
|
||||
|
||||
logger = @options[:logger]
|
||||
quiet = !@options[:log_requests]
|
||||
return TCPLogger.new(logger, found, quiet)
|
||||
end
|
||||
|
||||
if @options[:log_requests]
|
||||
require 'puma/commonlogger'
|
||||
logger = @options[:logger]
|
||||
|
|
|
@ -325,12 +325,6 @@ module Puma
|
|||
@options[:rackup] = path.to_s
|
||||
end
|
||||
|
||||
# Run Puma in TCP mode
|
||||
#
|
||||
def tcp_mode!
|
||||
@options[:mode] = :tcp
|
||||
end
|
||||
|
||||
def early_hints(answer=true)
|
||||
@options[:early_hints] = answer
|
||||
end
|
||||
|
@ -536,11 +530,6 @@ module Puma
|
|||
@options[:directory] = dir.to_s
|
||||
end
|
||||
|
||||
# Run the app as a raw TCP app instead of an HTTP rack app.
|
||||
def tcp_mode
|
||||
@options[:mode] = :tcp
|
||||
end
|
||||
|
||||
# Preload the application before starting the workers; this conflicts with
|
||||
# phased restart feature. This is off by default.
|
||||
#
|
||||
|
|
|
@ -92,10 +92,6 @@ module Puma
|
|||
log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
|
||||
log "* Min threads: #{min_t}, max threads: #{max_t}"
|
||||
log "* Environment: #{ENV['RACK_ENV']}"
|
||||
|
||||
if @options[:mode] == :tcp
|
||||
log "* Mode: Lopez Express (tcp)"
|
||||
end
|
||||
end
|
||||
|
||||
def redirected_io?
|
||||
|
@ -158,10 +154,6 @@ module Puma
|
|||
server.max_threads = max_t
|
||||
server.inherit_binder @launcher.binder
|
||||
|
||||
if @options[:mode] == :tcp
|
||||
server.tcp_mode!
|
||||
end
|
||||
|
||||
if @options[:early_hints]
|
||||
server.early_hints = true
|
||||
end
|
||||
|
|
|
@ -99,10 +99,6 @@ module Puma
|
|||
@binder = bind
|
||||
end
|
||||
|
||||
def tcp_mode!
|
||||
@mode = :tcp
|
||||
end
|
||||
|
||||
# On Linux, use TCP_CORK to better control how the TCP stack
|
||||
# packetizes our stream. This improves both latency and throughput.
|
||||
#
|
||||
|
@ -176,107 +172,6 @@ module Puma
|
|||
@thread_pool and @thread_pool.pool_capacity
|
||||
end
|
||||
|
||||
# Lopez Mode == raw tcp apps
|
||||
|
||||
def run_lopez_mode(background=true)
|
||||
@thread_pool = ThreadPool.new(@min_threads,
|
||||
@max_threads,
|
||||
Hash) do |client, tl|
|
||||
|
||||
io = client.to_io
|
||||
addr = io.peeraddr.last
|
||||
|
||||
if addr.empty?
|
||||
# Set unix socket addrs to localhost
|
||||
addr = "127.0.0.1:0"
|
||||
else
|
||||
addr = "#{addr}:#{io.peeraddr[1]}"
|
||||
end
|
||||
|
||||
env = { 'thread' => tl, REMOTE_ADDR => addr }
|
||||
|
||||
begin
|
||||
@app.call env, client.to_io
|
||||
rescue Object => e
|
||||
STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
|
||||
STDERR.puts e.backtrace
|
||||
end
|
||||
|
||||
client.close unless env['detach']
|
||||
end
|
||||
|
||||
@events.fire :state, :running
|
||||
|
||||
if background
|
||||
@thread = Thread.new do
|
||||
Puma.set_thread_name "server"
|
||||
handle_servers_lopez_mode
|
||||
end
|
||||
return @thread
|
||||
else
|
||||
handle_servers_lopez_mode
|
||||
end
|
||||
end
|
||||
|
||||
def handle_servers_lopez_mode
|
||||
begin
|
||||
check = @check
|
||||
sockets = [check] + @binder.ios
|
||||
pool = @thread_pool
|
||||
|
||||
while @status == :run
|
||||
begin
|
||||
ios = IO.select sockets
|
||||
ios.first.each do |sock|
|
||||
if sock == check
|
||||
break if handle_check
|
||||
else
|
||||
begin
|
||||
if io = sock.accept_nonblock
|
||||
client = Client.new io, nil
|
||||
pool << client
|
||||
end
|
||||
rescue SystemCallError
|
||||
# nothing
|
||||
rescue Errno::ECONNABORTED
|
||||
# client closed the socket even before accept
|
||||
begin
|
||||
io.close
|
||||
rescue
|
||||
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Object => e
|
||||
@events.unknown_error self, e, "Listen loop"
|
||||
end
|
||||
end
|
||||
|
||||
@events.fire :state, @status
|
||||
|
||||
graceful_shutdown if @status == :stop || @status == :restart
|
||||
|
||||
rescue Exception => e
|
||||
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
||||
STDERR.puts e.backtrace
|
||||
ensure
|
||||
begin
|
||||
@check.close
|
||||
rescue
|
||||
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
||||
end
|
||||
|
||||
# Prevent can't modify frozen IOError (RuntimeError)
|
||||
begin
|
||||
@notify.close
|
||||
rescue IOError
|
||||
# no biggy
|
||||
end
|
||||
end
|
||||
|
||||
@events.fire :state, :done
|
||||
end
|
||||
# Runs the server.
|
||||
#
|
||||
# If +background+ is true (the default) then a thread is spun
|
||||
|
@ -290,10 +185,6 @@ module Puma
|
|||
|
||||
@status = :run
|
||||
|
||||
if @mode == :tcp
|
||||
return run_lopez_mode(background)
|
||||
end
|
||||
|
||||
queue_requests = @queue_requests
|
||||
|
||||
@thread_pool = ThreadPool.new(@min_threads,
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
class TCPLogger
|
||||
def initialize(logger, app, quiet=false)
|
||||
@logger = logger
|
||||
@app = app
|
||||
@quiet = quiet
|
||||
end
|
||||
|
||||
FORMAT = "%s - %s"
|
||||
|
||||
def log(who, str)
|
||||
now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
|
||||
|
||||
log_str = "#{now} - #{who} - #{str}"
|
||||
|
||||
case @logger
|
||||
when IO
|
||||
@logger.puts log_str
|
||||
when Events
|
||||
@logger.log log_str
|
||||
end
|
||||
end
|
||||
|
||||
def call(env, socket)
|
||||
who = env[Const::REMOTE_ADDR]
|
||||
log who, "connected" unless @quiet
|
||||
|
||||
env['log'] = lambda { |str| log(who, str) }
|
||||
|
||||
begin
|
||||
@app.call env, socket
|
||||
rescue Object => e
|
||||
log who, "exception: #{e.message} (#{e.class})"
|
||||
else
|
||||
log who, "disconnected" unless @quiet
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
require_relative "helper"
|
||||
|
||||
require "puma/tcp_logger"
|
||||
|
||||
class TestTCPLogger < Minitest::Test
|
||||
|
||||
def setup
|
||||
@events = Puma::Events.new STDOUT, STDERR
|
||||
@server = Puma::Server.new nil, @events
|
||||
|
||||
@server.app = proc { |env, socket|}
|
||||
@server.tcp_mode!
|
||||
|
||||
@socket = nil
|
||||
end
|
||||
|
||||
def test_events
|
||||
# in lib/puma/launcher.rb:85
|
||||
# Puma::Events is default tcp_logger for cluster mode
|
||||
logger = Puma::Events.new(STDOUT, STDERR)
|
||||
out, err = capture_subprocess_io do
|
||||
Puma::TCPLogger.new(logger, @server.app).call({}, @socket)
|
||||
end
|
||||
assert_match(/connected/, out)
|
||||
assert_equal('', err)
|
||||
end
|
||||
|
||||
def test_io
|
||||
# in lib/puma/configuration.rb:184
|
||||
# STDOUT is default tcp_logger for single mode
|
||||
logger = STDOUT
|
||||
out, err = capture_subprocess_io do
|
||||
Puma::TCPLogger.new(logger, @server.app).call({}, @socket)
|
||||
end
|
||||
assert_match(/connected/, out)
|
||||
assert_equal('', err)
|
||||
end
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
require_relative "helper"
|
||||
|
||||
class TestTCPRack < Minitest::Test
|
||||
|
||||
def setup
|
||||
@port = UniquePort.call
|
||||
@host = "127.0.0.1"
|
||||
|
||||
@events = Puma::Events.new STDOUT, STDERR
|
||||
@server = Puma::Server.new nil, @events
|
||||
end
|
||||
|
||||
def teardown
|
||||
@server.stop(true)
|
||||
end
|
||||
|
||||
def test_passes_the_socket
|
||||
@server.tcp_mode!
|
||||
|
||||
body = "We sell hats for a discount!\n"
|
||||
|
||||
@server.app = proc do |env, socket|
|
||||
socket << body
|
||||
socket.close
|
||||
end
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @port
|
||||
|
||||
assert_equal body, sock.read
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue