mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Merge branch 'master' into chase/dont_error_on_pipe_close
This commit is contained in:
commit
c26cced04b
66 changed files with 964 additions and 430 deletions
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text eol=lf
|
||||
*.png binary
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -16,3 +16,11 @@ Gemfile.lock
|
|||
/test/test_puma.state
|
||||
/test/test_server.sock
|
||||
/test/test_control.sock
|
||||
|
||||
# windows local build artifacts
|
||||
/win_gem_test/shared/
|
||||
/win_gem_test/packages/
|
||||
/win_gem_test/test_logs/
|
||||
/Rakefile_wintest
|
||||
*.gem
|
||||
/lib/puma/puma_http11.rb
|
||||
|
|
40
.rubocop.yml
40
.rubocop.yml
|
@ -1,6 +1,6 @@
|
|||
AllCops:
|
||||
DisabledByDefault: true
|
||||
TargetRubyVersion: 1.9.3
|
||||
TargetRubyVersion: 2.2
|
||||
DisplayCopNames: true
|
||||
StyleGuideCopsOnly: false
|
||||
Exclude:
|
||||
|
@ -51,41 +51,5 @@ Style/MethodDefParentheses:
|
|||
Style/TrailingCommaInArguments:
|
||||
Enabled: true
|
||||
|
||||
Performance/Count:
|
||||
Enabled: true
|
||||
|
||||
Performance/Detect:
|
||||
Enabled: true
|
||||
|
||||
Performance/EndWith:
|
||||
Enabled: true
|
||||
|
||||
Performance/FlatMap:
|
||||
Enabled: true
|
||||
|
||||
Performance/HashEachMethods:
|
||||
Enabled: true
|
||||
|
||||
Performance/RangeInclude:
|
||||
Enabled: true
|
||||
|
||||
Performance/RedundantMerge:
|
||||
Enabled: true
|
||||
|
||||
Performance/RedundantSortBy:
|
||||
Enabled: true
|
||||
|
||||
Performance/ReverseEach:
|
||||
Enabled: true
|
||||
|
||||
Performance/Sample:
|
||||
Enabled: true
|
||||
|
||||
Performance/Size:
|
||||
Enabled: true
|
||||
|
||||
Performance/StartWith:
|
||||
Enabled: true
|
||||
|
||||
Performance/TimesMap:
|
||||
Performance:
|
||||
Enabled: true
|
||||
|
|
40
.travis.yml
40
.travis.yml
|
@ -3,27 +3,43 @@ sudo: false
|
|||
group: beta
|
||||
language: ruby
|
||||
cache: bundler
|
||||
|
||||
before_install:
|
||||
# https://github.com/travis-ci/travis-ci/issues/9383#issuecomment-377680108
|
||||
- gem install bundler
|
||||
branches:
|
||||
only:
|
||||
- "master"
|
||||
# rubygems 2.7.8 and greater include bundler
|
||||
- |
|
||||
rv="$(ruby -e 'STDOUT.write RUBY_VERSION')";
|
||||
if [ "$rv" \< "2.3" ]; then gem update --system 2.7.8 --no-document
|
||||
elif [ "$rv" \< "2.6" ]; then gem update --system --no-document --conservative
|
||||
fi
|
||||
- ruby -v && gem --version && bundle version
|
||||
|
||||
rvm:
|
||||
- 2.2.10
|
||||
- 2.3.7
|
||||
- 2.4.4
|
||||
- 2.5.1
|
||||
- 2.3.8
|
||||
- 2.4.5
|
||||
- 2.5.3
|
||||
- ruby-head
|
||||
- jruby-9.1.17.0
|
||||
- jruby-head
|
||||
- rbx-3
|
||||
- jruby-9.2.0.0
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
include:
|
||||
- rvm: ruby-head
|
||||
env: RUBYOPT="--jit"
|
||||
- rvm: 2.3.8
|
||||
os: osx
|
||||
- rvm: 2.5.3
|
||||
os: osx
|
||||
- rvm: jruby-head
|
||||
- rvm: rbx-3
|
||||
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: ruby-head
|
||||
env: RUBYOPT="--jit"
|
||||
- rvm: jruby-head
|
||||
- rvm: rbx-3
|
||||
|
||||
env:
|
||||
global:
|
||||
- TESTOPTS="-v"
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -6,13 +6,15 @@ gem "rdoc"
|
|||
gem "rake-compiler"
|
||||
|
||||
gem "rack", "< 3.0"
|
||||
gem "minitest", "~> 5.9"
|
||||
gem "minitest", "~> 5.11"
|
||||
gem "minitest-retry"
|
||||
|
||||
gem "jruby-openssl", :platform => "jruby"
|
||||
|
||||
gem "rubocop", "~> 0.50.0"
|
||||
gem "rubocop", "~> 0.58.0"
|
||||
|
||||
if %w(2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1).include? RUBY_VERSION
|
||||
gem "stopgap_13632", "~> 1.0", :platforms => ["mri", "mingw", "x64_mingw"]
|
||||
end
|
||||
|
||||
gem 'm'
|
||||
|
|
13
History.md
13
History.md
|
@ -1,3 +1,16 @@
|
|||
## 3.12.0 / 2018-07-13
|
||||
|
||||
* 5 features:
|
||||
* You can now specify which SSL ciphers the server should support, default is unchanged (#1478)
|
||||
* The setting for Puma's `max_threads` is now in `Puma.stats` (#1604)
|
||||
* Pool capacity is now in `Puma.stats` (#1579)
|
||||
* Installs restricted to Ruby 2.2+ (#1506)
|
||||
* `--control` is now deprecated in favor of `--control-url` (#1487)
|
||||
|
||||
* 2 bugfixes:
|
||||
* Workers will no longer accept more web requests than they have capacity to process. This prevents an issue where one worker would accept lots of requests while starving other workers (#1563)
|
||||
* In a test env puma now emits the stack on an exception (#1557)
|
||||
|
||||
## 3.11.4 / 2018-04-12
|
||||
|
||||
* 2 features:
|
||||
|
|
23
README.md
23
README.md
|
@ -7,8 +7,8 @@
|
|||
[](https://gitter.im/puma/puma?utm\_source=badge&utm\_medium=badge&utm\_campaign=pr-badge)
|
||||
[](http://travis-ci.org/puma/puma)
|
||||
[](https://ci.appveyor.com/project/nateberkopec/puma)
|
||||
[](https://gemnasium.com/puma/puma)
|
||||
[](https://codeclimate.com/github/puma/puma)
|
||||
[](https://dependabot.com/compatibility-score.html?dependency-name=puma&package-manager=bundler&version-scheme=semver)
|
||||
|
||||
Puma is a **simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications** in development and production.
|
||||
|
||||
|
@ -25,7 +25,7 @@ On MRI, there is a Global VM Lock (GVL) that ensures only one thread can run Rub
|
|||
```
|
||||
$ gem install puma
|
||||
$ puma <any rackup (*.ru) file>
|
||||
```
|
||||
```
|
||||
|
||||
## Frameworks
|
||||
|
||||
|
@ -160,18 +160,31 @@ Need a bit of security? Use SSL sockets:
|
|||
```
|
||||
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
|
||||
```
|
||||
|
||||
#### Controlling SSL Cipher Suites
|
||||
Need to use or avoid specific SSL cipher suites? Use ssl_cipher_filter or ssl_cipher_list options.
|
||||
#####Ruby:
|
||||
|
||||
Need to use or avoid specific SSL cipher suites? Use `ssl_cipher_filter` or `ssl_cipher_list` options.
|
||||
|
||||
##### Ruby:
|
||||
|
||||
```
|
||||
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA'
|
||||
```
|
||||
#####JRuby:
|
||||
|
||||
##### JRuby:
|
||||
|
||||
```
|
||||
$ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA'
|
||||
```
|
||||
|
||||
See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites.
|
||||
|
||||
Don't want to use insecure TLSv1.0 ?
|
||||
|
||||
```
|
||||
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true'
|
||||
```
|
||||
|
||||
### Control/Status Server
|
||||
|
||||
Puma has a built-in status/control app that can be used to query and control Puma itself.
|
||||
|
|
10
Rakefile
10
Rakefile
|
@ -73,16 +73,6 @@ else
|
|||
task :test => [:compile]
|
||||
end
|
||||
|
||||
task :test => [:ensure_no_puma_gem]
|
||||
task :ensure_no_puma_gem do
|
||||
Bundler.with_clean_env do
|
||||
out = `gem list puma`.strip
|
||||
if !$?.success? || out != ""
|
||||
abort "No other puma version should be installed to avoid false positives or loading it by accident but found #{out}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
desc "Run the integration tests"
|
||||
task :integration do
|
||||
|
|
117
appveyor.yml
117
appveyor.yml
|
@ -1,109 +1,20 @@
|
|||
# cache cleanup 2018-03-16
|
||||
|
||||
init:
|
||||
- set PATH=C:\ruby%ruby_version%\bin;C:\Program Files\7-Zip;C:\Program Files\AppVeyor\BuildAgent;C:\Program Files\Git\cmd;C:\Windows\system32
|
||||
# Download current trunk, install OpenSSL via trunk_pkgs.cmd file
|
||||
- if %ruby_version%==_trunk (
|
||||
appveyor DownloadFile https://ci.appveyor.com/api/projects/MSP-Greg/ruby-loco/artifacts/ruby_trunk.7z -FileName C:\ruby_trunk.7z &
|
||||
7z x C:\ruby_trunk.7z -oC:\ruby_trunk &
|
||||
C:\ruby_trunk\trunk_pkgs.cmd
|
||||
)
|
||||
|
||||
install:
|
||||
# Install ragel
|
||||
- if "%ri_file%"=="x64_2" ( C:\msys64\usr\bin\pacman -S --noconfirm mingw-w64-x86_64-ragel )
|
||||
- if "%ri_file%"=="x86_2" ( C:\msys64\usr\bin\pacman -S --noconfirm mingw-w64-i686-ragel )
|
||||
|
||||
# Install ragel & download RI OpenSSL Knapsack package
|
||||
# RI DevKit is only installed in Ruby23 and Ruby23-x64 folders
|
||||
# RI2 MSYS2/MinGW OpenSSL package is standard Appveyor item
|
||||
- if "%ri_file%"=="x86" (
|
||||
appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x86/openssl-1.0.2j-x86-windows.tar.lzma &
|
||||
7z e openssl-1.0.2j-x86-windows.tar.lzma &
|
||||
7z x -y openssl-1.0.2j-x86-windows.tar -oC:\ruby23\DevKit\mingw &
|
||||
set b_config="--with-ssl-dir=C:/ruby23/DevKit/mingw --with-opt-include=C:/ruby23/DevKit/mingw/include" &
|
||||
set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem &
|
||||
C:\msys64\usr\bin\pacman -S --noconfirm mingw-w64-i686-ragel &
|
||||
set PATH=%PATH%;C:\msys64\ming32\bin
|
||||
)
|
||||
- if "%ri_file%"=="x64" (
|
||||
appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x64/openssl-1.0.2j-x64-windows.tar.lzma &
|
||||
7z e openssl-1.0.2j-x64-windows.tar.lzma &
|
||||
7z x -y openssl-1.0.2j-x64-windows.tar -oC:\ruby23-x64\DevKit\mingw &
|
||||
set b_config="--with-ssl-dir=C:/ruby23-x64/DevKit/mingw --with-opt-include=C:/ruby23-x64/DevKit/mingw/include" &
|
||||
set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem &
|
||||
C:\msys64\usr\bin\pacman -S --noconfirm mingw-w64-x86_64-ragel &
|
||||
set PATH=%PATH%;C:\msys64\ming64\bin
|
||||
)
|
||||
- RAKEOPT:
|
||||
- APPVEYOR: true
|
||||
- ruby --version
|
||||
- gem --version
|
||||
- bundle --version
|
||||
- bundle install --without documentation --path C:/av_bundle
|
||||
|
||||
# Download & install current OpenSSL package for later RubyInstaller2 version(s)
|
||||
- if %ruby_version%==25-x64 (
|
||||
set openssl=mingw-w64-x86_64-openssl-1.1.0.g-1-any.pkg.tar.xz
|
||||
)
|
||||
- if %ruby_version%==25 (
|
||||
set openssl=mingw-w64-i686-openssl-1.1.0.g-1-any.pkg.tar.xz
|
||||
)
|
||||
- set dl_uri=https://dl.bintray.com/msp-greg/ruby_trunk
|
||||
- if %ruby_version%==25-x64 (
|
||||
C:\msys64\usr\bin\bash -lc "pacman-key -r 77D8FA18 --keyserver na.pool.sks-keyservers.net && pacman-key -f 77D8FA18 && pacman-key --lsign-key 77D8FA18" &
|
||||
appveyor DownloadFile %dl_uri%/%openssl% -FileName C:\%openssl% &
|
||||
appveyor DownloadFile %dl_uri%/%openssl%.sig -FileName C:\%openssl%.sig &
|
||||
C:\msys64\usr\bin\pacman -Rdd --noconfirm mingw-w64-x86_64-openssl &
|
||||
C:\msys64\usr\bin\pacman -Udd --noconfirm --force C:\%openssl%
|
||||
)
|
||||
- if %ruby_version%==25 (
|
||||
C:\msys64\usr\bin\bash -lc "pacman-key -r 77D8FA18 --keyserver na.pool.sks-keyservers.net && pacman-key -f 77D8FA18 && pacman-key --lsign-key 77D8FA18" &
|
||||
appveyor DownloadFile %dl_uri%/%openssl% -FileName C:\%openssl% &
|
||||
appveyor DownloadFile %dl_uri%/%openssl%.sig -FileName C:\%openssl%.sig &
|
||||
C:\msys64\usr\bin\pacman -Rdd --noconfirm mingw-w64-686-openssl &
|
||||
C:\msys64\usr\bin\pacman -Udd --noconfirm --force C:\%openssl%
|
||||
)
|
||||
# download shared script files
|
||||
- ps: >-
|
||||
if ( !(Test-Path -Path ./shared -PathType Container) ) {
|
||||
$uri = 'https://ci.appveyor.com/api/projects/MSP-Greg/av-gem-build-test/artifacts/shared.7z'
|
||||
$7z = 'C:\Program Files\7-Zip\7z.exe'
|
||||
$fn = "$env:TEMP\shared.7z"
|
||||
(New-Object System.Net.WebClient).DownloadFile($uri, $fn)
|
||||
&$7z x $fn -owin_gem_test 1> $null
|
||||
Remove-Item -LiteralPath $fn -Force
|
||||
Write-Host "Downloaded shared files" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
build_script:
|
||||
- bundle exec rake -rdevkit compile -- %b_config%
|
||||
|
||||
test_script:
|
||||
- set OPENSSL_DIR=
|
||||
- bundle exec rake -rdevkit test TESTOPTS="--verbose"
|
||||
|
||||
on_finish:
|
||||
- ruby -v
|
||||
- ps: .\win_gem_test\puma.ps1 $env:gem_bits
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- ruby_version: _trunk
|
||||
b_config: "--use-system-libraries"
|
||||
ri_file: x64_2
|
||||
- ruby_version: 25
|
||||
b_config: "--use-system-libraries"
|
||||
ri_file: x86_2
|
||||
- ruby_version: 25-x64
|
||||
b_config: "--use-system-libraries"
|
||||
ri_file: x64_2
|
||||
- ruby_version: 24
|
||||
b_config: "--use-system-libraries"
|
||||
ri_file: x86_2
|
||||
- ruby_version: 24-x64
|
||||
b_config: "--use-system-libraries"
|
||||
ri_file: x64_2
|
||||
- ruby_version: 23
|
||||
ri_file: x86
|
||||
- ruby_version: 23-x64
|
||||
ri_file: x64
|
||||
- ruby_version: 22
|
||||
ri_file: x86
|
||||
- ruby_version: 22-x64
|
||||
ri_file: x64
|
||||
|
||||
cache:
|
||||
- C:\av_bundle
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- gem_bits: 64
|
||||
- gem_bits: 32
|
||||
|
|
|
@ -20,6 +20,7 @@ Clustered mode is shown/discussed here. Single mode is analogous to having a sin
|
|||
* By default, a single, separate thread is used to receive HTTP requests across the socket.
|
||||
* When at least one worker thread is available for work, a connection is accepted and placed in this request buffer
|
||||
* This thread waits for entire HTTP requests to be received over the connection
|
||||
* The time spent waiting for the HTTP request body to be received is exposed to the Rack app as `env['puma.request_body_wait']` (milliseconds)
|
||||
* Once received, the connection is pushed into the "todo" set
|
||||
* Worker threads pop work off the "todo" set for processing
|
||||
* The thread processes the request via the rack application (which generates the HTTP response)
|
||||
|
|
|
@ -38,22 +38,42 @@ Here are some rules of thumb:
|
|||
* As you grow more confident in the thread safety of your app, you can tune the
|
||||
workers down and the threads up.
|
||||
|
||||
#### Ubuntu / Systemd (Systemctl) Installation
|
||||
|
||||
See [systemd.md](systemd.md)
|
||||
|
||||
#### Worker utilization
|
||||
|
||||
**How do you know if you're got enough (or too many workers)?**
|
||||
**How do you know if you've got enough (or too many workers)?**
|
||||
|
||||
A good question. Due to MRI's GIL, only one thread can be executing Ruby code at a time.
|
||||
But since so many apps are waiting on IO from DBs, etc., they can utilize threads
|
||||
to make better use of the process.
|
||||
|
||||
The rule of thumb is you never want processes that are pegged all the time. This
|
||||
means that there is more work to do that the process can get through. On the other
|
||||
means that there is more work to do than the process can get through. On the other
|
||||
hand, if you have processes that sit around doing nothing, then they're just eating
|
||||
up resources.
|
||||
|
||||
Watching your CPU utilization over time and aim for about 70% on average. This means
|
||||
Watch your CPU utilization over time and aim for about 70% on average. This means
|
||||
you've got capacity still but aren't starving threads.
|
||||
|
||||
**Measuring utilization**
|
||||
|
||||
Using a timestamp header from an upstream proxy server (eg. nginx or haproxy), it's
|
||||
possible to get an indication of how long requests have been waiting for a Puma
|
||||
thread to become available.
|
||||
|
||||
* Have your upstream proxy set a header with the time it received the request:
|
||||
* nginx: `proxy_set_header X-Request-Start "${msec}";`
|
||||
* haproxy: `http-request set-header X-Request-Start "%t";`
|
||||
* In your Rack middleware, determine the amount of time elapsed since `X-Request-Start`.
|
||||
* To improve accuracy, you will want to subtract time spent waiting for slow clients:
|
||||
* `env['puma.request_body_wait']` contains the number of milliseconds Puma spent
|
||||
waiting for the client to send the request body.
|
||||
* haproxy: `%Th` (TLS handshake time) and `%Ti` (idle time before request) can
|
||||
can also be added as headers.
|
||||
|
||||
## Daemonizing
|
||||
|
||||
I prefer to not daemonize my servers and use something like `runit` or `upstart` to
|
||||
|
@ -62,7 +82,7 @@ makes it easy to figure out what is going on. Additionally, unlike `unicorn`,
|
|||
puma does not require daemonization to do zero-downtime restarts.
|
||||
|
||||
I see people using daemonization because they start puma directly via capistrano
|
||||
task and thus want it to live on past the `cap deploy`. To this people I said:
|
||||
task and thus want it to live on past the `cap deploy`. To these people I say:
|
||||
You need to be using a process monitor. Nothing is making sure puma stays up in
|
||||
this scenario! You're just waiting for something weird to happen, puma to die,
|
||||
and to get paged at 3am. Do yourself a favor, at least the process monitoring
|
||||
|
|
|
@ -22,6 +22,8 @@ But again beware, upgrading an application sometimes involves upgrading the data
|
|||
|
||||
If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (`pumactl restart`). That way, no code is shared while deploying (in that case, `preload_app!` might help for quicker deployment, see ["Clustered Mode" in the README](../README.md#clustered-mode)).
|
||||
|
||||
**Note**: Hot and phased restarts are only available on MRI, not on JRuby. They are also unavailable on Windows servers.
|
||||
|
||||
### Release Directory
|
||||
|
||||
If your symlink releases into a common working directory (i.e., `/current` from Capistrano), Puma won't pick up your new changes when running phased restarts without additional configuration. You should set your working directory within Puma's config to specify the directory it should use. This is a change from earlier versions of Puma (< 2.15) that would infer the directory for you.
|
||||
|
|
|
@ -32,21 +32,26 @@ Type=simple
|
|||
# Preferably configure a non-privileged user
|
||||
# User=
|
||||
|
||||
# The path to the puma application root
|
||||
# Also replace the "<WD>" place holders below with this path.
|
||||
WorkingDirectory=
|
||||
# The path to the your application code root directory.
|
||||
# Also replace the "<YOUR_APP_PATH>" place holders below with this path.
|
||||
# Example /home/username/myapp
|
||||
WorkingDirectory=<YOUR_APP_PATH>
|
||||
|
||||
# Helpful for debugging socket activation, etc.
|
||||
# Environment=PUMA_DEBUG=1
|
||||
|
||||
# The command to start Puma. This variant uses a binstub generated via
|
||||
# `bundle binstubs puma --path ./sbin` in the WorkingDirectory
|
||||
# (replace "<WD>" below)
|
||||
ExecStart=<WD>/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
|
||||
# SystemD will not run puma even if it is in your path. You must specify
|
||||
# an absolute URL to puma. For example /usr/local/bin/puma
|
||||
# Alternatively, create a binstub with `bundle binstubs puma --path ./sbin` in the WorkingDirectory
|
||||
ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/puma.rb
|
||||
|
||||
# Variant: Rails start.
|
||||
# ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/config/puma.rb ../config.ru
|
||||
|
||||
# Variant: Use config file with `bind` directives instead:
|
||||
# ExecStart=<WD>/sbin/puma -C config.rb
|
||||
# Variant: Use `bundle exec --keep-file-descriptors puma` instead of binstub
|
||||
# Variant: Specify directives inline.
|
||||
# ExecStart=/<FULLPATH>/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
|
||||
|
||||
|
||||
Restart=always
|
||||
|
||||
|
@ -247,6 +252,12 @@ PIDFile=<WD>/shared/tmp/pids/puma.pid
|
|||
# reconsider if you actually need the forking config.
|
||||
Restart=no
|
||||
|
||||
# `puma_ctl restart` wouldn't work without this. It's because `pumactl`
|
||||
# changes PID on restart and systemd stops the service afterwards
|
||||
# because of the PID change. This option prevents stopping after PID
|
||||
# change.
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
~~~~
|
||||
|
|
|
@ -80,8 +80,10 @@
|
|||
# can also use the "ssl_bind" option.
|
||||
#
|
||||
# ssl_bind '127.0.0.1', '9292', {
|
||||
# cert: path_to_cert,
|
||||
# key: path_to_key,
|
||||
# cert: path_to_cert
|
||||
# ssl_cipher_filter: cipher_filter, # optional
|
||||
# verify_mode: verify_mode, # default 'none'
|
||||
# }
|
||||
# for JRuby additional keys are required:
|
||||
# keystore: path_to_keystore,
|
||||
|
@ -174,7 +176,8 @@
|
|||
# the given timeout. If not the worker process will be restarted. This is
|
||||
# not a request timeout, it is to protect against a hung or dead process.
|
||||
# Setting this value will not protect against slow requests.
|
||||
# Default value is 60 seconds.
|
||||
#
|
||||
# The minimum value is 6 seconds, the default value is 60 seconds.
|
||||
#
|
||||
# worker_timeout 60
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'redis'
|
|||
# 1. Add this plugin to your 'lib' directory
|
||||
# 2. In the `puma.rb` config file add the following lines
|
||||
# === Plugins ===
|
||||
# require './lib/puma/plugins/redis_stop_puma'
|
||||
# require './lib/puma/plugin/redis_stop_puma'
|
||||
# plugin 'redis_stop_puma'
|
||||
# 3. Now, when you set the redis key "puma::restart::web.1", your web.1 dyno
|
||||
# will restart
|
||||
|
|
|
@ -6,11 +6,13 @@ import org.jruby.Ruby;
|
|||
import org.jruby.runtime.load.BasicLibraryService;
|
||||
|
||||
import org.jruby.puma.Http11;
|
||||
import org.jruby.puma.IOBuffer;
|
||||
import org.jruby.puma.MiniSSL;
|
||||
|
||||
public class PumaHttp11Service implements BasicLibraryService {
|
||||
public boolean basicLoad(final Ruby runtime) throws IOException {
|
||||
Http11.createHttp11(runtime);
|
||||
IOBuffer.createIOBuffer(runtime);
|
||||
MiniSSL.createMiniSSL(runtime);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|||
VALUE obj;
|
||||
SSL_CTX* ctx;
|
||||
SSL* ssl;
|
||||
int ssl_options;
|
||||
|
||||
ms_conn* conn = engine_alloc(self, &obj);
|
||||
|
||||
|
@ -164,6 +165,10 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|||
ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
|
||||
VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
|
||||
|
||||
ID sym_no_tlsv1 = rb_intern("no_tlsv1");
|
||||
VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
|
||||
|
||||
|
||||
ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
conn->ctx = ctx;
|
||||
|
||||
|
@ -175,7 +180,12 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|||
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
|
||||
ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
||||
|
||||
if(RTEST(no_tlsv1)) {
|
||||
ssl_options |= SSL_OP_NO_TLSv1;
|
||||
}
|
||||
SSL_CTX_set_options(ctx, ssl_options);
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
|
||||
if (!NIL_P(ssl_cipher_filter)) {
|
||||
|
@ -189,12 +199,18 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|||
DH *dh = get_dh1024();
|
||||
SSL_CTX_set_tmp_dh(ctx, dh);
|
||||
|
||||
#ifndef OPENSSL_NO_ECDH
|
||||
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no
|
||||
// longer needed.
|
||||
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (ecdh) {
|
||||
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
||||
// Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
|
||||
// negotiation.
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
#endif
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
|
@ -217,7 +233,7 @@ VALUE engine_init_client(VALUE klass) {
|
|||
VALUE obj;
|
||||
ms_conn* conn = engine_alloc(klass, &obj);
|
||||
|
||||
conn->ctx = SSL_CTX_new(DTLSv1_method());
|
||||
conn->ctx = SSL_CTX_new(DTLS_method());
|
||||
conn->ssl = SSL_new(conn->ctx);
|
||||
SSL_set_app_data(conn->ssl, NULL);
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
||||
|
@ -433,6 +449,18 @@ void Init_mini_ssl(VALUE puma) {
|
|||
mod = rb_define_module_under(puma, "MiniSSL");
|
||||
eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
||||
|
||||
// OpenSSL Build / Runtime/Load versions
|
||||
|
||||
/* Version of OpenSSL that Puma was compiled with */
|
||||
rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
|
||||
|
||||
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
|
||||
/* Version of OpenSSL that Puma loaded with */
|
||||
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
|
||||
#else
|
||||
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
|
||||
#endif
|
||||
|
||||
rb_define_singleton_method(mod, "check", noop, 0);
|
||||
|
||||
eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
||||
|
|
72
ext/puma_http11/org/jruby/puma/IOBuffer.java
Normal file
72
ext/puma_http11/org/jruby/puma/IOBuffer.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
package org.jruby.puma;
|
||||
|
||||
import org.jruby.*;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.util.ByteList;
|
||||
|
||||
/**
|
||||
* @author kares
|
||||
*/
|
||||
public class IOBuffer extends RubyObject {
|
||||
|
||||
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
||||
return new IOBuffer(runtime, klass);
|
||||
}
|
||||
};
|
||||
|
||||
public static void createIOBuffer(Ruby runtime) {
|
||||
RubyModule mPuma = runtime.defineModule("Puma");
|
||||
RubyClass cIOBuffer = mPuma.defineClassUnder("IOBuffer", runtime.getObject(), ALLOCATOR);
|
||||
cIOBuffer.defineAnnotatedMethods(IOBuffer.class);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_SIZE = 4096;
|
||||
|
||||
final ByteList buffer = new ByteList(DEFAULT_SIZE);
|
||||
|
||||
IOBuffer(Ruby runtime, RubyClass klass) {
|
||||
super(runtime, klass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public RubyInteger used(ThreadContext context) {
|
||||
return context.runtime.newFixnum(buffer.getRealSize());
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public RubyInteger capacity(ThreadContext context) {
|
||||
return context.runtime.newFixnum(buffer.unsafeBytes().length);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject reset() {
|
||||
buffer.setRealSize(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = { "to_s", "to_str" })
|
||||
public RubyString to_s(ThreadContext context) {
|
||||
return RubyString.newStringShared(context.runtime, buffer.unsafeBytes(), 0, buffer.getRealSize());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "<<")
|
||||
public IRubyObject add(IRubyObject str) {
|
||||
addImpl(str.convertToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod(rest = true)
|
||||
public IRubyObject append(IRubyObject[] strs) {
|
||||
for (IRubyObject str : strs) addImpl(str.convertToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addImpl(RubyString str) {
|
||||
buffer.append(str.getByteList());
|
||||
}
|
||||
|
||||
}
|
|
@ -158,7 +158,13 @@ public class MiniSSL extends RubyObject {
|
|||
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
engine = sslCtx.createSSLEngine();
|
||||
|
||||
String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
|
||||
String[] protocols;
|
||||
if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
|
||||
protocols = new String[] { "TLSv1.1", "TLSv1.2" };
|
||||
} else {
|
||||
protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
|
||||
}
|
||||
|
||||
engine.setEnabledProtocols(protocols);
|
||||
engine.setUseClientMode(false);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
require 'socket'
|
||||
|
||||
|
@ -90,19 +92,19 @@ module Puma
|
|||
case uri.scheme
|
||||
when "tcp"
|
||||
if fd = @inherited_fds.delete(str)
|
||||
logger.log "* Inherited #{str}"
|
||||
io = inherit_tcp_listener uri.host, uri.port, fd
|
||||
logger.log "* Inherited #{str}"
|
||||
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
||||
logger.log "* Activated #{str}"
|
||||
io = inherit_tcp_listener uri.host, uri.port, sock
|
||||
logger.log "* Activated #{str}"
|
||||
else
|
||||
params = Util.parse_query uri.query
|
||||
|
||||
opt = params.key?('low_latency')
|
||||
bak = params.fetch('backlog', 1024).to_i
|
||||
|
||||
logger.log "* Listening on #{str}"
|
||||
io = add_tcp_listener uri.host, uri.port, opt, bak
|
||||
logger.log "* Listening on #{str}"
|
||||
end
|
||||
|
||||
@listeners << [str, io] if io
|
||||
|
@ -110,14 +112,12 @@ module Puma
|
|||
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
||||
|
||||
if fd = @inherited_fds.delete(str)
|
||||
logger.log "* Inherited #{str}"
|
||||
io = inherit_unix_listener path, fd
|
||||
logger.log "* Inherited #{str}"
|
||||
elsif sock = @activated_sockets.delete([ :unix, path ])
|
||||
logger.log "* Activated #{str}"
|
||||
io = inherit_unix_listener path, sock
|
||||
logger.log "* Activated #{str}"
|
||||
else
|
||||
logger.log "* Listening on #{str}"
|
||||
|
||||
umask = nil
|
||||
mode = nil
|
||||
backlog = 1024
|
||||
|
@ -139,6 +139,7 @@ module Puma
|
|||
end
|
||||
|
||||
io = add_unix_listener path, umask, mode, backlog
|
||||
logger.log "* Listening on #{str}"
|
||||
end
|
||||
|
||||
@listeners << [str, io]
|
||||
|
@ -186,6 +187,8 @@ module Puma
|
|||
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
||||
end
|
||||
|
||||
ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
|
||||
|
||||
if params['verify_mode']
|
||||
ctx.verify_mode = case params['verify_mode']
|
||||
when "peer"
|
||||
|
@ -204,11 +207,11 @@ module Puma
|
|||
logger.log "* Inherited #{str}"
|
||||
io = inherit_ssl_listener fd, ctx
|
||||
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
||||
logger.log "* Activated #{str}"
|
||||
io = inherit_ssl_listener sock, ctx
|
||||
logger.log "* Activated #{str}"
|
||||
else
|
||||
logger.log "* Listening on #{str}"
|
||||
io = add_ssl_listener uri.host, uri.port, ctx
|
||||
logger.log "* Listening on #{str}"
|
||||
end
|
||||
|
||||
@listeners << [str, io] if io
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
require 'uri'
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IO
|
||||
# We need to use this for a jruby work around on both 1.8 and 1.9.
|
||||
# So this either creates the constant (on 1.8), or harmlessly
|
||||
|
@ -145,8 +147,11 @@ module Puma
|
|||
def decode_chunk(chunk)
|
||||
if @partial_part_left > 0
|
||||
if @partial_part_left <= chunk.size
|
||||
@body << chunk[0..(@partial_part_left-3)] # skip the \r\n
|
||||
if @partial_part_left > 2
|
||||
@body << chunk[0..(@partial_part_left-3)] # skip the \r\n
|
||||
end
|
||||
chunk = chunk[@partial_part_left..-1]
|
||||
@partial_part_left = 0
|
||||
else
|
||||
@body << chunk
|
||||
@partial_part_left -= chunk.size
|
||||
|
@ -168,9 +173,9 @@ module Puma
|
|||
if len == 0
|
||||
@body.rewind
|
||||
rest = io.read
|
||||
rest = rest[2..-1] if rest.start_with?("\r\n")
|
||||
@buffer = rest.empty? ? nil : rest
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -208,7 +213,7 @@ module Puma
|
|||
while true
|
||||
begin
|
||||
chunk = @io.read_nonblock(4096)
|
||||
rescue Errno::EAGAIN
|
||||
rescue IO::WaitReadable
|
||||
return false
|
||||
rescue SystemCallError, IOError
|
||||
raise ConnectionError, "Connection error detected during read"
|
||||
|
@ -218,8 +223,7 @@ module Puma
|
|||
unless chunk
|
||||
@body.close
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
@ -228,6 +232,8 @@ module Puma
|
|||
end
|
||||
|
||||
def setup_body
|
||||
@body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
||||
|
||||
if @env[HTTP_EXPECT] == CONTINUE
|
||||
# TODO allow a hook here to check the headers before
|
||||
# going forward
|
||||
|
@ -252,8 +258,7 @@ module Puma
|
|||
unless cl
|
||||
@buffer = body.empty? ? nil : body
|
||||
@body = EmptyBody
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -262,8 +267,7 @@ module Puma
|
|||
if remain <= 0
|
||||
@body = StringIO.new(body)
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -298,8 +302,7 @@ module Puma
|
|||
# No data means a closed socket
|
||||
unless data
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
@ -335,8 +338,7 @@ module Puma
|
|||
# No data means a closed socket
|
||||
unless data
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
@ -413,8 +415,7 @@ module Puma
|
|||
unless chunk
|
||||
@body.close
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
@ -423,8 +424,7 @@ module Puma
|
|||
if remain <= 0
|
||||
@body.rewind
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -433,6 +433,14 @@ module Puma
|
|||
false
|
||||
end
|
||||
|
||||
def set_ready
|
||||
if @body_read_start
|
||||
@env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - @body_read_start
|
||||
end
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
end
|
||||
|
||||
def write_400
|
||||
begin
|
||||
@io << ERROR_400_RESPONSE
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/runner'
|
||||
require 'puma/util'
|
||||
require 'puma/plugin'
|
||||
|
@ -292,7 +294,9 @@ module Puma
|
|||
begin
|
||||
b = server.backlog || 0
|
||||
r = server.running || 0
|
||||
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
|
||||
t = server.pool_capacity || 0
|
||||
m = server.max_threads || 0
|
||||
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
||||
io << payload
|
||||
rescue IOError
|
||||
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
# Rack::CommonLogger forwards every request to the given +app+, and
|
||||
# logs a line in the
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/rack/builder'
|
||||
require 'puma/plugin'
|
||||
require 'puma/const'
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
class UnsupportedOption < RuntimeError
|
||||
end
|
||||
|
@ -98,8 +100,8 @@ module Puma
|
|||
# too taxing on performance.
|
||||
module Const
|
||||
|
||||
PUMA_VERSION = VERSION = "3.11.4".freeze
|
||||
CODE_NAME = "Love Song".freeze
|
||||
PUMA_VERSION = VERSION = "3.12.0".freeze
|
||||
CODE_NAME = "Llamas in Pajamas".freeze
|
||||
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
||||
|
||||
FAST_TRACK_KA_TIMEOUT = 0.2
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
require 'puma/state_file'
|
||||
require 'puma/const'
|
||||
require 'puma/detect'
|
||||
require 'puma/configuration'
|
||||
require_relative 'state_file'
|
||||
require_relative 'const'
|
||||
require_relative 'detect'
|
||||
require_relative 'configuration'
|
||||
require 'uri'
|
||||
require 'socket'
|
||||
|
||||
|
@ -129,7 +131,7 @@ module Puma
|
|||
uri = URI.parse @control_url
|
||||
|
||||
# create server object by scheme
|
||||
@server = case uri.scheme
|
||||
server = case uri.scheme
|
||||
when "tcp"
|
||||
TCPSocket.new uri.host, uri.port
|
||||
when "unix"
|
||||
|
@ -147,9 +149,9 @@ module Puma
|
|||
url = url + "?token=#{@control_auth_token}"
|
||||
end
|
||||
|
||||
@server << "GET #{url} HTTP/1.0\r\n\r\n"
|
||||
server << "GET #{url} HTTP/1.0\r\n\r\n"
|
||||
|
||||
unless data = @server.read
|
||||
unless data = server.read
|
||||
raise "Server closed connection before responding"
|
||||
end
|
||||
|
||||
|
@ -172,8 +174,8 @@ module Puma
|
|||
message "Command #{@command} sent success"
|
||||
message response.last if @command == "stats" || @command == "gc-stats"
|
||||
end
|
||||
|
||||
@server.close
|
||||
ensure
|
||||
server.close if server && !server.closed?
|
||||
end
|
||||
|
||||
def send_signal
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/launcher'
|
||||
require 'puma/configuration'
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Process
|
||||
|
||||
# This overrides the default version because it is broken if it
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
module Delegation
|
||||
def forward(what, who)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
IS_JRUBY = defined?(JRUBY_VERSION)
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
# The methods that are available for use inside the config file.
|
||||
# These same methods are used in Puma cli and the rack handler
|
||||
|
@ -55,6 +57,14 @@ module Puma
|
|||
@plugins.clear
|
||||
end
|
||||
|
||||
def set_default_host(host)
|
||||
@options[:default_host] = host
|
||||
end
|
||||
|
||||
def default_host
|
||||
@options[:default_host] || Configuration::DefaultTCPHost
|
||||
end
|
||||
|
||||
def inject(&blk)
|
||||
instance_eval(&blk)
|
||||
end
|
||||
|
@ -138,7 +148,7 @@ module Puma
|
|||
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
||||
#
|
||||
def port(port, host=nil)
|
||||
host ||= Configuration::DefaultTCPHost
|
||||
host ||= default_host
|
||||
bind "tcp://#{host}:#{port}"
|
||||
end
|
||||
|
||||
|
@ -285,12 +295,14 @@ module Puma
|
|||
|
||||
def ssl_bind(host, port, opts)
|
||||
verify = opts.fetch(:verify_mode, 'none')
|
||||
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
|
||||
|
||||
if defined?(JRUBY_VERSION)
|
||||
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
||||
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}"
|
||||
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}"
|
||||
else
|
||||
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{verify}"
|
||||
ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
|
||||
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -365,6 +377,21 @@ module Puma
|
|||
|
||||
alias_method :after_worker_boot, :after_worker_fork
|
||||
|
||||
# Code to run out-of-band when the worker is idle.
|
||||
# These hooks run immediately after a request has finished
|
||||
# processing and there are no busy threads on the worker.
|
||||
# The worker doesn't accept new requests until this code finishes.
|
||||
#
|
||||
# This hook is useful for running out-of-band garbage collection
|
||||
# or scheduling asynchronous tasks to execute after a response.
|
||||
#
|
||||
# This can be called multiple times to add hooks.
|
||||
#
|
||||
def out_of_band(&block)
|
||||
@options[:out_of_band] ||= []
|
||||
@options[:out_of_band] << block
|
||||
end
|
||||
|
||||
# The directory to operate out of.
|
||||
def directory(dir)
|
||||
@options[:directory] = dir.to_s
|
||||
|
@ -424,6 +451,13 @@ module Puma
|
|||
# that have not checked in within the given +timeout+.
|
||||
# This mitigates hung processes. Default value is 60 seconds.
|
||||
def worker_timeout(timeout)
|
||||
timeout = Integer(timeout)
|
||||
min = Cluster::WORKER_CHECK_INTERVAL
|
||||
|
||||
if timeout <= min
|
||||
raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
|
||||
end
|
||||
|
||||
@options[:worker_timeout] = Integer(timeout)
|
||||
end
|
||||
|
||||
|
@ -493,7 +527,7 @@ module Puma
|
|||
when Hash
|
||||
if hdr = val[:header]
|
||||
@options[:remote_address] = :header
|
||||
@options[:remote_address_header] = "HTTP_" + hdr.upcase.gsub("-", "_")
|
||||
@options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
|
||||
else
|
||||
raise "Invalid value for set_remote_address - #{val.inspect}"
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/const'
|
||||
require "puma/null_io"
|
||||
require 'stringio'
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
require 'puma/detect'
|
||||
# frozen_string_literal: true
|
||||
|
||||
if Puma.jruby?
|
||||
require 'puma/java_io_buffer'
|
||||
else
|
||||
require 'puma/puma_http11'
|
||||
end
|
||||
require 'puma/detect'
|
||||
require 'puma/puma_http11'
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
require 'java'
|
||||
|
||||
# Conservative native JRuby/Java implementation of IOBuffer
|
||||
# backed by a ByteArrayOutputStream and conversion between
|
||||
# Ruby String and Java bytes
|
||||
module Puma
|
||||
class JavaIOBuffer < java.io.ByteArrayOutputStream
|
||||
field_reader :buf
|
||||
end
|
||||
|
||||
class IOBuffer
|
||||
BUF_DEFAULT_SIZE = 4096
|
||||
|
||||
def initialize
|
||||
@buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
|
||||
end
|
||||
|
||||
def reset
|
||||
@buf.reset
|
||||
end
|
||||
|
||||
def <<(str)
|
||||
bytes = str.to_java_bytes
|
||||
@buf.write(bytes, 0, bytes.length)
|
||||
end
|
||||
|
||||
def append(*strs)
|
||||
strs.each { |s| self << s; }
|
||||
end
|
||||
|
||||
def to_s
|
||||
String.from_java_bytes @buf.to_byte_array
|
||||
end
|
||||
|
||||
alias_method :to_str, :to_s
|
||||
|
||||
def used
|
||||
@buf.size
|
||||
end
|
||||
|
||||
def capacity
|
||||
@buf.buf.length
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'ffi'
|
||||
|
||||
module Puma
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/events'
|
||||
require 'puma/detect'
|
||||
|
||||
|
@ -63,8 +65,8 @@ module Puma
|
|||
|
||||
generate_restart_data
|
||||
|
||||
if clustered? && (Puma.jruby? || Puma.windows?)
|
||||
unsupported 'worker mode not supported on JRuby or Windows'
|
||||
if clustered? && !Process.respond_to?(:fork)
|
||||
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
||||
end
|
||||
|
||||
if @options[:daemon] && Puma.windows?
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
begin
|
||||
require 'io/wait'
|
||||
rescue LoadError
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
module Puma
|
||||
|
@ -175,6 +177,11 @@ module Puma
|
|||
|
||||
class Context
|
||||
attr_accessor :verify_mode
|
||||
attr_reader :no_tlsv1
|
||||
|
||||
def initialize
|
||||
@no_tlsv1 = false
|
||||
end
|
||||
|
||||
if defined?(JRUBY_VERSION)
|
||||
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
||||
|
@ -213,11 +220,18 @@ module Puma
|
|||
@ca = ca
|
||||
end
|
||||
|
||||
|
||||
def check
|
||||
raise "Key not configured" unless @key
|
||||
raise "Cert not configured" unless @cert
|
||||
end
|
||||
end
|
||||
|
||||
def no_tlsv1=(tlsv1)
|
||||
raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1)
|
||||
@no_tlsv1 = tlsv1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
VERIFY_NONE = 0
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
# Provides an IO-like object that always appears to contain no data.
|
||||
# Used as the value for rack.input when the request has no body.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
class UnknownPlugin < RuntimeError; end
|
||||
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# :stopdoc:
|
||||
|
||||
require 'uri/common'
|
||||
|
||||
# Issue:
|
||||
# http://bugs.ruby-lang.org/issues/5925
|
||||
#
|
||||
# Relevant commit:
|
||||
# https://github.com/ruby/ruby/commit/edb7cdf1eabaff78dfa5ffedfbc2e91b29fa9ca1
|
||||
|
||||
|
||||
module URI
|
||||
begin
|
||||
256.times do |i|
|
||||
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
||||
end
|
||||
TBLENCWWWCOMP_[' '] = '+'
|
||||
TBLENCWWWCOMP_.freeze
|
||||
|
||||
256.times do |i|
|
||||
h, l = i>>4, i&15
|
||||
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
||||
TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
|
||||
TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
|
||||
TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
|
||||
end
|
||||
TBLDECWWWCOMP_['+'] = ' '
|
||||
TBLDECWWWCOMP_.freeze
|
||||
rescue Exception
|
||||
end
|
||||
end
|
||||
|
||||
# :startdoc:
|
|
@ -110,7 +110,8 @@ module Puma::Rack
|
|||
|
||||
has_options = false
|
||||
server.valid_options.each do |name, description|
|
||||
next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
|
||||
next if name.to_s =~ /^(Host|Port)[^a-zA-Z]/ # ignore handler's host and port options, we do our own.
|
||||
|
||||
info << " -O %-21s %s" % [name, description]
|
||||
has_options = true
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/util'
|
||||
require 'puma/minissl'
|
||||
|
||||
|
@ -21,7 +23,7 @@ module Puma
|
|||
#
|
||||
# When the request is written to by the client then the `IO.select` will "wake up" and
|
||||
# return the references to any objects that caused it to "wake". The reactor
|
||||
# then loops through each of these request objects, and sees if they're complete. If they
|
||||
# then loops through each of these request objects, and sees if they're complete. If they
|
||||
# have a full header and body then the reactor passes the request to a thread pool.
|
||||
# Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
|
||||
#
|
||||
|
@ -36,7 +38,7 @@ module Puma
|
|||
# Creates an instance of Puma::Reactor
|
||||
#
|
||||
# The `server` argument is an instance of `Puma::Server`
|
||||
# this is used to write a response for "low level errors"
|
||||
# that is used to write a response for "low level errors"
|
||||
# when there is an exception inside of the reactor.
|
||||
#
|
||||
# The `app_pool` is an instance of `Puma::ThreadPool`.
|
||||
|
@ -92,7 +94,7 @@ module Puma
|
|||
# `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
|
||||
#
|
||||
# Each element in the first entry is iterated over. The `Puma::Client` object is not
|
||||
# the `@ready` pipe, so the reactor checks to see if it has the fully header and body with
|
||||
# the `@ready` pipe, so the reactor checks to see if it has the full header and body with
|
||||
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
||||
# then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
|
||||
# can pick up the request and begin to execute application logic. This is done
|
||||
|
@ -108,9 +110,9 @@ module Puma
|
|||
# In addition to being woken via a write to one of the sockets the `IO.select` will
|
||||
# periodically "time out" of the sleep. One of the functions of this is to check for
|
||||
# any requests that have "timed out". At the end of the loop it's checked to see if
|
||||
# the first element in the `@timeout` array has exceed it's allowed time. If so,
|
||||
# the client object is removed from the timeout aray, a 408 response is written.
|
||||
# Then it's connection is closed, and the object is removed from the `sockets` array
|
||||
# the first element in the `@timeout` array has exceed its allowed time. If so,
|
||||
# the client object is removed from the timeout array, a 408 response is written.
|
||||
# Then its connection is closed, and the object is removed from the `sockets` array
|
||||
# that watches for new data.
|
||||
#
|
||||
# This behavior loops until all the objects that have timed out have been removed.
|
||||
|
@ -292,7 +294,7 @@ module Puma
|
|||
#
|
||||
# The main body of the reactor loop is in `run_internal` and it
|
||||
# will sleep on `IO.select`. When a new connection is added to the
|
||||
# reactor it cannot be added directly to the `sockets` aray, because
|
||||
# reactor it cannot be added directly to the `sockets` array, because
|
||||
# the `IO.select` will not be watching for it yet.
|
||||
#
|
||||
# Instead what needs to happen is that `IO.select` needs to be woken up,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/server'
|
||||
require 'puma/const'
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'stringio'
|
||||
|
||||
require 'puma/thread_pool'
|
||||
|
@ -14,10 +16,6 @@ require 'puma/util'
|
|||
|
||||
require 'puma/puma_http11'
|
||||
|
||||
unless Puma.const_defined? "IOBuffer"
|
||||
require 'puma/io_buffer'
|
||||
end
|
||||
|
||||
require 'socket'
|
||||
|
||||
module Puma
|
||||
|
@ -77,7 +75,6 @@ module Puma
|
|||
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
||||
|
||||
@binder = Binder.new(events)
|
||||
@own_binder = true
|
||||
|
||||
@leak_stack_on_error = true
|
||||
|
||||
|
@ -100,7 +97,6 @@ module Puma
|
|||
|
||||
def inherit_binder(bind)
|
||||
@binder = bind
|
||||
@own_binder = false
|
||||
end
|
||||
|
||||
def tcp_mode!
|
||||
|
@ -168,6 +164,18 @@ module Puma
|
|||
@thread_pool and @thread_pool.spawned
|
||||
end
|
||||
|
||||
|
||||
# This number represents the number of requests that
|
||||
# the server is capable of taking right now.
|
||||
#
|
||||
# For example if the number is 5 then it means
|
||||
# there are 5 threads sitting idle ready to take
|
||||
# a request. If one request comes in, then the
|
||||
# value would be 4 until it finishes processing.
|
||||
def pool_capacity
|
||||
@thread_pool and @thread_pool.pool_capacity
|
||||
end
|
||||
|
||||
# Lopez Mode == raw tcp apps
|
||||
|
||||
def run_lopez_mode(background=true)
|
||||
|
@ -257,10 +265,10 @@ module Puma
|
|||
end
|
||||
|
||||
# Prevent can't modify frozen IOError (RuntimeError)
|
||||
@notify.close rescue nil
|
||||
|
||||
if @status != :restart and @own_binder
|
||||
@binder.close
|
||||
begin
|
||||
@notify.close
|
||||
rescue IOError
|
||||
# no biggy
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -385,7 +393,10 @@ module Puma
|
|||
end
|
||||
|
||||
pool << client
|
||||
pool.wait_until_not_full
|
||||
busy_threads = pool.wait_until_not_full
|
||||
if busy_threads == 0
|
||||
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
||||
end
|
||||
end
|
||||
rescue SystemCallError
|
||||
# nothing
|
||||
|
@ -417,10 +428,6 @@ module Puma
|
|||
ensure
|
||||
@check.close
|
||||
@notify.close
|
||||
|
||||
if @status != :restart and @own_binder
|
||||
@binder.close
|
||||
end
|
||||
end
|
||||
|
||||
@events.fire :state, :done
|
||||
|
@ -929,6 +936,10 @@ module Puma
|
|||
@events.debug "Drained #{count} additional connections."
|
||||
end
|
||||
|
||||
if @status != :restart
|
||||
@binder.close
|
||||
end
|
||||
|
||||
if @thread_pool
|
||||
if timeout = @options[:force_shutdown_after]
|
||||
@thread_pool.shutdown timeout.to_i
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/runner'
|
||||
require 'puma/detect'
|
||||
require 'puma/plugin'
|
||||
|
@ -14,7 +16,9 @@ module Puma
|
|||
def stats
|
||||
b = @server.backlog || 0
|
||||
r = @server.running || 0
|
||||
%Q!{ "backlog": #{b}, "running": #{r} }!
|
||||
t = @server.pool_capacity || 0
|
||||
m = @server.max_threads || 0
|
||||
%Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
|
||||
end
|
||||
|
||||
def restart
|
||||
|
@ -22,7 +26,7 @@ module Puma
|
|||
end
|
||||
|
||||
def stop
|
||||
@server.stop false
|
||||
@server.stop(false) if @server
|
||||
end
|
||||
|
||||
def halt
|
||||
|
@ -32,7 +36,7 @@ module Puma
|
|||
def stop_blocked
|
||||
log "- Gracefully stopping, waiting for requests to finish"
|
||||
@control.stop(true) if @control
|
||||
@server.stop(true)
|
||||
@server.stop(true) if @server
|
||||
end
|
||||
|
||||
def jruby_daemon?
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module Puma
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Puma
|
||||
class TCPLogger
|
||||
def initialize(logger, app, quiet=false)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'thread'
|
||||
|
||||
module Puma
|
||||
|
@ -58,7 +60,7 @@ module Puma
|
|||
@clean_thread_locals = false
|
||||
end
|
||||
|
||||
attr_reader :spawned, :trim_requested
|
||||
attr_reader :spawned, :trim_requested, :waiting
|
||||
attr_accessor :clean_thread_locals
|
||||
|
||||
def self.clean_thread_locals
|
||||
|
@ -73,6 +75,10 @@ module Puma
|
|||
@mutex.synchronize { @todo.size }
|
||||
end
|
||||
|
||||
def pool_capacity
|
||||
waiting + (@max - spawned)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
#
|
||||
# Must be called with @mutex held!
|
||||
|
@ -188,6 +194,9 @@ module Puma
|
|||
# method would not block and another request would be added into the reactor
|
||||
# by the server. This would continue until a fully bufferend request
|
||||
# makes it through the reactor and can then be processed by the thread pool.
|
||||
#
|
||||
# Returns the current number of busy threads, or +nil+ if shutting down.
|
||||
#
|
||||
def wait_until_not_full
|
||||
@mutex.synchronize do
|
||||
while true
|
||||
|
@ -197,7 +206,8 @@ module Puma
|
|||
# is work queued that cannot be handled by waiting
|
||||
# threads, then accept more work until we would
|
||||
# spin up the max number of threads.
|
||||
return if @todo.size - @waiting < @max - @spawned
|
||||
busy_threads = @spawned - @waiting + @todo.size
|
||||
return busy_threads if @max > busy_threads
|
||||
|
||||
@not_full.wait @mutex
|
||||
end
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
||||
# frozen_string_literal: true
|
||||
|
||||
if major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
|
||||
require 'puma/rack/backports/uri/common_193'
|
||||
else
|
||||
require 'uri/common'
|
||||
end
|
||||
require 'uri/common'
|
||||
|
||||
module Puma
|
||||
module Util
|
||||
|
|
|
@ -49,6 +49,9 @@ module Rack
|
|||
self.set_host_port_to_config(host, port, user_config)
|
||||
end
|
||||
|
||||
if default_options[:Host]
|
||||
file_config.set_default_host(default_options[:Host])
|
||||
end
|
||||
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
||||
|
||||
user_config.app app
|
||||
|
|
|
@ -10,6 +10,8 @@ end
|
|||
|
||||
begin
|
||||
require "bundler/setup"
|
||||
# bundler/setup may not load bundler
|
||||
require "bundler" unless Bundler.const_defined?(:ORIGINAL_ENV)
|
||||
rescue LoadError
|
||||
warn "Failed to load bundler ... this should only happen during package building"
|
||||
end
|
||||
|
@ -70,12 +72,31 @@ if ENV['CI']
|
|||
end
|
||||
|
||||
module SkipTestsBasedOnRubyEngine
|
||||
def skip_on_jruby
|
||||
skip "Skipped on JRuby" if Puma.jruby?
|
||||
# called with one or more params, like skip_on :jruby, :windows
|
||||
# optional suffix kwarg is appended to the skip message
|
||||
# optional suffix bt should generally not used
|
||||
def skip_on(*engs, suffix: '', bt: caller)
|
||||
skip_msg = false
|
||||
engs.each do |eng|
|
||||
skip_msg = case eng
|
||||
when :jruby then "Skipped on JRuby#{suffix}" if Puma.jruby?
|
||||
when :windows then "Skipped on Windows#{suffix}" if Puma.windows?
|
||||
when :appveyor then "Skipped on Appveyor#{suffix}" if ENV["APPVEYOR"]
|
||||
when :ci then "Skipped on ENV['CI']#{suffix}" if ENV["CI"]
|
||||
else false
|
||||
end
|
||||
skip skip_msg, bt if skip_msg
|
||||
end
|
||||
end
|
||||
|
||||
def skip_on_appveyor
|
||||
skip "Skipped on Appveyor" if ENV["APPVEYOR"]
|
||||
# called with only one param
|
||||
def skip_unless(eng, bt: caller)
|
||||
skip_msg = case eng
|
||||
when :jruby then "Skip unless JRuby" unless Puma.jruby?
|
||||
when :windows then "Skip unless Windows" unless Puma.windows?
|
||||
else false
|
||||
end
|
||||
skip skip_msg, bt if skip_msg
|
||||
end
|
||||
end
|
||||
|
||||
|
|
4
test/rackup/10seconds.ru
Normal file
4
test/rackup/10seconds.ru
Normal file
|
@ -0,0 +1,4 @@
|
|||
run lambda { |env|
|
||||
sleep 10
|
||||
[200, {}, ["Hello World"]]
|
||||
}
|
|
@ -10,7 +10,7 @@ class TestBinder < Minitest::Test
|
|||
end
|
||||
|
||||
def test_localhost_addresses_dont_alter_listeners_for_tcp_addresses
|
||||
skip_on_jruby
|
||||
skip_on :jruby
|
||||
|
||||
@binder.parse(["tcp://localhost:10001"], @events)
|
||||
|
||||
|
@ -18,8 +18,7 @@ class TestBinder < Minitest::Test
|
|||
end
|
||||
|
||||
def test_localhost_addresses_dont_alter_listeners_for_ssl_addresses
|
||||
skip_on_appveyor
|
||||
skip_on_jruby
|
||||
skip_on :jruby
|
||||
|
||||
key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
|
@ -30,8 +29,7 @@ class TestBinder < Minitest::Test
|
|||
end
|
||||
|
||||
def test_binder_parses_ssl_cipher_filter
|
||||
skip_on_appveyor
|
||||
skip_on_jruby
|
||||
skip_on :jruby
|
||||
|
||||
key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
|
@ -45,15 +43,54 @@ class TestBinder < Minitest::Test
|
|||
end
|
||||
|
||||
def test_binder_parses_jruby_ssl_options
|
||||
skip unless Puma.jruby?
|
||||
skip_unless :jruby
|
||||
|
||||
keystore = File.expand_path "../../examples/puma/keystore.jks", __FILE__
|
||||
ssl_cipher_list = "TLS_DHE_RSA_WITH_DES_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"
|
||||
@binder.parse(["ssl://0.0.0.0?keystore=#{keystore}&ssl_cipher_list=#{ssl_cipher_list}"], @events)
|
||||
@binder.parse(["ssl://0.0.0.0:8080?keystore=#{keystore}&keystore-pass=&ssl_cipher_list=#{ssl_cipher_list}"], @events)
|
||||
|
||||
ssl= @binder.instance_variable_get(:@ios)[0]
|
||||
ctx = ssl.instance_variable_get(:@ctx)
|
||||
assert_equal(keystore, ctx.keystore)
|
||||
assert_equal(ssl_cipher_list, ctx.ssl_cipher_list)
|
||||
end
|
||||
|
||||
def test_binder_parses_tlsv1_disabled
|
||||
skip_on :jruby
|
||||
|
||||
key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
|
||||
@binder.parse(["ssl://0.0.0.0?key=#{key}&cert=#{cert}&no_tlsv1=true"], @events)
|
||||
|
||||
ssl = @binder.instance_variable_get(:@ios).first
|
||||
ctx = ssl.instance_variable_get(:@ctx)
|
||||
assert_equal(true, ctx.no_tlsv1)
|
||||
end
|
||||
|
||||
def test_binder_parses_tlsv1_enabled
|
||||
skip_on :jruby
|
||||
|
||||
key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
|
||||
@binder.parse(["ssl://0.0.0.0?key=#{key}&cert=#{cert}&no_tlsv1=false"], @events)
|
||||
|
||||
ssl = @binder.instance_variable_get(:@ios).first
|
||||
ctx = ssl.instance_variable_get(:@ctx)
|
||||
refute(ctx.no_tlsv1)
|
||||
end
|
||||
|
||||
def test_binder_parses_tlsv1_unspecified_defaults_to_enabled
|
||||
skip_on :jruby
|
||||
|
||||
key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
|
||||
@binder.parse(["ssl://0.0.0.0?key=#{key}&cert=#{cert}"], @events)
|
||||
|
||||
ssl = @binder.instance_variable_get(:@ios).first
|
||||
ctx = ssl.instance_variable_get(:@ctx)
|
||||
refute(ctx.no_tlsv1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,15 +56,15 @@ class TestCLI < Minitest::Test
|
|||
s = TCPSocket.new "127.0.0.1", 9877
|
||||
s << "GET /stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
assert_equal '{ "backlog": 0, "running": 0 }', body.split(/\r?\n/).last
|
||||
assert_equal '{ "backlog": 0, "running": 0 }', Puma.stats
|
||||
assert_equal '{ "backlog": 0, "running": 0, "pool_capacity": 16, "max_threads": 16 }', body.split(/\r?\n/).last
|
||||
assert_equal '{ "backlog": 0, "running": 0, "pool_capacity": 16, "max_threads": 16 }', Puma.stats
|
||||
|
||||
cli.launcher.stop
|
||||
t.join
|
||||
end
|
||||
|
||||
unless Puma.jruby? || Puma.windows?
|
||||
def test_control_clustered
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
@ -95,13 +95,14 @@ class TestCLI < Minitest::Test
|
|||
s = UNIXSocket.new @tmp_path
|
||||
s << "GET /stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
assert_match(/\{ "workers": 2, "phase": 0, "booted_workers": 2, "old_workers": 0, "worker_status": \[\{ "pid": \d+, "index": 0, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2 \} \},\{ "pid": \d+, "index": 1, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2 \} \}\] \}/, body.split("\r\n").last)
|
||||
assert_match(/\{ "workers": 2, "phase": 0, "booted_workers": 2, "old_workers": 0, "worker_status": \[\{ "pid": \d+, "index": 0, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2, "pool_capacity":2, "max_threads": 2 \} \},\{ "pid": \d+, "index": 1, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2, "pool_capacity":2, "max_threads": 2 \} \}\] \}/, body.split("\r\n").last)
|
||||
|
||||
cli.launcher.stop
|
||||
t.join
|
||||
end
|
||||
|
||||
def test_control
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
@ -118,13 +119,14 @@ class TestCLI < Minitest::Test
|
|||
s << "GET /stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
|
||||
assert_equal '{ "backlog": 0, "running": 0 }', body.split("\r\n").last
|
||||
assert_equal '{ "backlog": 0, "running": 0, "pool_capacity": 16, "max_threads": 16 }', body.split("\r\n").last
|
||||
|
||||
cli.launcher.stop
|
||||
t.join
|
||||
end
|
||||
|
||||
def test_control_stop
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
@ -147,6 +149,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_control_gc_stats
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
@ -201,6 +204,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_tmp_control
|
||||
skip_on :jruby
|
||||
url = "tcp://127.0.0.1:8232"
|
||||
cli = Puma::CLI.new ["--state", @tmp_path, "--control", "auto"]
|
||||
cli.launcher.write_state
|
||||
|
@ -217,6 +221,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_state_file_callback_filtering
|
||||
skip_on :jruby, :windows, suffix: " - worker mode not supported"
|
||||
cli = Puma::CLI.new [ "--config", "test/config/state_file_testing_config.rb",
|
||||
"--state", @tmp_path ]
|
||||
cli.launcher.write_state
|
||||
|
@ -227,8 +232,6 @@ class TestCLI < Minitest::Test
|
|||
assert_empty keys_not_stripped
|
||||
end
|
||||
|
||||
end # JRUBY or Windows
|
||||
|
||||
def test_state
|
||||
url = "tcp://127.0.0.1:8232"
|
||||
cli = Puma::CLI.new ["--state", @tmp_path, "--control", url]
|
||||
|
|
|
@ -43,6 +43,41 @@ class TestConfigFile < Minitest::Test
|
|||
end
|
||||
end
|
||||
|
||||
def test_ssl_bind
|
||||
skip_on :jruby
|
||||
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.ssl_bind "0.0.0.0", "9292", {
|
||||
cert: "/path/to/cert",
|
||||
key: "/path/to/key",
|
||||
verify_mode: "the_verify_mode",
|
||||
}
|
||||
end
|
||||
|
||||
conf.load
|
||||
|
||||
ssl_binding = "ssl://0.0.0.0:9292?cert=/path/to/cert&key=/path/to/key&verify_mode=the_verify_mode"
|
||||
assert_equal [ssl_binding], conf.options[:binds]
|
||||
end
|
||||
|
||||
def test_ssl_bind_with_cipher_filter
|
||||
skip_on :jruby
|
||||
|
||||
cipher_filter = "!aNULL:AES+SHA"
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.ssl_bind "0.0.0.0", "9292", {
|
||||
cert: "cert",
|
||||
key: "key",
|
||||
ssl_cipher_filter: cipher_filter,
|
||||
}
|
||||
end
|
||||
|
||||
conf.load
|
||||
|
||||
ssl_binding = conf.options[:binds].first
|
||||
assert ssl_binding.include?("&ssl_cipher_filter=#{cipher_filter}")
|
||||
end
|
||||
|
||||
def test_lowlevel_error_handler_DSL
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.load "test/config/app.rb"
|
||||
|
|
|
@ -2,6 +2,7 @@ require_relative "helper"
|
|||
|
||||
require "puma/cli"
|
||||
require "puma/control_cli"
|
||||
require "open3"
|
||||
|
||||
# These don't run on travis because they're too fragile
|
||||
|
||||
|
@ -67,7 +68,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def restart_server_and_listen(argv)
|
||||
skip_on_appveyor
|
||||
skip_on :windows
|
||||
server(argv)
|
||||
s = connect
|
||||
initial_reply = read_body(s)
|
||||
|
@ -115,7 +116,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_stop_via_pumactl
|
||||
skip if Puma.jruby? || Puma.windows?
|
||||
skip_on :jruby, :windows
|
||||
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.quiet
|
||||
|
@ -148,7 +149,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_phased_restart_via_pumactl
|
||||
skip if Puma.jruby? || Puma.windows? || ENV['CI']
|
||||
skip_on :jruby, :windows, :ci, suffix: " - UNIX sockets are not recommended"
|
||||
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.quiet
|
||||
|
@ -181,7 +182,7 @@ class TestIntegration < Minitest::Test
|
|||
until done
|
||||
@events.stdout.rewind
|
||||
log = @events.stdout.readlines.join("")
|
||||
if log.match(/- Worker \d \(pid: \d+\) booted, phase: 1/)
|
||||
if log =~ /- Worker \d \(pid: \d+\) booted, phase: 1/
|
||||
assert_match(/TERM sent/, log)
|
||||
assert_match(/- Worker \d \(pid: \d+\) booted, phase: 1/, log)
|
||||
done = true
|
||||
|
@ -195,7 +196,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_kill_unknown_via_pumactl
|
||||
skip if Puma.jruby? || Puma.windows?
|
||||
skip_on :jruby, :windows
|
||||
|
||||
# we run ls to get a 'safe' pid to pass off as puma in cli stop
|
||||
# do not want to accidently kill a valid other process
|
||||
|
@ -220,7 +221,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_restart_closes_keepalive_sockets_workers
|
||||
skip_on_jruby
|
||||
skip_on :jruby
|
||||
_, new_reply = restart_server_and_listen("-q -w 2 test/rackup/hello.ru")
|
||||
assert_equal "Hello World", new_reply
|
||||
end
|
||||
|
@ -229,7 +230,7 @@ class TestIntegration < Minitest::Test
|
|||
def test_restart_restores_environment
|
||||
# jruby has a bug where setting `nil` into the ENV or `delete` do not change the
|
||||
# next workers ENV
|
||||
skip_on_jruby
|
||||
skip_on :jruby
|
||||
|
||||
initial_reply, new_reply = restart_server_and_listen("-q test/rackup/hello-env.ru")
|
||||
|
||||
|
@ -239,7 +240,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_term_signal_exit_code_in_single_mode
|
||||
skip if Puma.jruby? || Puma.windows?
|
||||
skip_on :jruby, :windows
|
||||
|
||||
pid = start_forked_server("test/rackup/hello.ru")
|
||||
_, status = stop_forked_server(pid)
|
||||
|
@ -248,11 +249,38 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_term_signal_exit_code_in_clustered_mode
|
||||
skip if Puma.jruby? || Puma.windows?
|
||||
skip_on :jruby, :windows
|
||||
|
||||
pid = start_forked_server("-w 2 test/rackup/hello.ru")
|
||||
_, status = stop_forked_server(pid)
|
||||
|
||||
assert_equal 15, status
|
||||
end
|
||||
|
||||
def test_not_accepts_new_connections_after_term_signal
|
||||
skip_on :jruby, :windows
|
||||
|
||||
server('test/rackup/10seconds.ru')
|
||||
|
||||
_stdin, curl_stdout, _stderr, curl_wait_thread = Open3.popen3("curl 127.0.0.1:#{@tcp_port}")
|
||||
sleep 1 # ensure curl send a request
|
||||
|
||||
Process.kill(:TERM, @server.pid)
|
||||
true while @server.gets !~ /Gracefully stopping/ # wait for server to begin graceful shutdown
|
||||
|
||||
# Invoke a request which must be rejected
|
||||
_stdin, _stdout, rejected_curl_stderr, rejected_curl_wait_thread = Open3.popen3("curl 127.0.0.1:#{@tcp_port}")
|
||||
|
||||
assert nil != Process.getpgid(@server.pid) # ensure server is still running
|
||||
assert nil != Process.getpgid(rejected_curl_wait_thread[:pid]) # ensure first curl invokation still in progress
|
||||
|
||||
curl_wait_thread.join
|
||||
rejected_curl_wait_thread.join
|
||||
|
||||
assert_match /Hello World/, curl_stdout.read
|
||||
assert_match /Connection refused/, rejected_curl_stderr.read
|
||||
|
||||
Process.wait(@server.pid)
|
||||
@server = nil # prevent `#teardown` from killing already killed server
|
||||
end
|
||||
end
|
||||
|
|
|
@ -658,6 +658,83 @@ EOF
|
|||
assert_equal "hello", body
|
||||
end
|
||||
|
||||
def test_chunked_request_pause_between_cr_lf_after_size_of_second_chunk
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
body = env['rack.input'].read
|
||||
[200, {}, [""]]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
part1 = 'a' * 4200
|
||||
|
||||
chunked_body = "#{part1.size.to_s(16)}\r\n#{part1}\r\n1\r\nb\r\n0\r\n\r\n"
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "PUT /path HTTP/1.1\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n"
|
||||
|
||||
sleep 0.1
|
||||
|
||||
sock << chunked_body[0..-10]
|
||||
|
||||
sleep 0.1
|
||||
|
||||
sock << chunked_body[-9..-1]
|
||||
|
||||
data = sock.read
|
||||
|
||||
assert_equal "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", data
|
||||
assert_equal (part1 + 'b'), body
|
||||
end
|
||||
|
||||
def test_chunked_request_pause_between_closing_cr_lf
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
body = env['rack.input'].read
|
||||
[200, {}, [""]]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "PUT /path HTTP/1.1\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r"
|
||||
|
||||
sleep 1
|
||||
|
||||
sock << "\n0\r\n\r\n"
|
||||
|
||||
data = sock.read
|
||||
|
||||
assert_equal "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", data
|
||||
assert_equal 'hello', body
|
||||
end
|
||||
|
||||
def test_chunked_request_pause_before_closing_cr_lf
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
body = env['rack.input'].read
|
||||
[200, {}, [""]]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "PUT /path HTTP/1.1\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello"
|
||||
|
||||
sleep 1
|
||||
|
||||
sock << "\r\n0\r\n\r\n"
|
||||
|
||||
data = sock.read
|
||||
|
||||
assert_equal "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", data
|
||||
assert_equal 'hello', body
|
||||
end
|
||||
|
||||
def test_chunked_request_header_case
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
|
@ -677,6 +754,55 @@ EOF
|
|||
assert_equal "hello", body
|
||||
end
|
||||
|
||||
def test_chunked_keep_alive
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
body = env['rack.input'].read
|
||||
[200, {}, [""]]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "GET / HTTP/1.1\r\nConnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nh\r\n4\r\nello\r\n0\r\n\r\n"
|
||||
|
||||
h = header(sock)
|
||||
|
||||
assert_equal ["HTTP/1.1 200 OK", "Content-Length: 0"], h
|
||||
assert_equal "hello", body
|
||||
|
||||
sock.close
|
||||
end
|
||||
|
||||
def test_chunked_keep_alive_two_back_to_back
|
||||
body = nil
|
||||
@server.app = proc { |env|
|
||||
body = env['rack.input'].read
|
||||
[200, {}, [""]]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "GET / HTTP/1.1\r\nConnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nh\r\n4\r\nello\r\n0\r\n\r\n"
|
||||
|
||||
h = header(sock)
|
||||
assert_equal ["HTTP/1.1 200 OK", "Content-Length: 0"], h
|
||||
assert_equal "hello", body
|
||||
|
||||
sock << "GET / HTTP/1.1\r\nConnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\n\r\n4\r\ngood\r\n3\r\nbye\r\n0\r\n\r\n"
|
||||
sleep 0.1
|
||||
|
||||
h = header(sock)
|
||||
|
||||
assert_equal ["HTTP/1.1 200 OK", "Content-Length: 0"], h
|
||||
assert_equal "goodbye", body
|
||||
|
||||
sock.close
|
||||
end
|
||||
|
||||
def test_empty_header_values
|
||||
@server.app = proc { |env| [200, {"X-Empty-Header" => ""}, []] }
|
||||
|
||||
|
@ -691,4 +817,44 @@ EOF
|
|||
|
||||
assert_equal "HTTP/1.0 200 OK\r\nX-Empty-Header: \r\n\r\n", data
|
||||
end
|
||||
|
||||
def test_request_body_wait
|
||||
request_body_wait = nil
|
||||
@server.app = proc { |env|
|
||||
request_body_wait = env['puma.request_body_wait']
|
||||
[204, {}, []]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nh"
|
||||
sleep 1
|
||||
sock << "ello"
|
||||
|
||||
sock.gets
|
||||
|
||||
assert request_body_wait >= 1000
|
||||
end
|
||||
|
||||
def test_request_body_wait_chunked
|
||||
request_body_wait = nil
|
||||
@server.app = proc { |env|
|
||||
request_body_wait = env['puma.request_body_wait']
|
||||
[204, {}, []]
|
||||
}
|
||||
|
||||
@server.add_tcp_listener @host, @port
|
||||
@server.run
|
||||
|
||||
sock = TCPSocket.new @host, @server.connected_port
|
||||
sock << "GET / HTTP/1.1\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nh\r\n"
|
||||
sleep 1
|
||||
sock << "4\r\nello\r\n0\r\n"
|
||||
|
||||
sock.gets
|
||||
|
||||
assert request_body_wait >= 1000
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
require_relative "helper"
|
||||
require "puma/minissl"
|
||||
require "puma/puma_http11"
|
||||
|
||||
#———————————————————————————————————————————————————————————————————————————————
|
||||
# NOTE: ALL TESTS BYPASSED IF DISABLE_SSL IS TRUE
|
||||
#———————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
class SSLEventsHelper < ::Puma::Events
|
||||
attr_accessor :addr, :cert, :error
|
||||
|
@ -11,7 +17,11 @@ class SSLEventsHelper < ::Puma::Events
|
|||
end
|
||||
|
||||
DISABLE_SSL = begin
|
||||
Puma::Server.class
|
||||
Puma::MiniSSL.check
|
||||
puts "", RUBY_DESCRIPTION
|
||||
puts "Puma::MiniSSL OPENSSL_LIBRARY_VERSION: #{Puma::MiniSSL::OPENSSL_LIBRARY_VERSION}",
|
||||
" OPENSSL_VERSION: #{Puma::MiniSSL::OPENSSL_VERSION}", ""
|
||||
rescue
|
||||
true
|
||||
else
|
||||
|
@ -102,18 +112,18 @@ class TestPumaServerSSL < Minitest::Test
|
|||
end
|
||||
|
||||
def test_ssl_v3_rejection
|
||||
@http.ssl_version='SSLv3'
|
||||
@http.ssl_version= :SSLv3
|
||||
assert_raises(OpenSSL::SSL::SSLError) do
|
||||
@http.start do
|
||||
Net::HTTP::Get.new '/'
|
||||
end
|
||||
end
|
||||
unless Puma.jruby?
|
||||
assert_match(/wrong version number|no protocols available/, @events.error.message) if @events.error
|
||||
assert_match(/wrong version number|no protocols available|version too low/, @events.error.message) if @events.error
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end unless DISABLE_SSL
|
||||
|
||||
# client-side TLS authentication tests
|
||||
class TestPumaServerSSLClient < Minitest::Test
|
||||
|
@ -137,14 +147,14 @@ class TestPumaServerSSLClient < Minitest::Test
|
|||
|
||||
events = SSLEventsHelper.new STDOUT, STDERR
|
||||
server = Puma::Server.new app, events
|
||||
ssl_listener = server.add_ssl_listener host, port, ctx
|
||||
server.add_ssl_listener host, port, ctx
|
||||
server.run
|
||||
|
||||
http = Net::HTTP.new host, port
|
||||
http.use_ssl = true
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
|
||||
blk.call(http)
|
||||
yield http
|
||||
|
||||
client_error = false
|
||||
begin
|
||||
|
@ -152,7 +162,7 @@ class TestPumaServerSSLClient < Minitest::Test
|
|||
req = Net::HTTP::Get.new "/", {}
|
||||
http.request(req)
|
||||
end
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
rescue OpenSSL::SSL::SSLError, EOFError
|
||||
client_error = true
|
||||
end
|
||||
|
||||
|
@ -165,7 +175,7 @@ class TestPumaServerSSLClient < Minitest::Test
|
|||
assert_equal host, events.addr if error
|
||||
assert_equal subject, events.cert.subject.to_s if subject
|
||||
end
|
||||
|
||||
ensure
|
||||
server.stop(true)
|
||||
end
|
||||
|
||||
|
@ -211,4 +221,4 @@ class TestPumaServerSSLClient < Minitest::Test
|
|||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
end
|
||||
end
|
||||
end
|
||||
end unless DISABLE_SSL
|
||||
|
|
|
@ -18,12 +18,10 @@ class TestPumaControlCli < Minitest::Test
|
|||
end
|
||||
|
||||
def find_open_port
|
||||
begin
|
||||
server = TCPServer.new("127.0.0.1", 0)
|
||||
server.addr[1]
|
||||
ensure
|
||||
server.close
|
||||
end
|
||||
server = TCPServer.new("127.0.0.1", 0)
|
||||
server.addr[1]
|
||||
ensure
|
||||
server.close
|
||||
end
|
||||
|
||||
def test_config_file
|
||||
|
@ -32,8 +30,6 @@ class TestPumaControlCli < Minitest::Test
|
|||
end
|
||||
|
||||
def test_control_url
|
||||
skip if Puma.jruby? || Puma.windows?
|
||||
|
||||
host = "127.0.0.1"
|
||||
port = find_open_port
|
||||
url = "tcp://#{host}:#{port}/"
|
||||
|
|
|
@ -2,7 +2,7 @@ require_relative "helper"
|
|||
|
||||
require "rack/handler/puma"
|
||||
|
||||
class TestPumaUnixSocket < Minitest::Test
|
||||
class TestHandlerGetStrSym < Minitest::Test
|
||||
def test_handler
|
||||
handler = Rack::Handler.get(:puma)
|
||||
assert_equal Rack::Handler::Puma, handler
|
||||
|
@ -46,11 +46,11 @@ class TestPathHandler < Minitest::Test
|
|||
thread.join if thread
|
||||
end
|
||||
|
||||
|
||||
def test_handler_boots
|
||||
skip_on_appveyor
|
||||
in_handler(app) do |launcher|
|
||||
hit(["http://0.0.0.0:#{ launcher.connected_port }/test"])
|
||||
host = windows? ? "127.0.1.1" : "0.0.0.0"
|
||||
opts = { Host: host }
|
||||
in_handler(app, opts) do |launcher|
|
||||
hit(["http://#{host}:#{ launcher.connected_port }/test"])
|
||||
assert_equal("/test", @input["PATH_INFO"])
|
||||
end
|
||||
end
|
||||
|
@ -123,6 +123,44 @@ class TestUserSuppliedOptionsIsEmpty < Minitest::Test
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_host_when_using_config_file
|
||||
user_port = 5001
|
||||
file_port = 6001
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}" }
|
||||
|
||||
@options[:Host] = "localhost"
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://localhost:#{file_port}"], conf.options[:binds]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_host_when_using_config_file_with_explicit_host
|
||||
user_port = 5001
|
||||
file_port = 6001
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}, '1.2.3.4'" }
|
||||
|
||||
@options[:Host] = "localhost"
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://1.2.3.4:#{file_port}"], conf.options[:binds]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestUserSuppliedOptionsIsNotPresent < Minitest::Test
|
||||
|
|
|
@ -1,35 +1,33 @@
|
|||
require_relative "helper"
|
||||
|
||||
# UNIX sockets are not recommended on JRuby
|
||||
# (or Windows)
|
||||
unless Puma.jruby? || Puma.windows?
|
||||
class TestPumaUnixSocket < Minitest::Test
|
||||
class TestPumaUnixSocket < Minitest::Test
|
||||
|
||||
App = lambda { |env| [200, {}, ["Works"]] }
|
||||
App = lambda { |env| [200, {}, ["Works"]] }
|
||||
|
||||
Path = "test/puma.sock"
|
||||
Path = "test/puma.sock"
|
||||
|
||||
def setup
|
||||
@server = Puma::Server.new App
|
||||
@server.add_unix_listener Path
|
||||
@server.run
|
||||
end
|
||||
def setup
|
||||
# UNIX sockets are not recommended on JRuby or Windows
|
||||
skip_on :jruby, :windows, suffix: " - UNIX sockets are not recommended"
|
||||
@server = Puma::Server.new App
|
||||
@server.add_unix_listener Path
|
||||
@server.run
|
||||
end
|
||||
|
||||
def teardown
|
||||
@server.stop(true)
|
||||
File.unlink Path if File.exist? Path
|
||||
end
|
||||
def teardown
|
||||
@server.stop(true) if @server
|
||||
File.unlink Path if File.exist? Path
|
||||
end
|
||||
|
||||
def test_server
|
||||
sock = UNIXSocket.new Path
|
||||
def test_server
|
||||
sock = UNIXSocket.new Path
|
||||
|
||||
sock << "GET / HTTP/1.0\r\nHost: blah.com\r\n\r\n"
|
||||
sock << "GET / HTTP/1.0\r\nHost: blah.com\r\n\r\n"
|
||||
|
||||
expected = "HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nWorks"
|
||||
expected = "HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nWorks"
|
||||
|
||||
assert_equal expected, sock.read(expected.size)
|
||||
assert_equal expected, sock.read(expected.size)
|
||||
|
||||
sock.close
|
||||
end
|
||||
sock.close
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,11 +47,11 @@ do_start_one() {
|
|||
PIDFILE=$1/tmp/puma/pid
|
||||
if [ -e $PIDFILE ]; then
|
||||
PID=`cat $PIDFILE`
|
||||
# If the puma isn't running, run it, otherwise restart it.
|
||||
# If the puma is running, restart it, otherwise run it.
|
||||
if ps -p $PID > /dev/null; then
|
||||
do_start_one_do $1
|
||||
else
|
||||
do_restart_one $1
|
||||
else
|
||||
do_start_one_do $1
|
||||
fi
|
||||
else
|
||||
do_start_one_do $1
|
||||
|
@ -106,8 +106,6 @@ do_stop_one() {
|
|||
if [ -e $PIDFILE ]; then
|
||||
PID=`cat $PIDFILE`
|
||||
if ps -p $PID > /dev/null; then
|
||||
log_daemon_msg "---> Puma $1 isn't running."
|
||||
else
|
||||
log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
|
||||
if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
|
||||
cd $1 && bundle exec pumactl --state $STATEFILE stop
|
||||
|
@ -116,6 +114,8 @@ do_stop_one() {
|
|||
fi
|
||||
# Many daemons don't delete their pidfiles when they exit.
|
||||
rm -f $PIDFILE $STATEFILE
|
||||
else
|
||||
log_daemon_msg "---> Puma $1 isn't running."
|
||||
fi
|
||||
else
|
||||
log_daemon_msg "---> No puma here..."
|
||||
|
|
11
win_gem_test/Rakefile_wintest
Normal file
11
win_gem_test/Rakefile_wintest
Normal file
|
@ -0,0 +1,11 @@
|
|||
# rake -f Rakefile_wintest -N -R norakelib
|
||||
|
||||
require "rake/testtask"
|
||||
|
||||
Rake::TestTask.new(:win_test) do |t|
|
||||
t.libs << "test"
|
||||
t.warning = false
|
||||
t.options = '--verbose'
|
||||
end
|
||||
|
||||
task :default => [:win_test]
|
21
win_gem_test/package_gem.rb
Normal file
21
win_gem_test/package_gem.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/package'
|
||||
|
||||
spec = Gem::Specification.load("./puma.gemspec")
|
||||
|
||||
spec.files.concat ['Rakefile_wintest', 'lib/puma/puma_http11.rb']
|
||||
spec.files.concat Dir['lib/**/*.so']
|
||||
spec.test_files = Dir['{examples,test}/**/*.*']
|
||||
|
||||
# below lines are required and not gem specific
|
||||
spec.platform = ARGV[0]
|
||||
spec.required_ruby_version = [">= #{ARGV[1]}", "< #{ARGV[2]}"]
|
||||
spec.extensions = []
|
||||
if spec.respond_to?(:metadata=)
|
||||
spec.metadata.delete("msys2_mingw_dependencies")
|
||||
spec.metadata['commit'] = ENV['commit_info']
|
||||
end
|
||||
|
||||
Gem::Package.build(spec)
|
58
win_gem_test/puma.ps1
Normal file
58
win_gem_test/puma.ps1
Normal file
|
@ -0,0 +1,58 @@
|
|||
# PowerShell script for building & testing SQLite3-Ruby fat binary gem
|
||||
# Code by MSP-Greg, see https://github.com/MSP-Greg/av-gem-build-test
|
||||
|
||||
# load utility functions, pass 64 or 32
|
||||
. $PSScriptRoot\shared\appveyor_setup.ps1 $args[0]
|
||||
if ($LastExitCode) { exit }
|
||||
|
||||
# above is required code
|
||||
#———————————————————————————————————————————————————————————————— above for all repos
|
||||
|
||||
Make-Const gem_name 'puma'
|
||||
Make-Const repo_name 'puma'
|
||||
Make-Const url_repo 'https://github.com/puma/puma.git'
|
||||
|
||||
#———————————————————————————————————————————————————————————————— lowest ruby version
|
||||
Make-Const ruby_vers_low 22
|
||||
# null = don't compile; false = compile, ignore test (allow failure);
|
||||
# true = compile & test
|
||||
Make-Const trunk $false ; Make-Const trunk_x64 $false
|
||||
Make-Const trunk_JIT $null ; Make-Const trunk_x64_JIT $null
|
||||
|
||||
#———————————————————————————————————————————————————————————————— make info
|
||||
Make-Const dest_so 'lib\puma'
|
||||
Make-Const exts @(
|
||||
@{ 'conf' = 'ext/puma_http11/extconf.rb' ; 'so' = 'puma_http11' }
|
||||
)
|
||||
Make-Const write_so_require $true
|
||||
|
||||
#———————————————————————————————————————————————————————————————— Pre-Compile
|
||||
# runs before compiling starts on every ruby version
|
||||
function Pre-Compile {
|
||||
# load the correct OpenSSL version in the build system
|
||||
Check-OpenSSL
|
||||
Write-Host Compiling With $env:SSL_VERS
|
||||
}
|
||||
|
||||
#———————————————————————————————————————————————————————————————— Run-Tests
|
||||
function Run-Tests {
|
||||
# call with comma separated list of gems to install or update
|
||||
Update-Gems minitest, minitest-retry, rack, rake
|
||||
$env:CI = 1
|
||||
rake -f Rakefile_wintest -N -R norakelib | Set-Content -Path $log_name -PassThru -Encoding UTF8
|
||||
# add info after test results
|
||||
$(ruby -ropenssl -e "STDOUT.puts $/ + OpenSSL::OPENSSL_LIBRARY_VERSION") |
|
||||
Add-Content -Path $log_name -PassThru -Encoding UTF8
|
||||
minitest # collects test results
|
||||
}
|
||||
|
||||
#———————————————————————————————————————————————————————————————— below for all repos
|
||||
# below is required code
|
||||
Make-Const dir_gem $(Convert-Path $PSScriptRoot\..)
|
||||
Make-Const dir_ps $PSScriptRoot
|
||||
|
||||
Push-Location $PSScriptRoot
|
||||
.\shared\make.ps1
|
||||
.\shared\test.ps1
|
||||
Pop-Location
|
||||
exit $ttl_errors_fails + $exit_code
|
Loading…
Add table
Add a link
Reference in a new issue